/*
 * #%L
 * IsisFish data
 * %%
 * Copyright (C) 2006 - 2015 Ifremer, CodeLutin, Stéphanie Mahevas, Chatellier Eric
 * %%
 * 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>.
 * #L%
 */
package rules;

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 fr.ifremer.isisfish.entities.Metier;
import fr.ifremer.isisfish.entities.MetierSeasonInfo;
import fr.ifremer.isisfish.entities.Population;
import fr.ifremer.isisfish.entities.PopulationGroup;
import fr.ifremer.isisfish.entities.Species;
import fr.ifremer.isisfish.entities.TargetSpecies;
import fr.ifremer.isisfish.entities.Zone;
import fr.ifremer.isisfish.rule.AbstractRule;
import fr.ifremer.isisfish.simulator.PopulationMonitor;
import fr.ifremer.isisfish.simulator.SimulationContext;
import fr.ifremer.isisfish.types.TimeStep;
import fr.ifremer.isisfish.annotations.Doc;
import resultinfos.MatrixDiscardsPerStrMetPerZonePop;

import java.util.List;

/**
 * Cette regles tient compte de la taille minimum des poissons capturable.
 * 
 * Les captures ne sont pas impactées, mais des rejets sont générés si la
 * taille des poissons est inférieures à TailleMin.
 * 
 * En cas de propSurvie positif, la matrice d'effectif est modifié
 * pour tenir compte de la partie de poisson rejeté qui survi.
 * 
 * Created: 30 novembre 2006
 * 
 * @author smahevas
 * @version $Revision: 1.1 $
 * 
 * Last update: $Date: 2007-01-24 18:25:34 $ by : $Author: bpoussin $
 */
public class TailleMin extends AbstractRule {

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

    @Doc(value = "Begin step")
    public TimeStep param_beginStep = new TimeStep(0);

    @Doc(value = "End date")
    public TimeStep param_endStep = new TimeStep(119);

    @Doc(value = "Affected species")
    public Species param_species = null;

    @Doc(value = "Taille minimale")
    public double param_TailleMin = 27;

    @Doc(value = "Proportion de survie")
    public double param_propSurvie = 0;

    protected boolean affectation = true;

    protected String[] necessaryResult = {
        // put here all necessary result for this rule
        // example:
        // MatrixBiomass.NAME,
        // MatrixNetValueOfLandingsPerStrategyMet.NAME
    };

    @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() throws Exception {
        return "Tous les poissons dont la taille est inférieure à TailleMin"
                + " ne seront pas capturés. Ils sont placés en rejets et rejetés"
                + " suivant la proportion de survie.";
    }

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

    }

    private boolean isCaptureDate(TimeStep step, Species species, Metier metier) {
        MetierSeasonInfo info = metier.getMetierSeasonInfo(step.getMonth());
        TargetSpecies target = info.getSpeciesTargetSpecies(species);

        boolean result;
        if (target != null) {
            result = true;
        } else {
            result = false;
        }

        return result;
    }

    /**
     * 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 {
        log.info("Recherche si la taille Minimale s'applique");
        boolean result = true;
        if (step.before(param_beginStep)) {
            result = false;
        } else if (step.after(param_endStep)) {
            result = false;
        } else if (isCaptureDate(step, param_species, metier) != true) {
            result = false;
        }

        log.info("fin de condition TailleMin:" + result);
        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 {

        affectation = true; // pour que affectation soit vraie au debut de
                            // chaque pas de temps pour rentrer au moins une
                            // fois dans postaction
    }

    /**
     * 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 {
        //log.info("*$*$*$* TailleMin.actionApres:" + step + " metapop:"
        //        + param_species + " metier:" + metier);

        if (affectation) {
            log.info("*$*$*$* ok affecte capture inferieure a TailleMin au rejet");
            // ATTENTION
            // les captures pour cette metapop ne sont plus du qu'au metier pour
            // qui l'espece est secondaire: elles sont affectees aux rejets

            // pb : ne se fait pas par metier
            // il faut une matrice pour chaques pas de temps qui stocke les
            // rejets par metier, par metapop et par classes d'age (comme pour
            // les captures)
            //log.info("deb de affecterCaptureREJETTailleMin");

            PopulationMonitor popMon = context.getPopulationMonitor();

            int groupDim = 2;
            int zoneDim = 3;

            for (Population pop : param_species.getPopulation()) {
                MatrixND discard = popMon.getCatch(pop).copy();
                discard.setName(MatrixDiscardsPerStrMetPerZonePop.NAME);

                MatrixND eff = popMon.getN(pop);

                // collect les tailles des groupes pour eviter de multiples appels a la methode
                // getLength dans la boucle
                List<PopulationGroup> groups = (List<PopulationGroup>)discard.getSemantic(2);
                double[] length = new double[groups.size()];
                int cpt = 0;
                for (PopulationGroup group : groups) {
                    length[cpt++] = group.getLength();
                }

                // on iter que sur les valeurs != 0, car si deja zero, ca ne
                // sert a rien de le mettre a 0
                for (MatrixIterator i = discard.iteratorNotZero(); i.next();) {
                    int[] pos = i.getCoordinates();
                    int groupIndex = pos[groupDim];
                    if (length[groupIndex] >= param_TailleMin) {
                        i.setValue(0);
                    } else if (param_propSurvie > 0) {
                        Object[] coordonnees = i.getSemanticsCoordinates();
                        PopulationGroup group = (PopulationGroup) coordonnees[groupDim];
                        Zone zone = (Zone)coordonnees[zoneDim];
                        eff.setValue(group, zone,
                                eff.getValue(group, zone)
                                        + i.getValue() * param_propSurvie);
                    }
                }
                popMon.addDiscard(step, pop, discard);
            }

            //log.info("fin de affecterCaptureRejetTailleMin");

            // on a affecte une fois cette meta pop au rejet il ne faut pas le
            // refaire pour ce pas de temps
            affectation = false;
        }
    }
}
