#ifndef SPECIE_H
#define SPECIE_H

#include <iostream>
#include <string>
#include <list>
#include <vector>
#include <set>
#include <stdlib.h>
#include <cstring>
#include <unordered_map>

#include "../include/luaHead.h"
#include "../include/genome.h"
#include "../include/trait.h"
#include "../include/cell.h"
#include "../include/population.h"
#include "../include/fluxMat.h"
#include "misc.h"

/*
#include "../include/specie.h"
#include "../include/genome.h"
#include "../include/individual.h"


*/


/*
TRAITS
    -> list of values / formulas?
    -> ID, Formula
GENOME

AGE/AGE CLASS

FITNESS


*/


//TODO : ::update() => goes through traits an pops Z/E/iSel map if not at the right time

using namespace std;
class trait;
class cell;
class population;
class fluxMat;
class group_index;
class landscape;


//int myBinarySearch (vector<double> mySortedVector, double value, int exact);
//double calc_mean(vector<double> data);
double calc_var(vector<double> data);


class species
{
    public:
        species(string m_name, vector<string> m_traitsId, vector< int > m_ageClasses,
               int m_nLoci, vector<int> m_nAlleles, vector<int> m_ploidy, vector<int> chromoSplit,
               vector<trait> m_traits, vector<vector <int>> m_inherit,
               vector<double> m_crossRate, vector<double> m_mutationRate,
               vector<list<fluxMat>> m_fluxMat, vector<double> selectMatrix, int m_cap, double m_growth,
               double m_selfing, int m_maxAge, string m_assortMating, double m_rho);
        species(string m_name, vector<string> m_traitsId, vector< int > m_ageClasses,
               int m_nLoci, vector<int> m_nAlleles, vector<int> m_ploidy, vector<int> chromoSplit,
               vector<trait> m_traits, vector<vector <int>> m_inherit,
               vector<double> m_crossRate, vector<double> m_mutationRate,
               vector<list<fluxMat>> m_fluxMat, vector<double> selectMatrix, int m_cap, double m_growth,
               double m_selfing, int m_maxAge, string m_assortMating);

        void set_params(string m_name, vector<string> m_traitsId, vector< int > m_ageClasses,
               int m_nLoci, vector<int> m_nAlleles, vector<int> m_ploidy, vector<int> m_chromoSplit,
               vector<trait> m_traits,
               vector<vector <int>> m_inherit, vector<double> m_crossRate,
               vector<double> m_mutationRate,  vector<list<fluxMat>> m_fluxMat, vector<double> m_selectMatrix,
               int m_cap, double m_growth, double m_selfing, int m_maxAge, string m_assortMating);
        virtual ~species();


        // Accessors to simple fields
        string get_name() { return name; }
        int get_nLoci() { return nLoci; }
        int get_gSize() { return gSize; }
        int get_max_age() { return maxAge; }
        double get_growth() { return growth; }
        int get_cap() { return cap; }
        double get_selfing() { return selfing; }
        string get_assortMating() { return assortMating; }
        string get_assortSuffix() { return assortSuffix; }
        double get_assortThres() { return assortThres;  }

        bool is_assort_strategy(string askedStrategy);

        // Accessors to container fields
        int get_ploidy(int locus) { return ploidy.at(locus); }
        int get_locIndex(int locus) { return locIndex.at(locus); }
        int get_nAlleles(int locus) { return nAlleles.at(locus); }
        double get_crossRate(int locus) { return crossRate.at(locus); }
        double get_mutRate(int locus) { return mutationRate.at(locus); }

        // References to container fields
        vector<int>& get_ploidy() { return ploidy; }
        //double get_gammaParam() {return gammaParam; }
        vector<int> get_chromoSplit() { return chromoSplit; }
        vector<double>& get_crossRate() { return crossRate; }
        vector<double>& get_mutRate() { return mutationRate; }
        vector<double>& get_selectMatrix() { return selectMatrix; }
        vector<double>& get_selectMatrix_for_patch(int p) { return selectMatrixByPatch[p]; }
        vector<int>& get_nAlleles() { return nAlleles; }
        vector<int>& get_locIndex() {  return locIndex; }
        valarray<bool>& get_maskMale() { return mask_male; }
        valarray<bool>& get_maskFemale() { return mask_female; }
        vector<double> &get_demo() { return demo; }


        valarray<int> spawn_gamete(individual * pInd, int sex);
        vector<double> get_flux_mat(int cellNumber, int sex);

        void addMigration(population* parentPop, unsigned int dest, indId parent);
        void purgeMigrations();
        vector<string> get_origins(bool getInternal, int cellRef, int largMap);

        group_index * make_gIndex(landscape & myMap, string key, int m_nClasses);
        group_index * get_index(string key);
        bool index_exist(string key);

        void calc_demo(landscape &myMap);

        void clear_unmatchable();
        void add_unmatchable(indId u);
        set<indId> get_unmatchable();

        int select_weighted_pop(int index, int sex, vector<double> &weights);

        void checkChromoStruct(bool isDisplay);

        void get_selTraits(vector<trait*>& selTraits);
        trait * get_trait(unsigned int index);
        vector<trait *> get_traits();

        bool check_integrity(individual * pInd);

        vector<double> get_all_values(string indexedValue, landscape &myMap);
        void update_assort_threshold(landscape &myMap);
        void update_SelInt_matrix(landscape &myMap);
        void adapt_fluxMat(int t);

    protected:
    private:
        // Constant simple fields
        string name;
        int nLoci;
        int gSize;
        int maxAge;
        double growth;
        double cap;
        double selfing;
        string assortMating;
        string assortSuffix;
        double initVar;
        double assortThres;
        double gammaParam;
        double rho;

        // Constant contaner fields
        vector<int> ploidy;   //Gives the length of each locus (ploidy)
        vector<int> chromoSplit; // Describe the position of beginning for each chromosome
        vector<int> locIndex; //Gives the starting point of each locus on the genome
        vector<int> nAlleles; //Gives the number of alleles of each locus
        vector<double> crossRate; // Rate of crossing over
        vector<double> mutationRate; // Rate of mutations

        vector<double> selectMatrix;
        vector<vector<double>> selectMatrixByPatch;

        valarray<bool> mask_male;
        valarray<bool> mask_female;

        set<indId> unmatchable; // list of female to which no partner can be found at the current step

        vector<string> traitsId; //Initialisation only
        vector<int> ageClasses; //Useful
        vector<trait> traits;
        vector<int> revLocIndex; //From a position in genome gives the number of the loci
        vector<list<fluxMat>> flux; //vector of two : seed / pollen
        vector<vector<int>> fluxTime;
        vector<fluxMat> currentTimeFlux;


        // Changing fields
        vector<double> demo;
        unordered_map<string, group_index * > index;

        vector<vector<double>> allMigrations;
};

#endif // SPECIE_H
