/*
 * #%L
 * IsisFish data
 * %%
 * Copyright (C) 2006 - 2016 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 scripts.SiMatrix;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

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.annotations.Doc;
import resultinfos.MatrixNoActivity;

/**
 * Cantonnement.java
 *
 * Remplace aussi Cantonnement Engin
 * 
 * Created: 30 novembre 2006
 *
 * @author anonymous &lt;anonymous@labs.libre-entreprise.org&gt;
 * @version $Revision: 1.5 $
 *
 * Last update: $Date: 2007-11-02 17:41:41 $
 * by : $Author: bpoussin $
 */
public class Cantonnement extends AbstractRule {

    /** to use log facility, just put in your code: log.info("..."); */
    static private Log log = LogFactory.getLog(Cantonnement.class);
    
    @Doc(value="Zone de fermeture")
    public Zone param_zone = null;
    @Doc(value="Engin concernés")
    public Gear param_gear = null;
    @Doc(value="Begin step")
    public TimeStep param_beginStep = new TimeStep(0);
    @Doc(value="End step")
    public TimeStep param_endStep = new TimeStep(119);
    @Doc(value="Begin month")
    public Month param_beginMonth = Month.JANUARY;
    @Doc(value="End month")
    public Month param_endMonth = Month.DECEMBER;

    protected String [] necessaryResult = {
        // put here all necessary result for this rule
        // example: 
        // MatrixBiomass.NAME,
        // MatrixNetValueOfLandingsPerStrategyMet.NAME
        MatrixNoActivity.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 {
        // fermeture saisiniere de zone qui peut être réduite à un engin
        // et a certaines années de la simulation
        return "Cantonnement: can be used to Cantonnement with gear if you put gear in parameter";
    }
 
    /**
     * 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 {

    }

    /**
     * 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 {        
        if (step.before(param_beginStep)) {
            return false;
        } else if (step.after(param_endStep)) {
            return false;
        }

        if (step.getMonth().before(param_beginMonth)) {
            return false;
        } else if (step.getMonth().after(param_endMonth)) {
            return false;
        }
        if (param_gear != null && !param_gear.equals(metier.getGear())) { 
            return false;
        }

        
        List<Cell> cellMetiers = metier.getMetierSeasonInfo(step.getMonth()).getCells();
        List<Cell> cells = param_zone.getCell();
        
        cellMetiers.retainAll(cells);
        
        boolean result = !Collections.disjoint(cellMetiers, cells);
        if (result && log.isInfoEnabled()) {
            log.info("Cantonnement condition 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 {
        MetierMonitor metierMon = context.getMetierMonitor();
        
        MetierSeasonInfo infoMetier = metier.getMetierSeasonInfo(step.getMonth());
        
        List<Cell> cellMetiers = infoMetier.getCells();
        List<Cell> cells = param_zone.getCell();
        
        cellMetiers.removeAll(cells);
        
        if (cellMetiers.size() != 0) {
            ZoneDAO dao = context.getZoneDAO();
            
            //
            // Create new empty zone for cantonnement
            //
            String name = "ZoneCantonnement-" + metier.getName()+ "-" + step.getStep();
            Zone zoneCantonnement = dao.findByName(name);
            int cpt=0;
            while (zoneCantonnement != null) {
                cpt++;
                zoneCantonnement = dao.findByName(name + "-" + cpt);
            }
            zoneCantonnement = dao.create();
            if (cpt > 0) {
                name += "-" + cpt;
            }
            zoneCantonnement.setName(name);
            
            //
            // Remove prohibited zone and add not prohibited cell to zoneCantonnement
            //            
            Collection<Zone> zoneMetiers = infoMetier.getZone();
            for (Iterator<Zone> i = zoneMetiers.iterator(); i.hasNext();) {
                Zone zone = i.next();
                // copy list cell to not modify original zone
                List<Cell> tmpCells = new ArrayList<>(zone.getCell());
                tmpCells.removeAll(param_zone.getCell());
                
                zoneCantonnement.addAllCell(tmpCells);
                i.remove();
            }
            List<Zone> newZone = new ArrayList<>();
            newZone.add(zoneCantonnement);
            infoMetier.setZone(newZone);
        } else {
            // sinon toute la zone de pratique du metier est incluse dans zone Cantonnement
            // alors metier devient metier-nonactivite

            SiMatrix siMatrix = SiMatrix.getSiMatrix(context);
            MatrixND noActivity = metierMon.getOrCreateNoActivity(step,
                    MatrixNoActivity.NAME,
                    siMatrix.getStrategies(step),
                    siMatrix.getMetiers(step));                            
            metierMon.addforbiddenMetier(metier);
            List<Strategy> strategies = siMatrix.getStrategies(step);

            for (Strategy str : strategies) {
               StrategyMonthInfo info = str.getStrategyMonthInfo(step.getMonth());
               double prop = info.getProportionMetier(metier); 
               if (prop != 0) {
                   noActivity.setValue(str , metier, prop);
                   info.setProportionMetier(metier, 0);
               }
            }            
        }
        // fin de preAction
    }
 
    /**
     * 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
    }

}
