#include <iostream>
#include <unistd.h> // needed for usleep
#include <string>
#include <fstream>

#include "board.hh"

// Basisklassen
struct Regel
{
    virtual void anwenden(Board& board) = 0;
};

class TorusCondition : public BoundaryCondition
{
    bool boundary(Board& board, int i, int j) {
        if (i == board.height() - 1) {
            return board.isLiving(0, j);
        } else if (i == -1) {
            return board.isLiving(board.height() - 1, j);
        } else if (j == board.width() - 1) {
            return board.isLiving(i, 0);
        } else if (j == -1) {
            return board.isLiving(i, board.width() - 1);
        }
    }
};

class AliveCondition : public BoundaryCondition
{
    bool boundary(Board& board, int i, int j) {
        return true;
    }
};

class DeadCondition : public BoundaryCondition
{
    bool boundary(Board& board, int i, int j) {
        return false;
    }
};

class GameOfLifeRules : public Regel
{
    public :
        //void anwenden(Board& board) {
        //    // copy new board from old board
        //    Board updated = Board(board.width(), board.height());
        //    for (int i = 0; i < board.height(); i++) {
        //        for (int j = 0; i < board.width(); i++) {
        //            int n = board.livingCells(i, j);
        //            int self = board.isLiving(i, j);
        //            if (self && n < 2) {
        //                updated.updateField(i, j, false);
        //            } else if (!self && n == 3) {
        //                updated.updateField(i, j, true);
        //            } else if (self && n > 3) {
        //                updated.updateField(i, j, false);
        //            } else {
        //                updated.updateField(i, j, self);
        //            }
        //        }
        //    }
        //    board = updated;
        //}
};

// Ein zellulaerer Automat, der Regeln und Datenstrukturen von aussen bekommt
class Automat
{
    public :
        // der Datentyp fuer das 2D Bool Array
        Automat(Board& board, Regel& regel)
            : _board(board)
            , _regel(regel)
        {}

        // mache n Schritte
        void doSteps(int n=1)
        {
            for (int i=0; i<n; ++i)
            {
                // Linux-spezifische Art und Weise den Inhalt der Konsole zu loeschen
                // und den Cursor nach oben links zu setzen.
                //std::cout << "\x1B[2J\x1B[H" << "Step " << i << std::endl << _board;
                // Das Wiedergeben der Loesung soll immer 10 Sekunden (=1e7 Mikrosekunden)
                // dauern. Sie koennen diesen Wert auch aendern.
                usleep(1.e7/n);
                //_regel.anwenden(_board);
            }
        }

    private :
        Board& _board;
        Regel& _regel;
};

int main(int argc, char** argv)
{
    if (argc != 2)
    {
        std::cout << "Usage: ./<progname> <txt-file>" << std::endl;
        return 1;
    }

    Board board = Board(2, 2, AliveCondition);
    board.updateField(1,1, true);
    board.livingCells(1,1);

    // Initialisiere Datenstruktur des 2D Bool Array
    // TODO
    // Ueberlegen Sie sich wie Sie einen Startzustand in das 2D Bool Array bekommen
    // Lesen Sie ggf.
    // (1) eine solche Textdatei mittels Filestream ein
    // (2) ueberladen Sie Operatoren operator<< oder operator>>

    // TODO
    // - legen Sie eine Instanz der Randbedingung an
    // - legen Sie eine Instanz des Regelsystems an
    // - Initialisieren Sie den zellulaeren Automaten

    // Experimentieren Sie hier mit Ihrem Automaten

    return 0;
}
