#include <iostream>
#include <fstream>
#include <vector>

using namespace std;

class Person {
    string m_name;
    int m_age;
public:
    Person() {
        m_name = "";
        m_age = -1;
    }
    Person( string name, int age ) {
        m_name = name;
        m_age = age;
    }

    Person( const Person& p ) {
        m_name = p.m_name;
        m_age = p.m_age;
    }

    string name() const {
        return m_name;
    }

    int age() const {
        return m_age;
    }

    void name( string name ) {
        m_name = name;
    }

    void age( int age ) {
        m_age = age;
    }
};


// ========================================================================
// Here I overload the global file stream insertion and extration operators
// ========================================================================
ifstream& operator >>( ifstream& in, Person& person ) {
    string name;
    int age;

    in >> name;
    in >> age;

    person.name( name );
    person.age( age );

    return in;
}

ofstream& operator <<( ofstream& out, const Person& person ) {
    out << person.name() << endl; // If you don't use an end line here it won't seperate the data
    out << person.age();

    return out;
}
// ========================================================================


// ========================================================================
// Here I overload the outstream insertion operator, this alows me to just
// pass a Person object to cout << and have it print with out a problem,
// this is GREAT for fast debuging
ostream& operator <<( ostream& os, const Person& person ) {
    os << person.name() << " is " << person.age() << " years old.";
    return os;
}
// ========================================================================

class Family {
    std::vector<Person> m_family;
public:
    Family() {
    }

    void addPerson( const Person& person ) {
        m_family.push_back( person );
    }

    int numFamilyMembers() const {
        return m_family.size();
    }

    Person& getPerson( int i ) {
        return m_family.at( i );
    }

    const Person& getPerson( int i ) const {
        return m_family.at( i );
    }
};

// For outpint
ostream& operator <<( ostream& os, const Family& family ) {
    int n = family.numFamilyMembers();

    os << "Family has " << n << " family members: " << endl;

    for( int i=0; i<n; ++i ) {
        os << family.getPerson( i ) << endl; // This puts the individual people into the stream
    }
    os << endl;

    return os;
}

// For file output
ofstream& operator <<( ofstream& out, const Family& family ) {
    int n = family.numFamilyMembers();

    out << n; // This will tell who ever extracts this from the steam how many people to expect
    for( int i=0; i<n; ++i ) {
        out << family.getPerson( i ); // This puts the individual people into the stream
    }

    return out;
}

ifstream& operator >>( ifstream& in, Family& family ) {
    int numPeople;
    in >> numPeople; // Get the number of serialized people

    for( int i=0; i<numPeople; ++i ) {
        Person p;
        in >> p; // Get an individual person
        family.addPerson( p ); // Add the newly extracted person to the family
    }

    return in;
}

int main() {
    // Create our family
    Family f;
    f.addPerson( Person( "Mom", 37 ) );
    f.addPerson( Person( "Dad", 40 ) );
    f.addPerson( Person( "kid", 13 ) );
    cout << f << endl;

    // Open our file stream for writing
    ofstream fout( "family.dat" );
    // Serialize our family to the stream
    fout << f;
    // Flush the stream to the file
    fout << flush;
    // Close the file
    fout.close();

    // Open our file stream for reading
    ifstream fin( "family.dat" );
    // Create our family to store the deserialized data into
    Family f2;
    // Get our family from the file already!
    fin >> f2;
    // Now simpel print our family using the magic of our overloaded opperators
    cout << "Family deserialized from file:" << endl << f2 << endl;

    return 0;
}
