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

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import scripts.SiMatrix;
import fr.ifremer.isisfish.datastore.RegionStorage;
import fr.ifremer.isisfish.entities.EffortDescription;
import fr.ifremer.isisfish.entities.EffortDescriptionDAO;
import fr.ifremer.isisfish.entities.FisheryRegion;
import fr.ifremer.isisfish.entities.Gear;
import fr.ifremer.isisfish.entities.Metier;
import fr.ifremer.isisfish.entities.MetierDAO;
import fr.ifremer.isisfish.entities.MetierSeasonInfo;
import fr.ifremer.isisfish.entities.MetierSeasonInfoDAO;
import fr.ifremer.isisfish.entities.SetOfVessels;
import fr.ifremer.isisfish.entities.Strategy;
import fr.ifremer.isisfish.entities.StrategyMonthInfo;
import fr.ifremer.isisfish.entities.Zone;
import fr.ifremer.isisfish.rule.AbstractRule;
import fr.ifremer.isisfish.simulator.SimulationContext;
import fr.ifremer.isisfish.types.TimeStep;
import fr.ifremer.isisfish.types.Month;
import fr.ifremer.isisfish.annotations.Doc;

/**
 * ChangementParamControlablePresimu.java
 *
 * 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 ChangementParamControlablePresimu extends AbstractRule {

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

    @Doc(value="Zone de fermeture")
    public Zone param_zone = null;
    @Doc(value="Engin concernés")
    public Gear param_gear = null;
    @Doc(value="Begin month")
    public Month param_beginMonth = Month.JANUARY;
    @Doc(value="End month")
    public Month param_endMonth = Month.DECEMBER;
    @Doc(value="New param controlable value")
    public String param_newParamValue = "80";

    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 "Change gear parameter value for all metier with this 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 {
        SiMatrix siMatrix = SiMatrix.getSiMatrix(context);
        
        Zone ZoneFermee = param_zone; 
        Month MoisDeb = param_beginMonth;
        Month MoisFin= param_endMonth;
        String nouvelleValeurParam = param_newParamValue;
        Gear EnginInterdit = param_gear;
        
        log.info("nom de l'engin interdit " + EnginInterdit.getName());
        
        FisheryRegion RegionSimu = RegionStorage.getFisheryRegion(context.getDB());
        List<Metier> ListeMetiers = RegionSimu.getMetier();
        List<Month> SaisonFermee = Month.getMonths(MoisDeb, MoisFin);

        for (Metier metier : ListeMetiers) {
            log.info("metier examiné " + metier.getName() + " son engin " + metier.getGear().getName());
            if (!metier.getGear().equals(EnginInterdit)){
                log.info("l engin du métier n est pas concerné");
            } else {
                log.info("l engin du métier est bien concerné");
                if (ZoneFermee == null && Month.JANUARY.equals(MoisDeb) && Month.DECEMBER.equals(MoisFin)){
                    //aucun critère spatio-temporel il suffit donc de mettre
                    //le param controlable à la nouvelle valeur
                    metier.setGearParameterValue(nouvelleValeurParam);
                    log.info("aucune spatialisation ni saisonnalité, on passe le Param Controlable a "+nouvelleValeurParam);
                } else {   
                    List<MetierSeasonInfo> ListeSaisons = metier.getMetierSeasonInfo();
                    for (MetierSeasonInfo saisonmetier : ListeSaisons) {
                        List<Month> saison = saisonmetier.getMonths();
                        int taille=0;
                        if (ZoneFermee != null){
                            taille = siMatrix.nbCellInter(saisonmetier.getZone(), ZoneFermee);
                        } else {
                            log.info ("pas de sélection de zone, toute la zone d'étude est donc concernée");
                        }
                        if (ZoneFermee == null || taille!=0){
                            if (saison.containsAll(SaisonFermee)){
                                log.info ("il y a intersection de la zone metier au cours de la saison" + saison);
                                List<Month> moisconcerne = new ArrayList<>(saison);
                                moisconcerne.retainAll(SaisonFermee);
                                
                                MetierDAO metierDao = context.getMetierDAO();
                                MetierSeasonInfoDAO infoDao = context.getMetierSeasonInfoDAO();
                                EffortDescriptionDAO effortDao = context.getEffortDescriptionDAO();
                                
                                Metier NouveauMetier = metierDao.create();
                                NouveauMetier.setName(metier.getName()+" Mois "+moisconcerne.get(0).getMonthNumber()+" a "+moisconcerne.get(moisconcerne.size()-1).getMonthNumber()+" ValeurParam "+nouvelleValeurParam);
                                NouveauMetier.setGear(EnginInterdit);
                                NouveauMetier.setGearParameterValue(nouvelleValeurParam);
                                NouveauMetier.setComment("métier créé pendant la simu pour le changement de paramètre contrôlable");
                                
                                for (MetierSeasonInfo infsais : metier.getMetierSeasonInfo()){
                                    List<Month> sais = infsais.getMonths();
                                    MetierSeasonInfo NouveauInfoSaison = infoDao.create();
                                    NouveauInfoSaison.setMetier(NouveauMetier);
                                    NouveauInfoSaison.setMonths(sais);
                                    // EC : new ArrayList<Zone>() fix error "Found shared references to a collection"
                                    NouveauInfoSaison.setZone(new ArrayList<>(infsais.getZone()));
                                    NouveauInfoSaison.setComment("créé durant la simulation");
                                    NouveauMetier.addMetierSeasonInfo(NouveauInfoSaison);
                                }
                                
                                //On ajoute aux strategies qui pratiquent le métier le nouveau métier
                                List<Strategy> listestrategies = RegionSimu.getStrategy();
                                for(Strategy strategie : listestrategies){
                                    SetOfVessels SetOfBateau = strategie.getSetOfVessels();
                                    EffortDescription AncienEffort = SetOfBateau.getPossibleMetiers(metier);
                                    if (AncienEffort != null) {
                                        EffortDescription DescriptionEffort = effortDao.create();
                                        DescriptionEffort.setPossibleMetiers(NouveauMetier);
                                        DescriptionEffort.setSetOfVessels(SetOfBateau);
                                        DescriptionEffort.setFishingOperation(AncienEffort.getFishingOperation());
                                        DescriptionEffort.setFishingOperationDuration(AncienEffort.getFishingOperationDuration());
                                        DescriptionEffort.setGearsNumberPerOperation(AncienEffort.getGearsNumberPerOperation());
                                        DescriptionEffort.setCrewSize(AncienEffort.getCrewSize());
                                        DescriptionEffort.setUnitCostOfFishing(AncienEffort.getUnitCostOfFishing());
                                        DescriptionEffort.setFixedCrewSalary(AncienEffort.getFixedCrewSalary());
                                        DescriptionEffort.setCrewShareRate(AncienEffort.getCrewShareRate());
                                        DescriptionEffort.setCrewFoodCost(AncienEffort.getCrewFoodCost());
                                        DescriptionEffort.setRepairAndMaintenanceGearCost(AncienEffort.getRepairAndMaintenanceGearCost());
                                        DescriptionEffort.setLandingCosts(AncienEffort.getLandingCosts());
                                        DescriptionEffort.setOtherRunningCost(AncienEffort.getOtherRunningCost());
                                        
                                        SetOfBateau.addPossibleMetiers(DescriptionEffort);
                                        
                                        // on a ajouté le nouveau métier au set of vessels,
                                        // maintenant, on va reporté l'effort du métier ancien
                                        // vers le nouveau pour les mois concernés
                                        for (Month mois : Month.MONTH){
                                            StrategyMonthInfo PropStratMois = strategie.getStrategyMonthInfo(mois);
                                            double prop = PropStratMois .getProportionMetier(metier);
                                            if (moisconcerne.contains(mois) && prop != 0){
                                                //on créé un nouveau PropStrMet
                                                log.info ("la stratégie "+strategie.getName()+" pratique bien le metier "+metier.getName()+" au mois " + mois);
                                                PropStratMois.setProportionMetier(NouveauMetier, prop); 
                                                PropStratMois.setProportionMetier(metier, 0);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        // fin de la 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 {
        return false;
    }
 
    /**
     * 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 {
        // nothing
    }
 
    /**
     * 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 {
        // nothing
    }

}
