/*
 * Copyright (C) 2017 avigier
 *
 * 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>.
 */
package rules;

import static org.nuiton.i18n.I18n.*;

import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;
import java.util.ListIterator;
import java.util.HashSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.io.File;
import java.io.FilenameFilter;
import java.io.FileReader;

import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuiton.math.matrix.MatrixIterator;
import org.nuiton.math.matrix.MatrixFactory;
import org.nuiton.math.matrix.MatrixND;
import org.nuiton.util.FileUtil;
import org.nuiton.topia.TopiaContext;

//import javax.constraints.*;

import scripts.RuleUtil;
import scripts.SiMatrix;
import fr.ifremer.isisfish.datastore.ResultStorage;
import fr.ifremer.isisfish.entities.*;
import fr.ifremer.isisfish.entities.EffortDescription;
import fr.ifremer.isisfish.entities.Equation;
import fr.ifremer.isisfish.entities.Gear;
import fr.ifremer.isisfish.entities.Metier;
import fr.ifremer.isisfish.entities.Population;
import fr.ifremer.isisfish.entities.PopulationGroup;
import fr.ifremer.isisfish.entities.Selectivity;
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.entities.Zone;
import fr.ifremer.isisfish.rule.AbstractRule;
import fr.ifremer.isisfish.simulator.MetierMonitor;
import fr.ifremer.isisfish.simulator.PopulationMonitor;
import fr.ifremer.isisfish.simulator.ResultManager;
import fr.ifremer.isisfish.simulator.SimulationContext;
import fr.ifremer.isisfish.types.TimeStep;
import fr.ifremer.isisfish.types.Month;
import fr.ifremer.isisfish.util.Doc;


/**
 * NonBayOfBiscayCatch.java
 */
public class removeCatchOther extends AbstractRule {

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

        
    @Doc("starting year compared to 2008")
    public int param_startYear = 0; //January 2010

    // File of catch per zone in number between 2008 and 2014 for all non French fleets
    @Doc("File of catch other")
    public String param_file = "InputMixChannel/HistoricStrategies_2008-2014/CatchOTHZones.csv";
	
	@Doc("Otional multiplication factor for the Other catch")
    public double param_multCatch = 1;
	
	protected File  fileCatchOth;
    public MatrixND matrixCatchOth;
    boolean affectation;

    
    
    protected String[] necessaryResult = {
    };

    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
     */
    public String getDescription() throws Exception {
        return "Remove catch by fleets not explicitely modeled : historical on 2008-2014"+ 
		"averaged from year 7 on";
    }

    /**
     * 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
     */
    public void init(SimulationContext context) throws Exception {
        fileCatchOth = new File(param_file);
            matrixCatchOth = MatrixFactory.getInstance().create(fileCatchOth);
    }

    /**
     * La condition qui doit etre vraie 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
     */
    public boolean condition(SimulationContext context, TimeStep step, Metier metier) throws Exception {
        boolean result = true;
        
        return true;
    }

    /**
     * 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é
     */
    public void preAction(SimulationContext context, TimeStep step, Metier metier) throws Exception {
        // Nothing happens here.
        affectation = false; // Flag to avoid to loop on métiers with preAction and postAction when unnecessary
    }

    /**
     * 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é
     */
    public void postAction(SimulationContext context, TimeStep step, Metier metier) throws Exception {
        //System.out.println("Affectation " + affectation);
        //Do not loop on metiers.
        
        if(!affectation){
            
            PopulationMonitor popMon = context.getPopulationMonitor();
			List<Population> listPop = context.getSimulationStorage().getParameter().getPopulations();
            
			// Create % of pop in the zone for multi-pop species
			Map<Species , MatrixND> abSp = new HashMap<Species , MatrixND>();
			Map<Species, List<Population>> spPop = new HashMap<Species , List<Population>>();
            Map<Population , MatrixND> abPop = new HashMap<Population , MatrixND>();
           
			for(Population pop : listPop){
				MatrixND abondance = popMon.getN(pop).copy();
               abPop.put(pop,abondance);
               spPop.get(pop.getSpecies()).add(pop);
				    /*if(abSp.get(pop.getSpecies()) == null){ 
                        List<Integer> listGroupId = new ArrayList<Integer>();
                        List<PopulationGroup> listGroup = (List<PopulationGroup>)abondance.getSemantic(0);
                        for(PopulationGroup grp : listGroup){
                            listGroupId.add(grp.getId());
                        }
                        abondance.setSemantic(0,listGroupId);
						abSp.put(pop.getSpecies(), abondance);
                      
					}else {
                        MatrixND tmp = abSp.get(pop.getSpecies()).add(abondance);
                        abSp.put(pop.getSpecies(), tmp);
					}*/	
			}
			
			for(Population pop : listPop){
				MatrixND abondance = popMon.getN(pop);
				for(MatrixIterator i = abondance.iterator(); i.hasNext();) {
                    i.next();
					Object[] sems = i.getSemanticsCoordinates();
                    PopulationGroup group = (PopulationGroup) sems[0];
					Zone zone = (Zone) sems[1];
					int year = step.getYear()+param_startYear;
					if(year>6) year = 7;
					double catTemp = 0;
					if(i.getValue() != 0 & matrixCatchOth.getSemantic(1).contains(zone)){
                        TimeStep curr = new TimeStep(step.getStep()+year*12);
					    if(matrixCatchOth.getSubMatrix(0,curr).getSubMatrix(1,zone).getSubMatrix(2,group.getId()) != null){
					        catTemp = matrixCatchOth.getValue(curr,zone,group.getId())*param_multCatch;
                         double absp = i.getValue();
					        for(Population othpop : spPop.get(pop.getSpecies())){
                                if(!pop.getName().equals(othpop.getName()) & abPop.get(othpop).getSemantic(1).contains(zone)){
                                    absp += abPop.get(othpop).getSubMatrix(0,group.getId(),1).reduceDims(0).getValue(zone);
                                }
					        }
					        
					        double propPop = i.getValue() / absp;
					        if(catTemp*propPop < 0.9*i.getValue()){
						        double newAb = i.getValue()-(catTemp*propPop);
						        i.setValue(newAb);
					        }else i.setValue(i.getValue()*0.1);
					    }
					}
				}
			}
		}
   
            affectation = true;
       }
        //End of postAction
}


