/*
 * #%L
 * IsisFish data
 * %%
 * Copyright (C) 2006 - 2015 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 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 java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.nuiton.math.matrix.*;

import fr.ifremer.isisfish.simulator.MetierMonitor;
import fr.ifremer.isisfish.simulator.SimulationContext;
import fr.ifremer.isisfish.types.TimeStep;
import fr.ifremer.isisfish.types.Month;
import fr.ifremer.isisfish.entities.*;
import fr.ifremer.isisfish.rule.AbstractRule;
import fr.ifremer.isisfish.datastore.RegionStorage;

import fr.ifremer.isisfish.annotations.Doc;
import resultinfos.MatrixNoActivity;

/**
 * InterdictionEnginPreSimu.
 *
 * Created: 30 novembre 2006
 *
 * @author anonymous &lt;anonymous@labs.libre-entreprise.org&gt;
 * @version $Revision: 1.1 $
 *
 * Last update: $Date: 2007-01-24 18:25:34 $
 * by : $Author: bpoussin $
 */
public class InterdictionEnginPreSimu extends AbstractRule {

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

    @Doc(value="do the doc of param gear")
    public Gear param_gear = null;
    @Doc(value="do the doc of param beginMonth")
    public Month param_beginMonth = Month.JANUARY;
    @Doc(value="do the doc of param endMonth")
    public Month param_endMonth = Month.DECEMBER;

    protected Map<Month, MatrixND> tableNonActivite = new HashMap<>();
    protected boolean affectNonActivite = false;

    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 "Prohibited gear";
    }
 
    /**
     * Appelé au démarrage de la simulation, cette méthode permet d'initialiser
     * des valeurs
     * @param context La simulation pour lequel on utilise cette regle
     */
    @Override
    public void init(SimulationContext context) throws Exception {
        MetierMonitor metierMon = context.getMetierMonitor();
        
        //Month moisDeb = param_beginMonth;
        //Month moisFin = param_endMonth;
        Gear EnginInterdit = param_gear;

        log.info("nom de l'engin interdit "+EnginInterdit.getName());
        FisheryRegion RegionSimu = RegionStorage.getFisheryRegion(context.getDB());
        List<Metier> ListeMetiers = RegionSimu.getMetier();

        for (Metier metier : ListeMetiers) {
            if (metier.getGear().equals(EnginInterdit)) {
                //récupère toutes les stratégies pratiquant le métier et pour lesquelles la proportion !=0
                List<Strategy> ListeStrat=RegionSimu.getStrategy();
                for (Strategy Strat : ListeStrat){
                    SetOfVessels vessels = Strat.getSetOfVessels();
                    if (vessels.getPossibleMetiers(metier) != null){
                        for (Month mois : Month.getMonths(param_beginMonth, param_endMonth)) {
                            StrategyMonthInfo StratMonthInfo = Strat.getStrategyMonthInfo(mois);                            
                            metierMon.addforbiddenMetier(metier, mois);
                            
                            //1er cas: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
                            List<EffortDescription> MetiersPossibles =
                                    new ArrayList<>(Strat.getSetOfVessels().getPossibleMetiers());

                            // on verifie que les metiers sont bien pratiques au mois courant,
                            // qu'ils n'ont pas le meme ont le meme engin et qu'ils ne sont 
                            // pas interdits par ailleurs
                            for (Iterator<EffortDescription> effort=MetiersPossibles.iterator(); effort.hasNext();) {
                                Metier met = effort.next().getPossibleMetiers();
                                if (StratMonthInfo.getProportionMetier(met) == 0 
                                        || met.getGear().equals(metier.getGear())
                                        || met.getName().equalsIgnoreCase("nonActivite") 
                                        || metierMon.isForbidden(metier, mois)){
                                    effort.remove();
                                }
                            }   
          
                            //on repartit maintenant l'effort entre les differents metiers possibles dans la meme strategie si un metier possible existe bien
                            if (MetiersPossibles.size() != 0){
                                int NbMetier=MetiersPossibles.size();
                                for (EffortDescription effort : MetiersPossibles) {
                                    Metier met = effort.getPossibleMetiers();
                                    double NouvelleProportion = 
                                        StratMonthInfo.getProportionMetier(met)
                                        + (StratMonthInfo.getProportionMetier(metier) / NbMetier);
                                    StratMonthInfo.setProportionMetier(met, NouvelleProportion);
                                }
                                StratMonthInfo.setProportionMetier(metier, 0); //le metier vise a alors une proportion nulle
                            }
                            else{
                                log.info("debut reportNonActivite");
                                // on regarde si on a déjà une entrée pour le mois courant
                                MatrixND matNonActiviteMois=tableNonActivite.get(mois);
                                if (matNonActiviteMois == null){
                                    matNonActiviteMois = MatrixFactory.getInstance().create(
                                            MatrixNoActivity.NAME,
                                            new List[]{ListeStrat, ListeMetiers},
                                            new String[]{"Strategies", "Metiers"});
                                    tableNonActivite.put(mois, matNonActiviteMois);
                                }

                                matNonActiviteMois.setValue(Strat, metier, StratMonthInfo.getProportionMetier(metier));
                                log.info("fin reportNonActivite");

                                StratMonthInfo.setProportionMetier(metier, 0);
                            }
                        }
                    }
                }
            }
        }

        // fin methode
    }

    /**
     * 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 {
        boolean result = false;
        MatrixND mat = tableNonActivite.get(step.getMonth());
        if (mat != null && !affectNonActivite) {
            result = true;
        }
        return result;
        // fin
    }
 
    /**
     * 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 {
        // on ne doit le faire qu'une seul fois quelque soit le nombre de metier
        affectNonActivite = true;
        MatrixND mat = tableNonActivite.get(step.getMonth());
        
        MetierMonitor metierMon = context.getMetierMonitor();
        MatrixND noActivity = metierMon.getNoActivity(step);

        if (noActivity == null) {
            metierMon.setNoActivity(step, mat.copy());
        } else {
            noActivity.add(mat);
        }
        // fin
    }
 
    /**
     * 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 {
        affectNonActivite = false;
    }

}
