package rules;

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

import java.io.File;
import java.io.FileReader;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
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.MapFunction;
import org.nuiton.math.matrix.MatrixFactory;
import org.nuiton.math.matrix.MatrixND;
import org.nuiton.math.matrix.MatrixIterator;
import org.nuiton.util.FileUtil;


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.SimulationContext;
import fr.ifremer.isisfish.simulator.SimulationParameter; 
import fr.ifremer.isisfish.simulator.PopulationMonitor; 
import fr.ifremer.isisfish.simulator.ResultManager;
import fr.ifremer.isisfish.types.TimeStep;
import fr.ifremer.isisfish.types.Month;
import fr.ifremer.isisfish.util.Doc;

/**
 * GraviteVPUE1Anchois.java
 * 
 * Created: 26 aout 2008
 * 
 * @author anonymous <anonymous@labs.libre-entreprise.org>
 * @version $Revision: 1.2 $
 * 
 * Based on: $Date: 2008-10-23 08:59:38 $ by : $Author: Stephanie $
 * Updated: $Date: 2012-05-29 
 */
public class ForcingAnnualF extends AbstractRule {
    /** to use log facility, just put in your code: log.info("..."); */
    static private Log log = LogFactory.getLog(ForcingAnnualF.class);

    @Doc("Considered species")
    public Species param_species = null;
    @Doc(value = "do the doc of F species1")
    public String F_Group_Species1 = "Inputs/F_Group_Hake.csv";
    @Doc(value = "do the doc of Catches species1")
    public String Prop_F_Str_Species1 = "Inputs/PropCatch_Strategy_Hake.csv";

 
    protected File FSpecies1;
    protected File PropFSpecies1;
  
    protected MatrixND matrixFSpecies1;
    protected MatrixND matrixPropFSpecies1;
	
   // protected List<Metier> metiers;
    // interdit de faire des set sur les strategies de la map, il faut recuperer les strategies de la date courante
   // protected Map<String, Strategy> mesStrategies;

   
    public 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_GROSS_VALUE_OF_LANDINGS_PER_STRATEGY_MET,
            //ResultName.MATRIX_EFFORT_PER_STRATEGY_MET,
            ResultName.MATRIX_FISHING_MORTALITY, // garder la ,

    };

    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 {
        return _("Setting the Fishing Mortality per population, group, str and metier using Monthly_F_Group_Species1 and Monthly_Prop_F_PerStrategy");
    
         /*
		Algorithm :
		1. reading the two ascii files 
		Monthly_F_Group_Species1 = matrix (NgGroup , NbSimulationYears) 
		  containing Species1_Fishing mortality per month and per group
		Monthly_Prop_F_Str_Species1 = matrix (NbStr, 12*NbSimulationYears)
		  containing the proportion of F exerted by each strategy per month on each group
		  these values can be derived from the proportion of catch
		2. at each time step, 
		from Isis variables compute p(str,metier) = PropStrMonth(str,metier,date)/sumOverMetiers PropStrMonth(str,metier,date)
		From rule's inputs : p(str,g) = matrixMonthlyPropFSpecies1(str,g,step)
		Compute F(pop,g,str,m,z_pop) = [NbCells(z_pop)/NbCells(zPresence_pop(step))]*p(str,g)
											*p(str,metier)* matrixMonthlyFSpecies1[g,step.getYear()]
        */
    }

    /**
     * function used to initialise MatrixND to NaN double
     */
    private MapFunction nanFunction = new MapFunction() {
        public double apply(double value) {
            return Double.NaN;
        }
    };

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

		SimulationParameter param = context.getSimulationStorage()
                .getParameter();
        int lastTimeStep = param.getNumberOfYear()* Month.NUMBER_OF_MONTH;
        List<TimeStep> steps = new ArrayList<TimeStep>();// comment mettre la liste des steps de la simulation?

        for (int i=1; i<=lastTimeStep;i++){
		  steps.add(step);
		  step.next();
		}

	
		param_species = (Species) context.getDB().findByTopiaId(param_species.getTopiaId());
          PopulationMonitor popMon = context.getPopulationMonitor();  
          List<Population> pops = popMon.getPopulation(param_species);
          List<PopulationGroup> groups = pops.getPopulationGroup();// A VERIFIER liste des groupes de species 1
	     List<Strategy> strs = SiMatrix.getSiMatrix(context).getStrategies(step);
          
          
           
    //     for (Population pop : param_species.getPopulation()) { 
    //            if (pop.getSpecies().equals(param_species)) {
     //    		 List<PopulationGroup> groups = pop.getPopulationGroup();
       //   	 List<Strategy> strs = SiMatrix.getSiMatrix(context).getStrategies(step);

      //          }
           //}
	    //  Population pop = species.getPopulation();

           
    // A VERIFIER liste des groupes de species 1
	  

      	
		
        // load coefficients of relation others file in a matrix
        if (F_Group_Species1 == null
                || "".equals(F_Group_Species1)) {
            FSpecies1= FileUtil.getFile(".*.csv",
                    "fichier 0 csv separateur ';'");
        } else {
            FSpecies1 = new File(F_Group_Species1);
        }
		if (Prop_F_Str_Species1 == null
                || "".equals(Prop_F_Str_Species1)) {
            PropFSpecies1= FileUtil.getFile(".*.csv",
                    "fichier 0 csv separateur ';'");
        } else {
            PropFSpecies1 = new File(Prop_F_Str_Species1);
         }

          

        // creation de mes strategies qui ne contient pas les espagnols
        //mesStrategies = new HashMap<String, Strategy>();
        //for(Strategy str : strs) {
        //	mesStrategies.put(str.getName(), str);
        //}

        matrixFSpecies1 = MatrixFactory.getInstance().create(
                "matAnnualFSpecies1", new List[] { groups, steps },
                new String[] { "Groups", "Steps" });
        matrixPropFSpecies1 = MatrixFactory.getInstance().create(
                "matrixPropCatchSpecies1", new List[] { strs,groups, steps },
                new String[] { "Strategies", "Groups","TimeStep" });
        
        matrixFSpecies1.importCSV(new FileReader(FSpecies1), new int[] { 0,
                0 });
        matrixPropFSpecies1.importCSV(new FileReader(PropFSpecies1), new int[] { 0, 0, 0 });
        log.info("matrixMonthlyFSpecies1 : " + matrixFSpecies1);
		log.info("matrixMonthlyPropFSpecies1 : " + matrixPropFSpecies1);

        }

    /**
     * La condition qui doit etre vrai pour faire les actions
     * 
     * @param simulation
     *            La simulation pour lequel on utilise cette regle
     * @return vrai si on souhaite que les actions soit faites
     */
    public boolean condition(SimulationContext context, TimeStep step, Metier metier)
            throws Exception {

        return true; // on y passe a chaque pas de temps 
    }

    /**
     * Si la condition est vrai alors cette action est executee avant le pas de
     * temps de la simulation.
     * 
     * @param simulation
     *            La simulation pour lequel on utilise cette regle
     */
    // Booleen permettant de ne boucler que sur un seul metier dans la preaction :
    boolean first = true;

    public void preAction(SimulationContext context, TimeStep step, Metier metier)
            throws Exception {
        if (log.isDebugEnabled()) {
            log.debug("first = " + first + "step:" + step);
        }
        log.info("first = " + first + " ,on passe dans la preaction ?");
        if (first) { // on passe dans preaction pour la premiere fois

            log.info("Oui, preaction : ");
		
            List<Strategy> strs = SiMatrix.getSiMatrix(context).getStrategies(
                    step);

            param_species = (Species) context.getDB().findByTopiaId(param_species.getTopiaId());
  		    SiMatrix siMatrix = SiMatrix.getSiMatrix(context);
            PopulationMonitor popMon = context.getPopulationMonitor();
            Population pop = siMatrix.getPopulation(step);
			List<PopulationGroup> groups = pop.getPopulationGroup();
			List<Zone> zones = pop.getPopulationZone();
			List<Metier> metiers = SiMatrix.getSiMatrix(context).getMetiers(
                    step);
					
          	MatrixND N = popMon.getN(pop);
			
			ResultStorage resultmanager = context.getSimulationStorage()
                    .getResultStorage();
            
			//on commence par creer une instance de resultats de matrice Fishing Mortality
            MatrixND FishingMortality = MatrixFactory
                    .getInstance()
                    .create(
                            ResultName.MATRIX_FISHING_MORTALITY,
                            new List[] { strs, metiers,groups,zones },
                            new String[] { n_("Strategies"), n_("Metiers"),n_("Groups"),n_("Zones") });
            log.info("Avant Calcul FishingMortality  "
                    + FishingMortality);

			// calcul de propNbcell(zone)/Nbcell(ZonePresence) par group
            MatrixND NbCellZonePresencePerGroupPop = MatrixFactory
                    .getInstance()
                    .create(
							"matNbCellZonePresencePerGroupPop",
                            new List[] { groups },
                            new String[] {n_("Groups") });
            log.info("Avant Calcul NbCellZonePresencePerGroupPop  "
                    + NbCellZonePresencePerGroupPop);
			int NbCellPerPopGroup = 0;
			for (PopulationGroup group : groups) {
				for (Zone zone : zones) {
					if (N.getValue(pop,group,zone,step) != 0)
					 NbCellPerPopGroup += zone.sizeCell();
				}
			NbCellZonePresencePerGroupPop.setValue(group,NbCellPerPopGroup);
			NbCellPerPopGroup = 0;
			}
			log.info("Apres Calcul NbCellZonePresencePerGroupPop  "
                    + NbCellZonePresencePerGroupPop);
		   
		   MatrixND SommePropStr = MatrixFactory
                    .getInstance()
                    .create(
							"matSommePropStr",
                            new List[] { strs },
                            new String[] {n_("Strategies") });
			log.info("Avant Calcul SommePropStr  "
                    + SommePropStr); 
			double somme = 0;
   		    for (Strategy str : strs) {
                log.info("Boucle de calcul de PropStr : "
                            + str.getName());
                    
                StrategyMonthInfo smi = str.getStrategyMonthInfo(step
                            .getMonth());
                Collection<EffortDescription> strMet = str
                            .getSetOfVessels().getPossibleMetiers();
				for (EffortDescription ed : strMet) {
                    Metier strMetier = ed.getPossibleMetiers();
					double prop = smi.getProportionMetier(strMetier);
					somme += prop;
                  
                }
			    SommePropStr.setValue(str,somme);
				somme = 0;
 		}
			log.info("Apres Calcul SommePropStr  "
                    + SommePropStr); 
					
			// calul de F(pop,group,str,metier,zone)		
			for (PopulationGroup group : groups) {
				double NbCellGroup = NbCellZonePresencePerGroupPop.getValue(group);
				double FpopGroup = matrixFSpecies1.getValue(group,step);
				for (Zone zone : zones) {	
					if (N.getValue(pop,group,zone,step) != 0) {
					double propZone = zone.sizeCell()/NbCellGroup;
					for (Strategy str : strs) {
						StrategyMonthInfo smi = str.getStrategyMonthInfo(step
                            .getMonth());
						Collection<EffortDescription> strMet = str
                            .getSetOfVessels().getPossibleMetiers();
						double propStr = SommePropStr.getValue(str);
						double propFstr = matrixPropFSpecies1.getValue(str,group,step);	
						for (EffortDescription ed : strMet) {
							Metier strMetier = ed.getPossibleMetiers();
							double prop = smi.getProportionMetier(strMetier);
							double FPopGroupZoneStrMet = propZone*FpopGroup*(prop/propStr)*propFstr;
							FishingMortality.setValue(str,strMetier,group,zone,FPopGroupZoneStrMet);
							}
						}
					}
				}
			
			 // remplissage de FishingMortality dans le resultManager
			resultmanager.addResult(step,pop,ResultName.MATRIX_FISHING_MORTALITY,FishingMortality);
			
			
		first = false;
        }// fin de first= true

        if (log.isDebugEnabled()) {
            log.debug("fin ForcingMonthlyF avant");
     		   }
		    }
   	}
    /**
     * Si la condition est vrai alors cette action est executee apres le pas de
     * temps de la simulation.
     * 
     * @param simulation
     *            La simulation pour lequel on utilise cette regle
     */
    public void postAction(SimulationContext context, TimeStep step, Metier metier)
            throws Exception {
        first = true;

    }
           
}
