/*
 * Copyright (C) 2014 slehuta
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public 
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 */

package rules;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import scripts.ResultName;

import java.io.Writer;

import org.nuiton.math.matrix.*;
import org.nuiton.util.*;
import fr.ifremer.isisfish.util.Doc;
import fr.ifremer.isisfish.simulator.SimulationContext;
import fr.ifremer.isisfish.types.TimeStep;
import fr.ifremer.isisfish.entities.*;
import fr.ifremer.isisfish.rule.AbstractRule;
import fr.ifremer.isisfish.datastore.SimulationStorage;
import fr.ifremer.isisfish.datastore.ResultStorage;
import scripts.MinimisationUtil;
import scripts.ObjectiveFunctionBaranov;
import fr.ifremer.isisfish.types.Month;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.HashMap;

/**
 * HCR_Nephrops_DLStrend.java
 *
 * Created: 1 septembre 2014
 *
 * @author slehuta <user.name@vcs.hostName>
 * @version $Revision: 1545 $
 * Last update: $Date: 1 septembre 2014 $
 * by : $Author: slehuta $
 */
public class HCR_Nephrops_DLStrend extends AbstractRule {

    /** to use log facility, just put in your code: log.info("..."); */
    private static Log log = LogFactory.getLog(HCR_Nephrops_DLStrend.class);
    
    public TimeStep param_BeginDate = new TimeStep(72);// demarre en 2016
    public Population param_pop = null;
    public int param_surveyTime = 5; // Langolf a lieu en mai
    public int param_DLSx = 2;
    public int param_DLSz = 5;
    @Doc(value = "buffer in decimal (0.2)")
    public double param_buffer = 0;
    //public double param_L0HCR = 21;
    public double param_RelStability = 1;
    public double param_maxTACvar = 0.15;
    //@Doc(value = "in KG")
    //public double param_Catch2009 = 4219000; //ici on a besoin des captures en 2014 disponible puisque debut de simu en 2010
    @Doc(value = "in tons")
    public double param_TAC2015 = 3899;
    //public double param_Fpa = 0.4;
    // @Doc(value = "z last years separated with ;")
    // public String param_histoLength = "";
                
    protected boolean affectation = true;        
    // protected double[] lengthAtSurvey;
    // protected double[] HistoMeanLength;
    // protected MatrixND TacPopMatrix;
    protected double[] BiomassIndex;//pour le stockage l'indice de biomasse = BiomassISIS(mai)
	
	
    protected String[] necessaryResult = {
        // put here all necessary result for this rule
	    // example: 
	   // ResultName.MATRIX_ABUNDANCE,
		ResultName.MATRIX_BIOMASS,
		ResultName.MATRIX_CATCH_PER_STRATEGY_MET_PER_ZONE_MET
      //  ResultName.MATRIX_ABUNDANCE_BEGIN_MONTH,
      //  ResultName.MATRIX_TAC_PER_POP,
      //  ResultName.MATRIX_LANDING_WEIGHT_PER_MET,
	    // ResultName.MATRIX_NET_VALUE_OF_LANDINGS_PER_STRATEGY_MET,
    };

    public String[] getNecessaryResult() {
        return this.necessaryResult;
    }

    /**
     * Permet d'afficher a l'utilisateur une aide sur la regle.
     * @return L'aide ou la description de la regle
     */
    public String getDescription() throws Exception {
        // TODO
        return "TAC value depends on mean length of the pop";
    }

    /**
     * Appel
     * des valeurs.
     * 
     * @param context la simulation pour lequel on utilise cette regle
     */
    public void init(SimulationContext context) throws Exception {
    	//log.info("pop"+param_pop.getName()+param_pop.getPopulationGroup().size());
        // lengthAtSurvey = new double[param_pop.getPopulationGroup().size()];
         int nby = context.getSimulationStorage().getParameter().getNumberOfYear();
		 log.info("nbyear init HCR nephrops"+nby);
        // HistoMeanLength = new double[nby + param_DLSz];
        // String[] histo = param_histoLength.split(";");
        // for ( int i = 0 ; i < param_DLSz; i++) {
            // double val = Double.parseDouble(histo[i]);
            // HistoMeanLength[i] = val;        
        // }
		BiomassIndex = new double[nby-2]; //nb annee de simu -2
		
    }

    /**
     * La condition qui doit etre vrai pour faire les actions.
     * 
     * @param context la simulation pour lequel on utilise cette regle
     * @param step le pas de temps courant
     * @param metier le metier concern
     * @return vrai si on souhaite que les actions soit faites
     */
    public boolean condition(SimulationContext context, TimeStep step, Metier metier) throws Exception {
        if(step.before(param_BeginDate)){
            return false;
        //}else if((step.getMonth().getMonthNumber() == param_surveyTime) || (step.getMonth().getMonthNumber() == 0)) {
		}else if( step.getMonth().getMonthNumber() == 0) {
            affectation = true;
            return true;
        }else return false;
        
    }

    /**
     * Si la condition est vrai alors cette action est executee avant le pas
     * de temps de la simulation.
     * 
     * @param context la simulation pour lequel on utilise cette regle
     * @param step le pas de temps courant
     * @param metier le metier concern
     */
    public void preAction(SimulationContext context, TimeStep step, Metier metier) throws Exception {
    if(affectation){   
        // if(step.getMonth().getMonthNumber() == param_surveyTime) {
            // List <PopulationGroup> groups = param_pop.getPopulationGroup();
            // for(PopulationGroup pg : groups){
                // lengthAtSurvey[pg.getId()] = pg.getLength();
            // }


        // }else if(step.getMonth().getMonthNumber() == 0 && step.getYear()>param_BeginDate.getYear() ){

            // // Compute Mean length in the pop at survey time
            // double MeanLength = 0;

            ResultStorage resultmanager = context.getSimulationStorage().getResultStorage();
            // TimeStep survey = step.previousYear().add(param_surveyTime);
            // MatrixND AbundancePopSurvey = resultmanager.getMatrix(survey,param_pop,ResultName.MATRIX_ABUNDANCE_BEGIN_MONTH).copy();
            // MatrixND AP = AbundancePopSurvey.sumOverDim(1).reduce();
            // double TotalAbundance = AbundancePopSurvey.sumAll();

            // Mean length at survey time
           // for(MatrixIterator i=AP.iterator(); i.hasNext();) {
                // i.next();
                // Object[] sem = i.getSemanticsCoordinates();
                // PopulationGroup group = (PopulationGroup) sem[0];
                // MeanLength += i.getValue()/TotalAbundance * lengthAtSurvey[group.getId()];
            // }
            // if(context.getValue("errML") != null) {
            	// double[] errlist = (double[])context.getValue("errML");
                // MeanLength = MeanLength * errlist[step.getYear()];
            // }
            // //log.info("MeanLength "+ MeanLength);
            // HistoMeanLength[step.getYear() + param_DLSz-1] = MeanLength;  
			// System.out.println("histolength"+HistoMeanLength[0]+HistoMeanLength[1]+HistoMeanLength[2]+HistoMeanLength[3]+HistoMeanLength[4]+HistoMeanLength[5]);
            
			// Biomasse ISIS en mai y-2,y-3,y-4,y-5,y-6
			int year = step.getYear(); //annee du TAC
			TimeStep survey = step.previousYear().add(param_surveyTime - 12); //janvier(y-1)+ param_surveyTime -12 (en y-2)
			double totalBiomass =0;
               MatrixND BiomassPopSurvey = resultmanager.getMatrix(survey,param_pop,ResultName.MATRIX_BIOMASS).copy();
			if (year==6) { //en 2016 calcul des 5 indices
			totalBiomass = BiomassPopSurvey.sumAll(); // Somme toutes les valeurs de la matrice
			BiomassIndex[4] = totalBiomass; // indice 2014
			BiomassPopSurvey = resultmanager.getMatrix(survey.minus(12),param_pop,ResultName.MATRIX_BIOMASS).copy();
			totalBiomass =  BiomassPopSurvey.sumAll(); // Somme toutes les valeurs de la matrice
			BiomassIndex[3]= totalBiomass;// indice 2013
			BiomassPopSurvey = resultmanager.getMatrix(survey.minus(24),param_pop,ResultName.MATRIX_BIOMASS).copy();
			totalBiomass =  BiomassPopSurvey.sumAll(); // Somme toutes les valeurs de la matrice
			BiomassIndex[2]= totalBiomass;// indice 2012
			BiomassPopSurvey = resultmanager.getMatrix(survey.minus(36),param_pop,ResultName.MATRIX_BIOMASS).copy();
			totalBiomass =  BiomassPopSurvey.sumAll(); // Somme toutes les valeurs de la matrice
			BiomassIndex[1]= totalBiomass;//indice 2011
			BiomassPopSurvey = resultmanager.getMatrix(survey.minus(48),param_pop,ResultName.MATRIX_BIOMASS).copy();
			totalBiomass =  BiomassPopSurvey.sumAll(); // Somme toutes les valeurs de la matrice
			BiomassIndex[0]= totalBiomass;// indice 2010
			} else { //pour les autres annees ajout de l'indice suivant dans la table
		     totalBiomass =  BiomassPopSurvey.sumAll(); // Somme toutes les valeurs de la matrice
			BiomassIndex[step.getYear()-2]= totalBiomass;// indice y-2
			} 
			
            // Calcule Catch y-2
            double Catch = 0;
            
            
                ResultStorage matResult = context.getSimulationStorage().getResultStorage();
                for (TimeStep dat = new TimeStep(0); dat.before(step); dat = dat.next()) {
                    if( dat.getYear() == (year-2)) {
                        MatrixND mat = matResult.getMatrix(dat, param_pop, 
                        ResultName.MATRIX_CATCH_PER_STRATEGY_MET_PER_ZONE_MET);
                        Catch += mat.sumAll();
                    }
                }	
            
            log.info("landings:"+Catch);
            // calcul trend in indicator
            double trend_x = 0;
            double trend_z = 0; 
            // Loop for the trend on last x years
            // for(int yx = (year- param_DLSx + param_DLSz-1); yx <=(year- 2 + param_DLSz) ; yx++){
            	// log.info("ML yx:"+HistoMeanLength[yx]);
                // trend_x += HistoMeanLength[yx];
            // }
            // trend_x = trend_x / (double)param_DLSx;
			//attention formule dans DLS (y =year-1) - year :annee du tac, y annee de calcul du tac
			for(int yx = (year-1 - param_DLSx ); yx <=(year- 2) ; yx++){
            	log.info("ML yx:"+BiomassIndex[yx]);
                trend_x += BiomassIndex[yx];
            }
            trend_x = trend_x / (double)param_DLSx;
            log.info("trend_x:"+trend_x);
            // Loop for the trend on last z years
            for(int yz = (year -1-param_DLSz); yz <=(year-1-param_DLSx -1) ; yz++){
                trend_z += BiomassIndex[yz];
            }
            trend_z = trend_z / (double)(param_DLSz-param_DLSx);
            log.info("trend_z:"+trend_z);
            double I = (trend_x/trend_z) - param_buffer;
            double TAC = Catch * I;
            log.info("TAC 1:"+TAC);
            
			// Prevent variations in TAC larger than param_maxTACvar  
            double previousTAC;
            if(context.getValue("TAC_"+ param_pop.getName()) != null){
                previousTAC = (double) context.getValue("TAC_"+ param_pop.getName());
            }else {
                previousTAC = param_TAC2015 * 1000;
            }	
            log.info("prev tac:"+previousTAC);
            if(Math.abs(TAC - previousTAC) / previousTAC > param_maxTACvar ){
            	log.info("var sup a 15%");
            	double signe = 1.0;
            	if(previousTAC > TAC){
            		signe = -1.0;
            	}
                TAC = previousTAC * (1.0 + signe * param_maxTACvar) ;
                log.info("signe ="+signe);
            }
            log.info("TAC total 2: "+ TAC); 
            context.setValue("TAC_"+ param_pop.getName(),TAC); 
            // prevent TAC above Fpa
            /*double Ftac = MinimisationUtil.fmin(0, 1, 0.01, new ObjectiveFunctionBaranov(TAC/mw,M,N));
            if(Ftac > param_Fpa) {
                TAC = param_Fpa*B;
            }*/

            // double TACfr = param_RelStability * TAC;
            // context.setValue("TACfr_"+ param_pop.getName(),TACfr); 
            // //log.info("TAC fr "+ TACfr);

            // if(param_RelStability != 1){
                // // Modify natural mortality
                    // // compute F
                // double TACoth = (1-param_RelStability) * TAC;
				// context.setValue("TACfr_"+ param_pop.getName(),TACfr); 
                // //System.out.println("TACoth"+TACoth);

                // // compute abundance, biomass and mean weight for F
                // // Import de la biomasse de l'annee en cours
                // MatrixND MatAb = resultmanager.getMatrix(step.previous(), param_pop, ResultName.MATRIX_ABUNDANCE);
                // MatAb = MatAb.sumOverDim(1).reduce();
                // double B = 0;
                // double N = 0;
                // List<PopulationGroup> groups = param_pop.getPopulationGroup(); 
                // double[] mwa = new double[param_pop.getPopulationGroup().size()+1];
                // for(PopulationGroup pg:groups){
                    // mwa[pg.getId()] = pg.getMeanWeight();
                // }
                // mwa[param_pop.getGroupMax()+1] = mwa[param_pop.getGroupMax()];

                // for (MatrixIterator i=MatAb.iterator(); i.hasNext();) {
                    // i.next();
                    // Object [] sems = i.getSemanticsCoordinates();
                    // PopulationGroup group = (PopulationGroup)sems[0];
                    // B += i.getValue()* mwa[group.getId()+1];
                    // N += i.getValue();
                // }
                // double avR = context.get(param_pop).getAsDouble("averageR");
                // B += avR * mwa[0];
                // N += avR;
                // double mw = B/N;   
                // double M = context.get(param_pop).getAsDouble("Mnat");
                // double FtotOTH = MinimisationUtil.fmin(0, 1, 0.01, new ObjectiveFunctionBaranov(TACoth/mw,M,N));
                // if(FtotOTH > 0.4) FtotOTH = 0.4;
                // context.setValue("Foth_"+ param_pop.getName(),FtotOTH);
                
                // TacPopMatrix = MatrixFactory.getInstance().create(
							// ResultName.MATRIX_TAC_PER_POP, 
                            // new List[] { Arrays.asList(new String[]{"TAC","Foth","MeanLength"})});
                // TacPopMatrix.setValue("TAC", TAC);
                // TacPopMatrix.setValue("Foth", FtotOTH);
				// TacPopMatrix.setValue("MeanLength", MeanLength);
                // resultmanager.addResult(step, param_pop, TacPopMatrix);
                
            // }
        // //}    
        affectation = false;
    }
    }

    /**
     * Si la condition est vrai alors cette action est ex
     * de temps de la simulation.
     * 
     * @param context La simulation pour lequel on utilise cette regle
     * @param step le pas de temps courant
     * @param metier le metier concern
     */
    public void postAction(SimulationContext context, TimeStep step, Metier metier) throws Exception {
        // TODO
    }
}
