/*
 * #%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.Species;
import fr.ifremer.isisfish.entities.Strategy;
import fr.ifremer.isisfish.entities.StrategyMonthInfo;
import fr.ifremer.isisfish.entities.TargetSpecies;
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;
    

    /** TAC in tonnes */
    @Doc("TAC in tons 2001")
    public double param_tacInTons2001 = 22600;
    @Doc("TAC in tons 2002")
    public double param_tacInTons2002 = 27000;
    @Doc("TAC in tons 2003")
	public double param_tacInTons2003 = 30000;
	@Doc("TAC in tons 2004")
	public double param_tacInTons2004 = 39100;
	@Doc("TAC in tons 2005")
	public double param_tacInTons2005 = 42600;
	@Doc("TAC in tons 2006")
	public double param_tacInTons2006 = 43900;
	@Doc("TAC in tons 2007")
	public double param_tacInTons2007 = 52680;
	@Doc("TAC in tons 2008")
	public double param_tacInTons2008 = 54000;
	@Doc("TAC in tons 2009")
	public double param_tacInTons2009 = 51500;
	@Doc("TAC in tons 2010")
	public double param_tacInTons2010 = 55105;
	@Doc("TAC in tons 2011")
	public double param_tacInTons2011 = 55105;
	
	@Doc(value = "param_Retention_L50")
    public double param_Retention_L50 = 29.4307;

    @Doc(value = "param_Retention_slope")
    public double param_Retention_slope = 2.02485;
	
	
    boolean affectation = false;
	boolean tacAtteint = 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,
    };

    /**
     * @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 tailleMin + 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
        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 tailleMin 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
		
        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 execut����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������e 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 {
        
        TargetSpecies ts = metier.getMetierSeasonInfo(step.getMonth())
                .getSpeciesTargetSpecies(param_species);
        
		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()) {
					if (!pop.getName().equals("Population_new")) {
						log.info("pop : " + pop.getName());
						MatrixND discard = popMon.getCatch(pop).copy(); //on cree une matrice de rejet identique aux captures
						log.info("catch = " + popMon.getCatch(pop));
						// 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 
						discard.setName(ResultName.MATRIX_DISCARDS_PER_STR_MET_PER_ZONE_POP);
						popMon.addDiscard(step, pop, discard);
						log.info("[TAC] add discard for " + pop + ": "+ discard);
						if (param_propSurvie > 0) {
							MatrixND eff = popMon.getN(pop);
							//on ajoute les survivants aux effectifs
							for (MatrixIterator i = discard.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");

				
				SiMatrix siMatrix = SiMatrix.getSiMatrix(context);
				List<Strategy> strategies = siMatrix.getStrategies(step);
				PopulationMonitor popMon = context.getPopulationMonitor();
				log.info("popMon biomass" + popMon.getBiomass(param_species));
				for (Population pop : param_species.getPopulation()) {
				   if (pop.getSpecies().equals(param_species)) {
						MatrixND discard = popMon.getCatch(pop).copy();
						MatrixND eff = popMon.getN(pop);
						for (Strategy str : strategies) {
							List<PopulationGroup> groups = pop.getPopulationGroup();
							for (PopulationGroup group : groups) {
								 List<Zone> zones = pop.getPopulationZone(); 
								 for (Zone zone : zones) {
									double value=0;
									discard.setValue(str,metier,group,zone,value); // initialise discard si pas de rejet (ie taille>=TailleMin et pas metierLangoustine)
									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);
									  value = popMon.getCatch(pop).getValue(str, metier, group, zone) * propRejet;
									  log.info("VALUE" + value);
									  discard.setValue(str, metier, group, zone, value);
									  
									}else if (group.getLength() < param_TailleMin) { 
									  value = popMon.getCatch(pop).getValue(str, metier, group, zone);
									  log.info("VALUE" + value);
									  discard.setValue(str, metier, group, zone, value);
									}
									if (param_propSurvie >0) {
										//ajout de la survie aux effectifs
										double valueSurvie = value*param_propSurvie;
										double effAvtRejet = eff.getValue(str, metier, group, zone);
										eff.setValue(str, metier, group, zone, effAvtRejet+valueSurvie);
									}
								}   
							}
						}
						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 = true;
			}
        }
        
    }

}
