#include "../include/map.h"


/*
 Current description of this class:
 This is the basic grid that contains all the cells an allows to acess them.
 In a run, only one instance of this object shall be created.
 We might only create the cells we need or create them all anyway (the latter is simplier
 and withoud any major backdraw).
 As the cells are placed in a vector, it should be possible to determine things like
 custom distances shape of the maps adn so on.

 For simplicity concern, size is determined by the number of cells
 the map is a rectangle.

 Methods:
  - Iterate on the map.
    Multiple iterators?

  - Cet cell from the current iterator, this increments iteration?

  - Get cell from coordinates

  - Get a subset of cells from either a distance function or any "area" informations
    Return the cell with a distance?



*/



landscape::landscape(int lati, int longi)//, int Eref)
{
    dim_lati = lati;
    dim_longi = longi;
    //Eref = Eref;


    for(int i = 0; i < dim_lati; i++)
        for(int j = 0; j < dim_longi; j++)
            cellVect.push_back(cell(i, j, dim_longi*i+j));

    lati_iter = 0;
    longi_iter = 0;

}

landscape::~landscape()
{
    //dtor
}




cell * landscape::get_cell(int m_iter)
{
    //Convert boundaries
    int temp_longi = longi_from_iter(m_iter);
    int temp_lati = lati_from_iter(m_iter);

    return get_cell(temp_lati, temp_longi);
}



cell * landscape::get_cell(int m_lati, int m_longi)
{
    int ind;
    if (m_longi < 0 || m_longi >= max_longi())
        return NULL;
    else if(m_lati < 0 || m_lati >= max_lati())
        return NULL;
    else
    {
        ind = iter_from_coord(m_lati, m_longi);
        return &(cellVect[ind]);
    }
}


cell * landscape::current_cell()
{
    return get_cell(get_iter());
}




cell * landscape::next_cell()
{
    return get_cell(iter_next());
}





/*ITERATION REVAMP*/




unsigned int landscape::max_size()
/*
 * Gets the number of cells
 * In case stoge method is changed
 */
{
    return (unsigned int)(dim_longi * dim_lati);
}



int landscape::max_longi()
{
    return dim_longi;
}



int landscape::max_lati()
{
    return dim_lati;
}



int landscape::iter_from_coord(int m_lati, int m_longi)
{
    return m_lati * dim_longi + m_longi;
}



int landscape::longi_from_iter(int m_iter)
/*
 * Private, only used form iternal conversions (iter_move)
 */
{
    return m_iter % dim_longi;
}



int landscape::lati_from_iter(int m_iter)
/*
 * Private, only used form iternal conversions (iter_move)
 */
{
    return floor(m_iter/dim_longi);
}



int landscape::get_iter()
/*
 * returns current position of 1D iterator
 * Boudaries checking is performed here only for all 1D functions
 * -1 if outsid boundaries (end of iteration
 */
{
    //Conversion from lati/longi to 1D
    int temp_longi = get_longi();
    if(temp_longi == -1) return -1;

    int temp_lati = get_lati();
    if(temp_lati == -1) return -1;

    return temp_lati * dim_longi + temp_longi;
}



int landscape::iter_next()
/*
 * 1D interation "next", based on uderlying 2D sutructur
 * Returns new iterator value
 * Returns -1 if enreached
 */
{
    //Increments longitude, if end of line reached (-1), reset longi and ++ lati
    if(longi_next() == -1)
    {
        longi_set_begin();
        if(lati_next() == -1)
        {
            return -1;
        }
    }
    return get_iter();
}



int landscape::iter_move(int m_x)
/*
 * Move iterator relativly to the current position
 */
{
    int new_iter = get_iter() + m_x;
    iter_set(new_iter);
    return get_iter();
}



int landscape::iter_set(int m_x)
/*
 * Absolute move in 1D (underlying 2D)
 * Returns -1 if out of bounds
 */
{
    longi_set(longi_from_iter(m_x));
    lati_set(lati_from_iter(m_x));
    return get_iter();
}



int landscape::iter_set(int m_longi, int m_lati)
{
    longi_set(m_longi);
    lati_set(m_lati);
    return get_iter();
}



int landscape::iter_set_begin()
{
    return get_iter();
}



int landscape::longi_next()
{
    longi_iter++;
    return get_longi();
}



int landscape::longi_move(int m_x)
{
    longi_iter += m_x;
    return get_longi();
}



int landscape::longi_set(int m_x)
{
    longi_iter = m_x;
    return get_longi();
}



int landscape::longi_set_begin()
{
    longi_iter = 0;
    return get_longi();
}



int landscape::get_longi()
/*
 * Longi boudaries check in here only
 */
{
    if(longi_iter >= 0 && longi_iter < max_longi())
        return longi_iter;
    return -1;
}



int landscape::lati_next()
{
    lati_iter++;
    return get_lati();
}



int landscape::lati_move(int m_x)
{
    lati_iter += m_x;
    return get_lati();
}



int landscape::lati_set(int m_x)
{
    lati_iter = m_x;
    return get_lati();
}



int landscape::lati_set_begin()
{
    lati_iter = 0;
    return get_lati();
}



int landscape::get_lati()
/*
 * Lati boudaries check in here only
 */
{
    if(lati_iter >= 0 && lati_iter < max_lati())
        return lati_iter;
    return -1;
}


vector<bool> landscape::isPopEmptyInSpe(species * m_pSpe)
{
    return isPopEmpty[m_pSpe];
}

bool landscape::getIsEmpty(species * m_pSpe, int c)
{
    return isPopEmpty[m_pSpe][c];
}

vector<int> landscape::getPopulatedPops(species * m_pSpe)
{
    vector<int> populatedPops;
    for(unsigned int cellId = 0; cellId < max_size(); ++cellId)
    {
        if (!isPopEmpty[m_pSpe][cellId])
        {
            populatedPops.push_back(cellId);
        }
    }
    return populatedPops;
}

void landscape::setIsEmpty(species * m_pSpe, int c, bool isEmpty)
{
    isPopEmpty[m_pSpe][c] = isEmpty;
}

void landscape::addIsEmptyForSpe(species * m_pSpe)
{
    vector<bool> tmpPop(dim_longi * dim_lati, false);//need empty false at the beginning to have former behavior
    isPopEmpty[m_pSpe] = tmpPop;
}

void landscape::refreshIsEmptyForSpe(species * m_pSpe)
{
    cell * currCell = NULL;
    population * currPop = NULL;
    for(int lati = 0; lati < dim_lati; lati++)
    {
        //cout << "lati" << lati << endl;
        for(int longi = 0; longi < dim_longi; longi++)
        {
            //cout << "longi" << longi << endl;
            currCell = get_cell(lati, longi);
            //cout << "currCell" << currCell << endl;
            if(currCell == NULL)
            {
                cout << "print_index out of boundaries" << endl;
                //return;
            }

            currPop = currCell->get_pop(m_pSpe);
            if(currPop == NULL)
            {
                cout << "refresh isEmpty no pop" << endl;
                //return;
            }
            /*else
            {
                cout << "currPop" << currPop << endl;
            }*/
            if (currPop->get_nInd() > 0)
            {
                isPopEmpty[m_pSpe][lati*dim_longi+longi] = false;
            }
            else
            {
                isPopEmpty[m_pSpe][lati*dim_longi+longi] = true;
            }
        }
    }
}



void landscape::refresh_pop_ageClass(species * m_pSpe)
{
    vector<int> ages(dim_lati*dim_longi);
    vector<int> ninds(dim_lati*dim_longi);
    vector<int> currAges(dim_longi*dim_lati); //age classes

    for(int lati = 0; lati < dim_lati; lati++)
    {
        for(int longi = 0; longi < dim_longi; longi++)
        {
            cell* currCell = get_cell(lati, longi);
            if(currCell == NULL)
            {
                cout << "CELL OUT OF BOUNDARIES IN REFRESH POP" << endl;
            }
            population * currPop = currCell->get_pop(m_pSpe);
            if(currPop == NULL)
            {
                cout << "POP OUT OF BOUNDARIES IN REFRESH POP" << endl;
            }
            vector<int> transitionAges = m_pSpe->getAgeEnds();
            int currC = 0;
            int agePatch = currPop->get_rInd(currPop->pick_random()).get_age();
            //cout <<  "AP" << agePatch << " ";
            while ((agePatch >= transitionAges[currC]) && (currC < transitionAges.size()))
            {
                currC++;
            }
            currAges[lati*dim_longi+longi] = currC;

	    // log other informations for display
	    ninds[lati*dim_longi+longi] = currPop->get_nInd();
	    ages[lati*dim_longi+longi] = agePatch; 
        }
    }
    pop_ageClass[m_pSpe] = currAges;

    // DISPLAY
    cout << "Pop\t|\t";
    for(int p = 0; p < dim_lati*dim_longi; p++)
    {
        cout << p << "\t";
    }
    cout << "\nAge\t|\t";
    for(int p = 0; p < dim_lati*dim_longi; p++)
    {
        cout << ages[p] << "\t";
    }

    cout << "\nClass\t|\t";
    for(int p = 0; p < dim_lati*dim_longi; p++)
    {
        cout << currAges[p] << "\t";
    }

    cout << "\nNind\t|\t";
    for(int p = 0; p < dim_lati*dim_longi; p++)
    {
        cout << ninds[p] << "\t";
    }
    cout << endl;

}

vector<int> landscape::get_pop_ageClass(species * m_pSpe)
{
    return pop_ageClass[m_pSpe];
}


void landscape::print_demographics(mClock * myClock, species * m_pSpe)
{
    ostringstream oss;
    oss << "outputs/demo_" << myClock->get_step() << ".csv";
    string filename = oss.str();

    ofstream myfile (filename);
    if (!myfile.is_open())
    {
        cout << "Unable to open file";
        return;
    }

    cell * currCell = NULL;
    population * currPop = NULL;
    //Iterate on cell without iterator (avoid messing with the main loop)
    for(int lati = 0; lati < max_lati(); lati++)
    {
        for(int longi = 0; longi < max_longi(); longi++)
        {
            currCell = get_cell(lati, longi);
            if(currCell == NULL)
            {
                cout << "print_demographics out of boundaries" << endl;
                return;
            }
            currPop = currCell->get_pop(m_pSpe);
            if(currPop == NULL)
            {
                cout << "print_demographics no pop" << endl;
                return;
            }
            myfile << currPop->get_nInd() << " ";
        }
        myfile << "\n";

    }
    myfile.close();
    return;
}






void landscape::print_index(mClock * m_clock, species * m_pSpe, string key)
{
    individual_index * pIndex;

    vector<double> values;

    //Make file name
    ostringstream oss;
    oss << "outputs/quantitative/" << key << m_clock->get_step() << "_" << m_pSpe->get_name() <<  ".txt";
    string filename = oss.str();

    //Open file
    ofstream myfile (filename);
    if (!myfile.is_open())
    {
        cout << "Unable to open file";
        return;
    }


    //Iterate
    cell * currCell = NULL;
    population * currPop = NULL;
    //Iterate on cell without iterator (avoid messing with the main loop)
    for(int lati = 0; lati < max_lati(); lati++)
    {
        for(int longi = 0; longi < max_longi(); longi++)
        {
            currCell = get_cell(lati, longi);
            if(currCell == NULL)
            {
                cout << "print_index out of boundaries" << endl;
                return;
            }

            currPop = currCell->get_pop(m_pSpe);
            if(currPop == NULL)
            {
                cout << "print_index no pop" << endl;
                return;
            }

            if( (pIndex = currPop->get_index(key)) == NULL )
            {
                cout << "print_index : no key found for :"  << key << endl;
                return;
            }
            values = pIndex->get_values();


            for(unsigned int i = 0; i < values.size(); i++)
            {
                myfile << values[i] << ", ";
            }

            myfile << endl;
        }
    }
    myfile.close();
    return;
}
