/*
 * #%L
 * IsisFish data
 * %%
 * Copyright (C) 2006 - 2011 Ifremer, CodeLutin
 * %%
 * 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 2 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-2.0.html>.
 * #L%
 */
package rules;

import static org.nuiton.i18n.I18n._;

import java.util.HashSet;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuiton.math.matrix.MatrixIterator;
import org.nuiton.math.matrix.MatrixND;

import scripts.ResultName;
import scripts.RuleUtil;
import scripts.SiMatrix;
import fr.ifremer.isisfish.datastore.ResultStorage;
import fr.ifremer.isisfish.entities.EffortDescription;
import fr.ifremer.isisfish.entities.Metier;
import fr.ifremer.isisfish.entities.Population;
import fr.ifremer.isisfish.entities.PopulationGroup;
import fr.ifremer.isisfish.entities.Species;
import fr.ifremer.isisfish.entities.Strategy;
import fr.ifremer.isisfish.entities.StrategyMonthInfo;
import fr.ifremer.isisfish.entities.TargetSpecies;
import fr.ifremer.isisfish.entities.Zone; // ???????
import fr.ifremer.isisfish.rule.AbstractRule;
import fr.ifremer.isisfish.simulator.MetierMonitor;
import fr.ifremer.isisfish.simulator.PopulationMonitor;
import fr.ifremer.isisfish.simulator.SimulationContext;
import fr.ifremer.isisfish.types.TimeStep;
import fr.ifremer.isisfish.types.Month;
import fr.ifremer.isisfish.util.Doc;

/**
 * Le but de cette regle est de permettre de simuler une harvest control rule,
 * et plus precisemment la transition entre une gestion par approche de precaution 
 * et une gestion par le MSY.
 *
 * La premiere annee de transition est 2011 et la derniere 2014, pour une gestion
 * au MSY des 2015.
 * 
 *Ici cette regle va s'appliquer a partir de la quatrieme annee de simu (annee initiale=2008)
 * et a chacune des 4 populations simulees, alors que jusqu'a 2010 on avait un TAC commun pour la plie.
 * 
 * Created:
 *
 * @author anonymous <anonymous@labs.libre-entreprise.org>
 * @version $Revision: 0.1 $
 *
 * Last update: $Date: 131212 $
 * by : $Author: Loic $
 */
public class  HCR_transition_MSY extends AbstractRule {

    /** to use log facility, just put in your code: log.info("..."); */
    static private Log log = LogFactory.getLog(HCR_transition_MSY.class);

	@Doc("Affected population")
    public Population param_populationHCR = null;
    @Doc("Begin date")
    public TimeStep param_beginStep = new TimeStep(36);
    @Doc("End date")
    public TimeStep param_endStep = new TimeStep(119);
    @Doc("Fmsy")
    public double param_fmsy = 0.29;
    @Doc("Fpa")
    public double param_fpa = 0.4;
	@Doc("MSY Btrigger")
    public double param_msyBtrigger = 8000; // Attention aux unites : kg VS t
	@Doc("Duration of transition period (year)")
    public double param_transitionDuration = 5;
	@Doc("Proportion de survie")
    public double param_propSurvieHCR = 0;
	@Doc("Variation annuelle max du TAC ")
    public double param_varTac = 0.15;

	//public double Tac2008 = 0;
	//public double Tac2009 = 0;
	public double Tac2010 = 0;

    boolean affectation = false;

    protected String[] necessaryResult = {
    // put here all necessary result for this rule
    // example: 
    // ResultName.MATRIX_BIOMASS,
    // ResultName.MATRIX_NET_VALUE_OF_LANDINGS_PER_STRATEGY_MET,
	ResultName.MATRIX_BIOMASS,
	ResultName.MATRIX_TOTAL_FISHING_MORTALITY,
		
    };

    /**
     * @return the necessaryResult
     */
    @Override
    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
     */
    @Override
    public String getDescription() {
        return _("Harvest Control Rule simulatinf the transition between a precautionary approach management and a MSY management, for a given populaion");
    }

    /**
     * Appele au demarrage de la simulation, cette methode permet d'initialiser
     * des valeurs
     * 
     * @param context La simulation pour lequel on utilise cette regle
     */
    @Override
    public void init(SimulationContext context) throws Exception {
    }

    /**
     * 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 concerne
     * @return vrai si on souhaite que les actions soit faites
     */
    @Override
    public boolean condition(SimulationContext context, TimeStep step, Metier metier)
            throws Exception {

        boolean result = false;
		
		if (step.after(param_beginStep) && step.before(param_endStep) && step.getMonth() == Month.FEBRUARY){ // .previous() // JANUARY ?
			result = true;
             }
	    return result;		
    }

    /**
     * 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 concerne
     */
    @Override
    public void preAction(SimulationContext context, TimeStep step, Metier metier)
            throws Exception {
			
		//Tac2008 = (double) context.getValueAndCompute("tacInTons2008", 0);
		//Tac2009 = (double) context.getValueAndCompute("tacInTons2008", 0);
		Tac2010 = (double) context.getValueAndCompute("tacInTons2010", 0);
		log.info("Tac2010 = " + Tac2010);
	
        affectation = false;

        log.info("Mise en place de l'HCR");
        
		ResultStorage matResult = context.getSimulationStorage().getResultStorage();
		
		// Import de la mortalite par peche de 2010
		MatrixND MatF2010 = matResult.getMatrix(param_beginStep.previous(), param_populationHCR, ResultName.MATRIX_TOTAL_FISHING_MORTALITY); // Ne marche que si param_beginStep est Janvier 2011
		double F2010 = MatF2010.sumAll();
		log.info("F2010 = " + F2010);
		
		// Import de la biomasse de l'annee en cours
		MatrixND MatBiom = matResult.getMatrix(step.previous(), param_populationHCR, ResultName.MATRIX_BIOMASS); // step,
		MatBiom = MatBiom.reduce();
		log.info("MatBiom = " + MatBiom);
		
				
		// Calcul de la SSB de l'annee en cours
		double SSBiom = 0;		
		
		for (MatrixIterator i=MatBiom.iterator(); i.hasNext();) {
			i.next();
			Object [] sems = i.getSemanticsCoordinates();
			PopulationGroup group = (PopulationGroup)sems[0];
			
			double val = i.getValue()* group.getMaturityOgive();

			SSBiom = SSBiom + val;
		}
		SSBiom = SSBiom / 1000;
		log.info("SSBiom  = " + SSBiom);
		
		// Comparaison de la SSB a MSYBtrigger et calcul de fMsyHcr
		double fMsyHcr = 0;
		
		if ( SSBiom < param_msyBtrigger) {
			double triggerMult = 1 - SSBiom / param_msyBtrigger;
			fMsyHcr = param_fmsy - triggerMult * param_fmsy;
		}
		else {
		fMsyHcr = param_fmsy;
		}
		
		// Calcul des multiplicateurs a appliquer a F2010 et fMsyHCR
		double multF2010 = 1 - (step.getYear() - (param_beginStep.getYear() - 1)) * (1/param_transitionDuration);
		if (multF2010 < 0){
			multF2010 = 0;
		}
		double multFmsy = 0 + (step.getYear() - (param_beginStep.getYear() - 1)) * (1/param_transitionDuration);
		if (multFmsy > 1){
			multFmsy = 1;
		}
		log.info("multF2010 = " + multF2010);
		log.info("multFmsy = " + multFmsy);
		
		// Calcul de fMsyHcrTransition
		double fMsyHcrTransition = multF2010 * F2010 + multFmsy * fMsyHcr;
		log.info("fMsyHcrTransition = " + fMsyHcrTransition);
		
		// Si fMsyHcrTransition > fpa on applique fpa
		double AppliedF = 0;
		
		if (fMsyHcrTransition < param_fpa) {
			AppliedF = fMsyHcrTransition;
		}
		else {
			AppliedF = param_fpa;
		}
		log.info("AppliedF = " + AppliedF);
		
		// Calcul du niveau de captures correspondant au F choisi
		double TAC = AppliedF * SSBiom;
		log.info("TAC = " + TAC);
		
		// Si niveau de captures inferieur de plus de 15 pcts au TAC de l'annee precedente limiter a 15 pcts
		double diffTac = 0;
		double TacReel = 0;
		double TacPrevYr = 0;
		
		if (step.getYear() == param_beginStep.getYear()){
			diffTac = TAC / Tac2010 - 1;
			log.info("diffTac = " + diffTac);
			if (diffTac < -1 * param_varTac){
				TacReel = Tac2010 - param_varTac * Tac2010;
			}
			else if (diffTac >= param_varTac){
				TacReel = Tac2010 + param_varTac * Tac2010;
			}
			else {
			TacReel = TAC;
			}
		}
		else {
		// retrouver le niveau de TAC de l'annee precedente
		TacPrevYr = (double) context.getValueAndCompute("TAC_HCR", 0);
		log.info("TacPrevYr = " + TacPrevYr);
		diffTac = TAC / TacPrevYr - 1;
		log.info("diffTac = " + diffTac);
		if (diffTac < -1 * param_varTac){
				TacReel = TacPrevYr - param_varTac * TacPrevYr;
			}
		else if (diffTac >= param_varTac){
				TacReel = TacPrevYr + param_varTac * TacPrevYr;
			}
		else {
			TacReel = TAC;
			}
		}
		log.info("TacReel = " + TacReel);
		
		// Stocker le TAC dans une matrice (pour pouvoir comparer les annees entre elles)
		
		// Faire en sorte que le TAC soit utilise par la regle TACPoidsPop_PourHCR
		context.setComputeValue("TAC_HCR", TacReel); //TacReel
		context.setValue("BeginStep_TAC_HCR", step); // Pas besoin de EndStep car on sait que c'est BeginStep+12 (mis dans regle TAC)
		context.setValue("Population_TAC_HCR", param_populationHCR);
		context.setComputeValue("PropSurvie_TAC_HCR", param_propSurvieHCR);
		
	}

    /**
     // * Si la condition est vrai alors cette action est executee apres 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 concerne
     */
    @Override
    public void postAction(SimulationContext context, TimeStep step, Metier metier)
            throws Exception {
		// Creation de la matrice de stockage de TAC
		//ResultStorage resultmanager = context.getSimulationStorage().getResultStorage();
		//
        //    if (!affectation){
		//	
        //     // Ici les TACs sont definis au niveau de la population
		//	 public MatrixND matrixTacPop(TimeStep step, Population pop) throws TopiaException { // ResultStorage resManager ?
		//	 
		//			List<Population> populations = Collections.singletonList(pop);			
		//	 
		//			MatrixND TacPopMatrix = MatrixFactory.getInstance().create(
		//					ResultName.MATRIX_TAC_PER_POP, new List[] { populations },
		//					new String[] { n_("Populations") });
		//
		//			if (step.getMonth() == Month.JANUARY) {		// Pas besoin car postAction ne s'applique que si la condition est vraie
		//				TacPopMatrix.setValue(param_population, TacReel);
		//			}
		//			else {		
		//				TacPopMatrix.setValue(param_population, 0);
		//			}
		//			return TacPopMatrix;
		//		}
		//			// on a affecte une fois un TAC a une pop et il ne faut pas le refaire
		//			affectation = true;
        //    }
        //}
    }

}
