#include "graphics.hpp"
#include <iostream>
#include <stdlib.h>
#include <math.h>
#include <vector>
#include <time.h>

using namespace genv;
using namespace std;

const unsigned int X = 500;
const unsigned int Y = 500;

const float vFarkas = 4;
const float vBarany = 3;

// ----------------- Vektor struct -----------------------
struct vektor
{
    float x, y;
    void random()
    {
        x = floor(rand() % X);
        y = floor(rand() % Y);
    }
    float hosszNegyzet()
    {
        return x*x + y*y;
    }
    float hossz()
    {
        return sqrt(x*x + y*y);
    }
    vektor& operator+= (const vektor& v)
    {
        this->x = this->x + v.x;
        this->y = this->y + v.y;
        return *this;
    }
};

vektor operator+ (const vektor& v1, const vektor& v2)
{
    vektor temp;
    temp.x = v1.x + v2.x;
    temp.y = v1.y + v2.y;
    return temp;
}

vektor operator- (const vektor& v1, const vektor& v2)
{
    vektor temp;
    temp.x = v1.x - v2.x;
    temp.y = v1.y - v2.y;
    return temp;
}

vektor operator* (const float skalar, const vektor& v)
{
    vektor temp;
    temp.x = skalar * v.x ;
    temp.y = skalar * v.y;
    return temp;
}

vektor operator* (const vektor& v, const float skalar)
{
    vektor temp;
    temp.x = skalar * v.x ;
    temp.y = skalar * v.y;
    return temp;
}

vektor operator/ (const vektor& v, const float skalar)
{
    vektor temp;
    temp.x = v.x / skalar;
    temp.y = v.y / skalar;
    return temp;
}

vektor operator/= (vektor& v, const float skalar)
{
    v.x = v.x / skalar;
    v.y = v.y / skalar;
    return v;
}

// --------------------- Állatok -----------------------
//******************************************************

// Bárány ------------------------
struct barany
{
public:
    vektor hely;
    void rajzol()
    {
        gout << move_to(hely.x, hely.y) << color (255,255,255) << box(5,5);
    }
    barany()
    {
        hely.random();
        rajzol();
    }
    vektor tavolsag(vektor honnan)
    {
        return hely - honnan;
    }
    void menekul();
    ~barany()
    {
        cout << "  meghalt: " << this << endl;
    }
};

// Farkas -------------------------
struct farkas
{
private:
    vektor hely;
    void rajzol()
    {
        gout << move_to(hely.x, hely.y) << color (0,0,0) << box(5,5);
    }
    void kivalaszt();
public:
    barany *melyiket = 0;
    farkas()
    {
        hely.random();
        rajzol();
    }
    vektor tavolsag(vektor honnan)
    {
        return honnan - hely;
    }
    void uldoz ();
};

// Globális változók ---------------------
vector <barany*> baranyok;
vector <farkas*> farkasok;

// Tagfüggvények függvényteste -----------
// Farkas kiválasztja áldozatát
void farkas::kivalaszt ()
{
    for (unsigned int j = 0; j < 10; j++)
    {
        // Legközelebbi bárány kiszemelése
        float minTav = 9999999; // "Végtelen" távolság
        for (int unsigned i = 0; i < baranyok.size(); i++)
        {
            if (baranyok[i])
            {
                vektor tavV = baranyok[i]->tavolsag(hely);
                float tav = tavV.hosszNegyzet();
                if (tav < minTav)
                {
                    melyiket = baranyok[i];
                    minTav = tav;
                }
            }
        }
        // Megszámolom, hány másik farkas üldözi ezt a bárányt
        int hanyan = 0;
        for (unsigned int k = 0; k < farkasok.size(); k++)
        {
            if (farkasok[k]->melyiket == melyiket) hanyan++;
            if (hanyan > 3) melyiket = 0; // Ha több, mint ketten üldözik, akkor megpróbálok másikat választani
        }
        if (melyiket) break; // Ha üldözhetem, akkor boldog vagyok, és kilépek a ciklusból
    }
}

// Farkas üldözi a kiválasztott bárányt
void farkas::uldoz ()
{
    static vektor emozg;  // Farkas lendülete (elõzõ mozgásvektora

    if (!melyiket) kivalaszt();

    if (!melyiket) // Ha nincs kit üldözni, mozogj random!
    {
        // Véletlenszerű mozgást végeznek
        vektor temp;
        temp.x = rand() % 5 - 2;
        temp.y = rand() % 5 - 2;
        hely += temp + emozg;
        emozg = temp;

        // Ha a szélén vannak, akkor kezdjenek el az ellenkező irányba mozogni
        if (hely.x < 100) emozg.x = 5;
        else if (hely.x > X - 100) emozg.x = -5;
        if (hely.y < 100) emozg.y = 5;
        else if (hely.y > X - 100) emozg.y = -5;

        // Ha mégis elérnék a szélét, menjenek át a túloldalra, és ne legyen lendületük
        if (hely.x < 5) {hely.x = X - 5; emozg.x = 0;}
        else if (hely.x > X - 5) {hely.x = 5; emozg.x = 0;}
        if (hely.y < 5) {hely.y = Y - 5; emozg.y = 0;}
        else if (hely.y > Y - 5) {hely.y = 5; emozg.y = 0;}

        if (emozg.hossz() > 10) emozg /= emozg.hossz();

        rajzol();
    }

    else // Ha van kit üldözni
    {
        // Legközelebbi bárány fele mozog
        vektor mozg = melyiket->tavolsag(hely);
        hely += mozg / mozg.hossz() * vFarkas + emozg;
        emozg = mozg / mozg.hossz();  // a farkasok lomhábbak, mert lendületvektoruk kétszeresét adja hozzá

        // vonalat rajzol a bárányhoz
        gout << color(150,200,150) << move_to(hely.x, hely.y) << line_to(melyiket->hely.x,melyiket->hely.y);

        // Visszahozza, ha túlmegy a szélén
        if (hely.x < 0) hely.x = 0;
        else if (hely.x > X) hely.x = X-5;
        if (hely.y < 0) hely.y = 0;
        else if (hely.y > Y) hely.y = Y-5;

        // Ha beérte, akkor megeszi
        if (mozg.hossz() < 5)
        {
            delete melyiket;
            barany *temp = melyiket;
            for (unsigned int i=0; i < farkasok.size(); i++)
            {
                if (farkasok[i]->melyiket == temp) farkasok[i]->melyiket = 0;
            }
            for (unsigned int i=0; i < baranyok.size(); i++)
            {
                if (baranyok[i] == temp)
                {
                    baranyok[i] = baranyok[baranyok.size()-1];
                    baranyok.pop_back();
                }
            }
        }

        rajzol();

    }

}

// Barány menekül ---------
void barany::menekul()
{
    static vektor emozg;
    // Legközelebbi farkas keresése
    float minTav = 9999999; // "Végtelen" távolság
    farkas *mitol;
    for (int unsigned i = 0; i < farkasok.size(); i++)
    {
        vektor tavV = farkasok[i]->tavolsag(hely);
        float tav = tavV.hosszNegyzet();
        if (tav < minTav)
        {
            mitol = farkasok[i];
            minTav = tav;
        }
    }
    // Legközelebbi farkastól elfele mozog
    vektor mozg = mitol->tavolsag(hely);
    hely += mozg  / mozg.hossz() * vBarany + emozg;
    emozg = mozg / mozg.hossz(); // a baranyok mozgékonyabbak, mert lendületvektor felét adja hozzá

    // Ha túlmegy, átugrik a rajzvászon túloldalára
    // Azért, hogy ne szoruljanak be a bárányok a sarokba
    if (hely.x < 10) hely.x = X - 50;
    else if (hely.x > X) hely.x = 10;
    if (hely.y < 10) hely.y = Y - 50;
    else if (hely.y > Y) hely.y = 10;

    rajzol();
}


// ---------------------- Main --------------------------
// ******************************************************
int main()
{
    // Inicializáció
    srand (time(NULL));

    gout.open(X,Y);

    // Nyáj és horda feltöltése állatokkal
    for (int i = 1; i < 100; i++)
        baranyok.push_back(new barany);
    for (int i = 1; i < 11; i++)
        farkasok.push_back(new farkas);

    gout << refresh;

    // Akció --------------
    event ev;
    gin.timer(40);

    bool van_meg = true;
    while(gin >> ev && ev.keycode != key_escape && van_meg)
    {
        if (ev.type == ev_timer)
        {
            // Képernyő törlése
            gout << color(0,180,10) << move_to(0,0) << box(X,Y);

            for (unsigned int i = 0; i < farkasok.size(); i++)
                farkasok[i]->uldoz();

            van_meg = false;
            for (unsigned int i = 0; i < baranyok.size(); i++)
            {   if (baranyok[i])
                {
                    baranyok[i]->menekul();
                    van_meg = true;
                }
            }

            gout << refresh;
        }
    }

    // Vége ----------------
    if (van_meg)
        cout << "\n*****************************************\n\n\tMeguntad?\n";
    else
        cout << "\n******************************************\n\n\tElfogytak a baranyok!\n";
    return 0;
}
