//CritterModel.h

#ifndef __CritterModelObserver__CritterModel__
#define __CritterModelObserver__CritterModel__

#include <stdio.h>
#include <iostream> 
#include <string>
#include <vector>

//IObserver.h

#ifndef CritterModelObserver_IObserver_h
#define CritterModelObserver_IObserver_h

class IObserver {
public:
    virtual void update() = 0;
};

#endif

//Observable.h

#ifndef __CritterModelObserver__Observable__
#define __CritterModelObserver__Observable__

#include <stdio.h>

class Observable {
public:
    Observable();
    virtual ~Observable();
    void addObserver(IObserver* ob);
    void removeObserver(IObserver* ob);
    
protected:
    void notify() const;
    
private:
    IObserver** observers;
    static const int maxObservers;
    int numObservers;
};

#endif /* defined(__CritterModelObserver__Observable__) */

class CritterModel : public Observable {
    
public:
    CritterModel();
    virtual ~CritterModel();
    CritterModel(int x, int y);
    
    // Getters
    inline int getReward() const { return reward; }
    inline int getHitPoint() const { return hitPoint; }
    inline int getStrength() const { return strength; }
    inline int getSpeeed() const { return speed; }
    inline int getLevel() const { return level; }
    inline int getXCoord() const { return x; }
    inline int getYCoord() const { return y; }
    inline int getDuration() const { return duration; }
    // Setters
    void setLevel(int l) {
        level = l;
    }
    
    void setStrength(int s) {
        strength = s;
    }
    
    void setHitPoint(int h) {
        hitPoint = h;
    }
    /**
    void setCellType(int c) {
        cellType = c;
    }
    */
    void setSpeed(int s) {
        speed = s;
    }
    
    void setReward(int r) {
        reward = r;
    }
    
    void setXCoord(int xC) {
        x = xC;
    }
    
    void setYCoord(int yC) {
        y = yC;
    }
    
    void setDuration(int d) {
        duration = d;
    }
    
    // Other methods
    int generateCritters(int);
    std::string getCell();
    void printCritters();
    void moveCritter(int, int); // Undefined at the moment.
    void shoot();
    void timer();

private:
    int reward;
    int hitPoint;
    int strength;
    int speed;
    int level;
    int duration;
    //int cellType;
    int x,y;
    //Cell cell;
    
    
};

#endif /* defined(__CritterModelObserver__CritterModel__) */


//CritterModel.cpp
#include <iostream> 
#include <time.h>
#include <vector>

using namespace std;

CritterModel::CritterModel() : Observable() {
    reward = 0;
    hitPoint = 0;
    strength = 0;
    speed = 0;
    level = 0;
    y = 0;
    x = 0;
}

CritterModel::~CritterModel() {
    
}




// Generate Critters
CritterModel* generateCritters(int numCritters) {
    CritterModel* critters = new CritterModel[numCritters];
    
    for (int i=0; i < numCritters; i++) {
        critters[i] = CritterModel();
    }
    return critters;
}
 

// Move those critters
void CritterModel::moveCritter(int xCoord, int yCoord) {
    bool flag = false;
    if (this->x != xCoord) {
        x = xCoord;
        flag = true;
    }
    
    if (this->y != yCoord) {
        y = yCoord;
        flag = true;
    }
    
    if (flag) {
        notify();
    }
}


//Observable.cpp
#include <iostream>

const int Observable::maxObservers = 5;

Observable::Observable() {
    observers = new IObserver* [maxObservers];
    for (int i=0; i < maxObservers; i++) {
        observers[i] = NULL;
        numObservers = 0;
    }
}
    // Destructor
    Observable::~Observable() {
        delete[] observers;
    }

    // Add observer method
    void Observable::addObserver(IObserver* ob) {
        // Check if full
        if (numObservers == maxObservers) {
            return;
        }
         // Check if observer exists
        for (int i=0; i < maxObservers; i++) {
            if (observers[i] == ob) {
                return;
            }
        }
        
        // Otherwise add the observer
        observers[numObservers] = ob;
        ++numObservers;
    }

void Observable::removeObserver(IObserver* ob) {
    // ommitted
}

void Observable::notify() const {
    for (int i=0; i < numObservers; i++) {
        observers[i]->update();
    }
}

//CritterDisplay.h

#ifndef __CritterModelObserver__CritterDisplay__
#define __CritterModelObserver__CritterDisplay__

#include <stdio.h>

class CritterDisplay: public IObserver {
    
public:
    CritterDisplay(CritterModel* critterModel);
    virtual ~CritterDisplay();
    void update();
    
private:
    CritterModel* critterModel;
};

#endif /* defined(__CritterModelObserver__CritterDisplay__) */

//CritterDisplay.cpp
#include <iostream> 

using namespace std;

CritterDisplay::CritterDisplay(CritterModel* critterModel) {
    this->critterModel = critterModel;
}

CritterDisplay::~CritterDisplay() {}

void CritterDisplay::update() {
    
    cout <<
    "================" << endl <<
    "Position: " << "(" << critterModel->getXCoord() << ", " << critterModel->getYCoord() << ")" << endl <<
    "Speed: " << critterModel->getSpeeed() << endl <<
    "Strength: " << critterModel->getStrength() << endl <<
    "Level: " << critterModel->getLevel() << endl <<
    "Hit Points: " << critterModel->getHitPoint() << endl <<
    "Reward: " << critterModel->getReward() << endl << "\n";
    
    
} 


//Driver.cpp


#include <iostream>
#include <time.h>
#include <unistd.h>
#include <limits>

using namespace std;

void PressEnterToBegin()
{
    std::cout << "Press ENTER to begin..."; //<< &fflush;
    std::cin.ignore( std::numeric_limits <std::streamsize> ::max(), '\n' );
}

int main(int argc, const char * argv[]) {
    
    double dif = 0.0;
    bool flag = true;
    int x,y = 0;
    
    // Initialize players coin
    int pCoin = 100;
    
    // Initialize coin steal
    int stealCoin = 0;
    
    // Initialize reward
    int reward = 0;
    
    //CritterModel* critters;
    CritterModel* critterModel = new CritterModel();
    
    //CritterModel* critters = CritterModel::generateCritters(5);
    //critters->generateCritters(5);
    
    CritterDisplay* display = new CritterDisplay(critterModel);
    
    critterModel->addObserver(display);
    critterModel->setSpeed(2);
    critterModel->setStrength(10);
    critterModel->setLevel(1);
    critterModel->setHitPoint(100);
    critterModel->setReward(300);
    
    // Welcome message
    std::cout << "Welcome to the Critter game!\n" << "You have $100 in your bank to begin with. If a Critter happens to get to the exit, \nit steals coin from your bank proportional to the Critters strength.\n\n" << "Game play: \n1.) Game starts by a Critter wave being ready to enter the map.\n2.) Press ENTER to begin.\n3.) After the Critters enter, you will be notified that a timer will start.\n4.) Fire by pressing ENTER before time runs out and Critters exit the map.\n " << "---------------------------------------------------------------------------\n\n";
    
    PressEnterToBegin();
    
    // Start timed loop
    time_t start, end;
    time(&start);
    do  {
        
        // End simulation after dif > 2
        if (dif > 2) {
            flag = false;
        }
        
        // Otherwise move critter to next cell
        critterModel->moveCritter(x, y);
        ++x;
        ++y;
        
        time(&end);
        dif = difftime(end, start);
        cout << dif << endl;
        
        // Counter to simulate speed of critter. A critter moves every 2 seconds and the sleep method stops time which keeps dif < 2 in the above if-statment
        if (x > 1 || y > 1) {
            sleep(2);
        }
        
        // If critter coordinates are at the exit, steal coin from player
        if (critterModel->getXCoord() == 4 && critterModel->getYCoord() == 4) {
            stealCoin = stealCoin + critterModel->getStrength();
            pCoin = pCoin - stealCoin;
            std::cout << "A critter got through! You have $" << pCoin << " left in your bank.\n\n";
        }
        
    }while (flag);
    
    
    
    delete display;
    delete critterModel;
    
    return 0;
    
    
}