/*----
  signature: main: void -> int
  purpose: playing with arrays and objects and pointers,
           oh my!

  compile using:
    g++ 112lect07-2.cpp PlayerChar.cpp -o 112lect07-2
  run using:
    ./112lect07-2

  by: Sharon Tuttle
  last modified: 2022-10-06
----*/

#include <cstdlib>
#include <iostream>
#include <string>
#include <cmath>
#include "PlayerChar.h"    
using namespace std;

int main()
{
    cout << boolalpha;

    // static array of PlayerChar objects

    PlayerChar squad_static[5];

    cout << endl << "squad_static contents: " << endl;

    for (int i=0; i < 5; i++)
    {
        cout << squad_static[i].player_to_string() << endl;
    }

    // now consider:
    // here is a dynamically-allocated array
    //    of PlayerChar objects

    PlayerChar *squad_dynamic;

    squad_dynamic = new PlayerChar[5];

    cout << endl << "squad_dynamic contents: " << endl;

    for (int i=0; i < 5; i++)
    {
        cout << squad_dynamic[i].player_to_string() << endl;
    }

    delete [] squad_dynamic;

    // this is a static array of 5 elements,
    //    each element is of type pointer-to-PlayerChar

    PlayerChar *squad_ptrs_stat[5];

    // it is BAD IDEA to follow pointers-to-objects
    //   that don't point anywhere,
    // and ALSO a BAD IDEA to assume what is in
    //   a pointer variable that has declared but not
    //   initialized

    // what do I get if I try to follow the uninitialized
    //     pointers in this array? ...a run-time error;

    // what if I initialize the pointers first?

    for (int i=0; i < 5; i++)
    {
        squad_ptrs_stat[i] =
            new PlayerChar("Moo" + to_string(i),
                           3, 4.4, "finder", 5);
    }
    
    cout << endl << "squad_ptrs_stat contents: " << endl;

    for (int i=0; i < 5; i++)
    {
        cout << squad_ptrs_stat[i]->player_to_string() << endl;
    }

    // careful! need to FREE the memory EACH of these
    //    pointers points to when done!

    for (int i=0; i < 5; i++)
    {
        delete squad_ptrs_stat[i];
        squad_ptrs_stat[i] = NULL;
    }

    // oh, you wanted a DYNAMIC array of pointers
    //    to PlayerChar objects?
    // squad_ptrs_dyn will be of type
    //    pointer-to-pointer-to-PlayerChar

    PlayerChar **squad_ptrs_dyn;
 
    // making squad_ptrs_dyn point to
    //    a dynamically-allocated
    //    array of 5 pointer-to-Players instances
    
    squad_ptrs_dyn = new PlayerChar*[5];

    // need these pointers to POINT to something!

    for (int i=0; i < 5; i++)
    {
        squad_ptrs_dyn[i] =
            new PlayerChar("Baa" + to_string(i),
                           6, 7.7, "muncher", 8);
    }

    cout << endl
         << "following squad_ptrs_dyn's pointers:" << endl;

    for (int i=0; i < 5; i++)
    {
        cout << squad_ptrs_dyn[i]->player_to_string()
             << endl;
    }

    // OYYYY! need to free those dynamically-allocated
    //    PlayerChar objects AND this dynamically-allocated
    //    array of pointers-to-PlayerChars...!

    for (int i=0; i < 5; i++)
    {
        delete squad_ptrs_dyn[i];
        squad_ptrs_dyn[i] = NULL;
    }

    // NOW delete the dynamic array of POINTERS to PlayerChars

    delete [] squad_ptrs_dyn;

    // some discussion set-up for additional methods
    //    one should explicitly implement in classes
    //    whose data fields involve dynamic memory
    //    (or sometimes even just collections?)

    cout << endl
         << "demoing default overloaded assignment operator"
         << endl << "   for classes: " << endl;
    
    PlayerChar alphonse("Alphonse", 25, 10.6, "armour", 30);
    PlayerChar edward = alphonse;

    // If you assign alphonse to edward, what does that mean?
    // The default OVERLOADED ASSIGNMENT OPERATOR generated for
    //     PlayerChar copies the value in alphonse's data
    //     fields into edward's data fields.

    // here, you can see they are in different locations in
    //    memory:
    
    cout << "alphonse's address: "
         << &alphonse << endl;
    cout << "edward's address: "
         << &edward << endl;    

    // but they have equivalent data fields:

    cout << "alphonse's data fields: "
         << alphonse.player_to_string() << endl;
    cout << "edward's data fields: "
         << edward.player_to_string() << endl;

    // and if you change edward's name field, in ONLY
    //    changes edward's name, it does NOT also
    //    change alphonse's name

    edward.set_name("Edward");

    cout << endl << "after edward's name change: " << endl;

    cout << "alphonse's data fields: "
         << alphonse.player_to_string() << endl;
    cout << "edward's data fields: "
         << edward.player_to_string() << endl;

    cout << endl;
    return EXIT_SUCCESS;
}