/*
 * #%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 java.util.List;
import java.util.ArrayList;
import java.util.Iterator;

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.Species;
import fr.ifremer.isisfish.entities.Strategy;
import fr.ifremer.isisfish.entities.StrategyMonthInfo;
import fr.ifremer.isisfish.entities.TargetSpecies;
import fr.ifremer.isisfish.entities.MetierSeasonInfo;
import fr.ifremer.isisfish.entities.PopulationGroup;
import fr.ifremer.isisfish.entities.*;
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;

/**
 * TAC peut-etre utilise pour les differents TAC, en proportion des effectifs
 * et/ou avec survie ou non.
 * 
 * <li>Pour utiliser le tac proportionnel, il faut mettre dans le parametre
 * propTac une valeur > 0, le TAC sera alors recalcule a chaque mois de janvier.
 * <li>Pour utiliser la survie il faut mettre dans le parametre propSurvie une
 * valeur > 0, automatiquement les suvie seront ajoute aux effectifs
 * 
 * Created: 7 septembre 2006
 *
 * @author anonymous <anonymous@labs.libre-entreprise.org>
 * @version $Revision: 1.3 $
 *
 * Last update: $Date: 30 aout 2012 $
 * by : $Author: Stephanie Mahevas$
 */
public class TACpoids20012011OgiveTriMerlu extends AbstractRule {

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

    @Doc("Affected species")
    public Species param_species = null;
    
    @Doc("Proportion de survie")
    public double param_propSurvie = 0.45;
    @Doc(value = "param_Retention_L50")
    public double param_Retention_L50 = 29.4307;

    @Doc(value = "param_Retention_slope")
    public double param_Retention_slope = 2.02485;
	
	@Doc(value = "param_TailleMin")
    public double param_TailleMin = 27.0;
    /** TAC in tonnes */
    @Doc("TAC in tons 2001")
    public double param_tacInTons2001 =8452;
    @Doc("TAC in tons 2002")
    public double param_tacInTons2002 = 10098;
    @Doc("TAC in tons 2003")
	public double param_tacInTons2003 = 11220;
	@Doc("TAC in tons 2004")
	public double param_tacInTons2004 = 14623;
	@Doc("TAC in tons 2005")
	public double param_tacInTons2005 = 15932;
	@Doc("TAC in tons 2006")
	public double param_tacInTons2006 = 16412;
	@Doc("TAC in tons 2007")
	public double param_tacInTons2007 = 19701;
	@Doc("TAC in tons 2008")
	public double param_tacInTons2008 = 20196;
	@Doc("TAC in tons 2009")
	public double param_tacInTons2009 = 19261;
	@Doc("TAC in tons 2010")
	public double param_tacInTons2010 = 20609;
	@Doc("TAC in tons 2011")
	public double param_tacInTons2011 = 20609;
	
	
	

    
	
    boolean affectation = true; // pour ne passer qu'une seule fois dans afterSimulation (pour le premier metier)
	boolean tacAtteint = false; // pour ne passer dans beforeSimulation que si le tac est atteint

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

    /**
     * @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 _("Combinaison de la regle tac poids sur 2001-2011 avec ogive de tri + prop de survie des rejets.");
    }

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

        log.info("test si TAC atteint");
        // read species in current session : plus necessaire bug resolu
        //param_species = (Species) context.getDB().findByTopiaId(
        //        param_species.getTopiaId());
		TargetSpecies ts = metier.getMetierSeasonInfo(step.getMonth())
					.getSpeciesTargetSpecies(param_species);
        boolean result = false;
		if (ts != null) { // on n'applique la regle que si espece capturÃƒÆ’Ã†â€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÆ’Ã¢â‚¬Â ÃƒÂ¢Ã¢â€šÂ¬Ã¢â€žÂ¢ÃƒÆ’Ã†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã…Â¡ÃƒÆ’Ã¢â‚¬Å¡Ãƒâ€šÃ‚Â©e par ce metier
			double param_tacInTons = 0;
			
			if (step.getYear()>10) { //si on est apres 2011
				result = false;
			} else {
			    result = true; // au moins ogive tri s'applique
				if (step.getYear()==0) {
					param_tacInTons = param_tacInTons2001;
				} else if (step.getYear()==1) {
					param_tacInTons = param_tacInTons2002;
				} else if (step.getYear()==2) {
					param_tacInTons = param_tacInTons2003;
				} else if (step.getYear()==3) {
					param_tacInTons = param_tacInTons2004;
				} else if (step.getYear()==4) {
					param_tacInTons = param_tacInTons2005;
				} else if (step.getYear()==5) {
					param_tacInTons = param_tacInTons2006;
				} else if (step.getYear()==6) {
					param_tacInTons = param_tacInTons2007;
				} else if (step.getYear()==7) {
					param_tacInTons = param_tacInTons2008;
				} else if (step.getYear()==8) {
					param_tacInTons = param_tacInTons2009;
				} else if (step.getYear()==9) {
					param_tacInTons = param_tacInTons2010;
				} else if (step.getYear()==10) {
					param_tacInTons = param_tacInTons2011;
				} 
				// test si Tac atteint
				double catchTons = RuleUtil.getTotalCatchTons(context,
							param_species, step);
				log.info("[TAC] catchTons = " + catchTons
							+ " >= param_tacInTons:" + param_tacInTons);
				if (catchTons >= param_tacInTons) { 
					tacAtteint = 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 concern   */
    @Override
    public void preAction(SimulationContext context, TimeStep step, Metier metier)
            throws Exception {
	
		// la preaction ne tourne que si le TAC est atteint
		affectation = true;
		
        if (tacAtteint) {
           
			log.info("[TAC] preAction for: " + metier);
			log.info(" TAC atteint [TAC] preAction for: " + metier);
			TargetSpecies ts = metier.getMetierSeasonInfo(step.getMonth())
					.getSpeciesTargetSpecies(param_species);
			if (ts != null && ts.getPrimaryCatch()) {
				// recupere tous les metiers qui ont l'espece en capture principale =>metiers vises
				// aimedMetiers ne fonctionne pas je ne sais pas pourquoi ! mais au final forbiddenMetier aura le meme effet
				
				context.getMetierMonitor().addforbiddenMetier(metier);

				//recupere toutes les strategies pratiquant le metier et pour lesquelles la proportion !=0
				SiMatrix siMatrix = SiMatrix.getSiMatrix(context);
				Set<Strategy> strs = new HashSet<Strategy>();
				for (Strategy str : siMatrix.getStrategies(step)) {
					double prop = str.getStrategyMonthInfo(step.getMonth())
							.getProportionMetier(metier);
					if (prop != 0) {
						strs.add(str);
					}
				}

				for (Strategy str : strs) {
					StrategyMonthInfo smi = str.getStrategyMonthInfo(step
							.getMonth());

					// 1er cas de figure: l'effort est reporte sur un metier de la
					// meme strategie, n'ayant pas l'espece comme capture principale
					// et pechant avec le meme engin
					Set<Metier> possibleMetierCase1 = new HashSet<Metier>();
					// second cas de figure: on cherche un metier de substitution
					// sans condition sur les engins, mais qui soit pratique
					Set<Metier> possibleMetierCase2 = new HashSet<Metier>();
					// 3 eme cas de figure: on cherche des metiers non vises,
					// sans consideration sur les engins, et pour lesquels la
					// proportion peut etre nulle
					Set<Metier> possibleMetierCase3 = new HashSet<Metier>();

					for (EffortDescription effort : str.getSetOfVessels()
							.getPossibleMetiers()) {
						Metier newMetier = effort.getPossibleMetiers();
						if (
						/*!aimedMetiers.contains(newMetier)
						&&*/!metier.getName().equalsIgnoreCase("nonActiviy")
								&& !metier.getName().equalsIgnoreCase("nonActivie")
								&& !metier.getName().equalsIgnoreCase(
										"non Activite")
								&& !context.getMetierMonitor().getForbiddenMetier()
										.contains(newMetier)) {
							possibleMetierCase3.add(newMetier);

							if (smi.getProportionMetier(newMetier) != 0) {
								possibleMetierCase2.add(newMetier);

								if (metier.getGear().equals(newMetier.getGear())) {
									possibleMetierCase1.add(newMetier);
								}
							}
						}
					}

					Set<Metier> possibleMetier = null;
					if (possibleMetierCase1.size() != 0) {
						log.info("[TAC] Use case 1");
						possibleMetier = possibleMetierCase1;
					} else if (possibleMetierCase2.size() != 0) {
						log.info("[TAC] Use case 2");
						possibleMetier = possibleMetierCase2;
					} else if (possibleMetierCase3.size() != 0) {
						log.info("[TAC] Use case 3");
						possibleMetier = possibleMetierCase3;
					}

					if (possibleMetier != null) {
						// on repartit maintenant l'effort entre les differents metiers
						// possibles dans la meme strategie si un metier possible existe
						// bien la repartion est proportionnelle a l'effort deja alloue
						// dans la strategie

						double somme = 0;
						for (Metier met : possibleMetier) {
							somme += smi.getProportionMetier(met);
						}
						for (Metier met : possibleMetier) {
							double newProportion = smi.getProportionMetier(met)
									+ (smi.getProportionMetier(metier)
											* smi.getProportionMetier(met) / somme);
							smi.setProportionMetier(met, newProportion);
						}
						smi.setProportionMetier(metier, 0); //le metier vise a alors une proportion nulle 
						log.info("[TAC] il y a des metiers possibles");
					} else {
						log.info("[TAC] Use no activity");

						// sinon on met tout dans le metier nonActivite
						MetierMonitor metierMon = context.getMetierMonitor();
						MatrixND mat = metierMon.getOrCreateNoActivity(step,
								ResultName.MATRIX_NO_ACTIVITY, siMatrix
										.getStrategies(step), siMatrix
										.getMetiers(step));
						mat.setValue(str, metier, smi.getProportionMetier(metier));

						smi.getProportionMetier().setValue(metier, 0);
					}
				}
			}
		          
		
        }
       
    }

    /**
     * Si la condition est vrai alors cette action est execute 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 concern */
    @Override
    public void postAction(SimulationContext context, TimeStep step, Metier metier)
            throws Exception {
        
        
        
		if (affectation) {
			// Application des rejets si Tac atteint et tailleMin sinon sur les captures  
			// la matrice de captures a une dimension metier - donc a cette etape, toutes 
			// les captures de tous les metiers sont connues
			// Ne doit pas s'appliquer pour chaque metier de la boucle mais une seule fois! 
			// affectation est mis ÃƒÆ’Ã†â€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÆ’Ã¢â‚¬Â ÃƒÂ¢Ã¢â€šÂ¬Ã¢â€žÂ¢ÃƒÆ’Ã†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã…Â¡ÃƒÆ’Ã¢â‚¬Å¡Ãƒâ€šÃ‚Â  true ÃƒÆ’Ã†â€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÆ’Ã¢â‚¬Â ÃƒÂ¢Ã¢â€šÂ¬Ã¢â€žÂ¢ÃƒÆ’Ã†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã…Â¡ÃƒÆ’Ã¢â‚¬Å¡Ãƒâ€šÃ‚Â  la fin de la boucle sur les pop
			// la preaction ne tourne que si le TAC est atteint
						
			if (tacAtteint) { //postAction de la regle TAC dans ce cas TailleMin ne s'applique pas
				log.info("[TAC] postAction for: " + metier);
	
				PopulationMonitor popMon = context.getPopulationMonitor();
                    log.info("popMon biomass" + popMon.getBiomass(param_species));
                    for (Population pop : param_species.getPopulation()) {
                    
                        log.info("pop : " + pop.getName());
                        // si on a deja une matrice rejet on le vide (elle vient
                        // forcement de la regle taille minimale or si le tac est
                        // atteint, tout va dorenavent dans les rejets et on mais
                        // TOUTES les captures dans les rejets
                        MatrixND discard = popMon.getDiscard(step, pop);
                        //log.info("discard : " + discard);
                        if (discard != null) {
                            discard.mults(0);
                        }
                        log.info("catch = " + popMon.getCatch(pop));
                        MatrixND discardRegle = popMon.getCatch(pop).copy(); //nouvelle instance pour le calcul dans la regle
                        // ca ne doit pas pouvoir marcher car MATRIX_DISCARDS_PER_STR_MET est de dimension pop groupe str met - et discard n'a plus la dimension pop 
                        discardRegle.setName(ResultName.MATRIX_DISCARDS_PER_STR_MET_PER_ZONE_POP);
                        popMon.addDiscard(step, pop, discardRegle);
                        log.info("[TAC] add discard for " + pop + ": "
                                + discardRegle);
                        if (param_propSurvie > 0) {
                            MatrixND eff = popMon.getN(pop);
                            //on rÃƒÆ’Ã†â€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÆ’Ã¢â‚¬Â ÃƒÂ¢Ã¢â€šÂ¬Ã¢â€žÂ¢ÃƒÆ’Ã†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã‚Â ÃƒÆ’Ã‚Â¢ÃƒÂ¢Ã¢â‚¬Å¡Ã‚Â¬ÃƒÂ¢Ã¢â‚¬Å¾Ã‚Â¢ÃƒÆ’Ã†â€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÆ’Ã‚Â¢ÃƒÂ¢Ã¢â‚¬Å¡Ã‚Â¬Ãƒâ€šÃ‚Â ÃƒÆ’Ã†â€™Ãƒâ€šÃ‚Â¢ÃƒÆ’Ã‚Â¢ÃƒÂ¢Ã¢â€šÂ¬Ã…Â¡Ãƒâ€šÃ‚Â¬ÃƒÆ’Ã‚Â¢ÃƒÂ¢Ã¢â€šÂ¬Ã…Â¾Ãƒâ€šÃ‚Â¢ÃƒÆ’Ã†â€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÆ’Ã¢â‚¬Â ÃƒÂ¢Ã¢â€šÂ¬Ã¢â€žÂ¢ÃƒÆ’Ã†â€™Ãƒâ€šÃ‚Â¢ÃƒÆ’Ã‚Â¢ÃƒÂ¢Ã¢â€šÂ¬Ã…Â¡Ãƒâ€šÃ‚Â¬ÃƒÆ’Ã¢â‚¬Â¦Ãƒâ€šÃ‚Â¡ÃƒÆ’Ã†â€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÆ’Ã‚Â¢ÃƒÂ¢Ã¢â‚¬Å¡Ã‚Â¬Ãƒâ€¦Ã‚Â¡ÃƒÆ’Ã†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã…Â¡ÃƒÆ’Ã¢â‚¬Å¡Ãƒâ€šÃ‚Â©ajoute les survivants aux effectifs
                            for (MatrixIterator i = discardRegle.iterator(); i
                                    .next();) {
                                Object[] coord = i.getSemanticsCoordinates();
                                eff.setValue(coord[2], coord[3], eff.getValue(
                                        coord[2], coord[3])
                                        + i.getValue() * param_propSurvie);
                            }
                        }         
                }
			
			} else {
				log.info("deb de affecterCaptureRejetSelonOgive");

			PopulationMonitor popMon = context.getPopulationMonitor();
               for (Population pop : param_species.getPopulation()) {
					MatrixND discard = popMon.getCatch(pop).copy();
					MatrixND eff = popMon.getN(pop);
					
					for (MatrixIterator i = discard.iterator(); i.next();) {
                            Object[] coordonnees = i.getSemanticsCoordinates();
               				PopulationGroup group = (PopulationGroup) coordonnees[2];
			                // Metier met = (Metier) coordonnees[1];
							if (metier.getName().equals("metier lang simp Sud")||metier.getName().equals("metier lang simp Nord")||metier.getName().equals("metier lang simple")||metier.getName().equals("metier lang jum Nord")||metier.getName().equals("metier lang jum Sud")||metier.getName().equals("metier lang jum")) {
								 
								  double propTrie = 1 / (1 + Math.exp(-(group.getLength() - param_Retention_L50)/ param_Retention_slope));
								  double propRejet = 1 - propTrie;
								  log.info("REJETS" + propRejet);
								  double value = i.getValue() * propRejet;
								  log.info("VALUE" + value);
								  i.setValue(value);
								  if (param_propSurvie >0) {
									//ajout de la survie aux effectifs
									eff.setValue(coordonnees[2], coordonnees[3],
                                        eff.getValue(coordonnees[2],
                                        coordonnees[3]) + i.getValue() *
                                        param_propSurvie);
							    }
								  
							}else if (group.getLength() >= param_TailleMin) { 
								   i.setValue(0); // initialise discard si pas de rejet (ie taille>=TailleMin et pas metierLangoustine)
							       
							} else {  //si group.getLength() < param_TailleMin
								 if (param_propSurvie >0) {
									//ajout de la survie aux effectifs
									eff.setValue(coordonnees[2], coordonnees[3],
                                             eff.getValue(coordonnees[2],
                                             coordonnees[3]) + i.getValue() *param_propSurvie);
							       }
							
					         }
					}         
					discard.setName(ResultName.MATRIX_DISCARDS_PER_STR_MET_PER_ZONE_POP);
					popMon.addDiscard(step, pop, discard); 
			}
				
			}
			// on a affecte une fois cette meta pop au rejet il ne faut pas
				// le refaire
			affectation = false;
        }
        
    }

}
