#ifndef TRAIT_H
#define TRAIT_H

#include <valarray>
#include <unordered_map>


/*
A trait:
- Has an associated lua state
- A number of implicated genes + a list of loci for those genes (or a vect...)
- associated values for those locis
- An ID key
ALL of above is initialised from a lua script
We keep the acces to the lua script to call the function

Get_values takes an individual and return the value of the trait


*/
class cell;
class species ;
class individual;
class ltypes;
class population;

using indId = unsigned int;

#include "individual.h"
#include "population.h"
#include "luaHead.h"
#include "ltypes.h"
#include "cell.h"
#include "specie.h"

//void set_norm(valarray<double> &values);
//double gasdev();

class trait
{
    public:
        //trait(char * luaScript, char * key);
        /*trait(string myName, unordered_map<string, double> myParameters,
             vector<double> myE, vector<double> myZopt, vector<ltypes> myTypesIndex,
             vector<double> mySelInt, bool myNhpp, bool myIndexed);*/
        trait(string myName, unordered_map<string, double> myParameters,
             unordered_map<int, vector<double>> myTemporalE, unordered_map<int, vector<double>> myTemporalZopt, vector<ltypes> myTypesIndex,
             unordered_map<int, vector<double>> myTemporalSelInt, bool myNhpp, bool myIndexed, double meanZ, double varEps);

        virtual ~trait();

        //Trait value calculation
        double calc_add_value(individual & myInd, double E);
        vector<unsigned int> get_loci();// iterate on all ltypes to get impacted loci
        vector<vector<unsigned int>> get_loci_by_lt();

        //Simple accessors
        inline bool isKeyInTemporalZopt(unsigned int t);
        inline bool isKeyInTemporalE(unsigned int t);
        inline bool isKeyInTemporalSelInt(unsigned int t);
        inline double get_Zopt(unsigned int pos);
        inline double get_E(unsigned int pos);
        inline double get_SelInt(unsigned int pos);
        inline string get_name();
        inline ltypes * get_ltype(unsigned int i);
        inline unsigned int get_nLtype();
        inline bool is_nhpp();
        inline bool is_indexed();
        inline double get_meanZ();
        inline double get_varEps();

        //Change environmental values if specific step is reached
        void set_E(unsigned int t);
        void set_Zopt(unsigned int t);
        void set_SelInt(unsigned int t);


    protected:
    private:
        // 1 : Id
        string name;
        // 2 : params
        unordered_map<string, double> parameters;
        // 3 : E
        vector<double> E; // => change location to cell? should be a list of frames
        unordered_map<int, vector<double>> temporalE;
        // 4 : Zopt
        vector<double> Zopt; // => change location to cell? should be a list of frames
        unordered_map<int, vector<double>> temporalZopt;
        // 5 : types
        vector<ltypes> typesIndex;
        // 6 : SelInt
        vector<double> selInt;
        unordered_map<int, vector<double>> temporalSelInt;
        // 7 : Non heritable phenotipic plasticity  (NHPP)
        //used when claulation genetic value
        bool nhpp;
        //8 : indexed
        bool indexed;

        double meanZ;
        double varEps;
};



inline bool trait::isKeyInTemporalSelInt(unsigned int t)
{
    if (temporalSelInt.count(t) > 0)
    {
        return true;
    }
    else
    {
        return false;
    }
}

inline bool trait::isKeyInTemporalZopt(unsigned int t)
{
    if (temporalZopt.count(t) > 0)
    {
        return true;
    }
    else
    {
        return false;
    }
}

inline bool trait::isKeyInTemporalE(unsigned int t)
{
    if (temporalE.count(t) > 0)
    {
        return true;
    }
    else
    {
        return false;
    }
}


inline double trait::get_meanZ()
{
    return meanZ;
}

inline double trait::get_varEps()
{
    return varEps;
}

//Add range check on accessors?
//Include usage of time
inline double trait::get_Zopt(unsigned int pos)
{
    return Zopt[pos];
}

inline double trait::get_E(unsigned int pos)
{
    return E[pos];
}

inline double trait::get_SelInt(unsigned int pos)
{
    return selInt[pos];
}

inline string trait::get_name()
{
    return name;
}

inline ltypes * trait::get_ltype(unsigned int i)
{
    return &typesIndex[i];
}

unsigned int trait::get_nLtype()
{
    return typesIndex.size();
}


inline bool trait::is_nhpp()
{
    return nhpp;
}


inline bool trait::is_indexed()
{
    return indexed;
}


#endif // TRAIT_H
