/*
 * Copyright (C) 2014 slehuta
 *
 * 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 org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

//import scripts.ResultName;
import resultinfos.MatrixDiscardsPerStrMetPerZonePop;
import java.io.Writer;

import org.nuiton.math.matrix.*;

import fr.ifremer.isisfish.util.Doc;
import fr.ifremer.isisfish.simulator.SimulationContext;
import fr.ifremer.isisfish.types.TimeStep;
import fr.ifremer.isisfish.entities.*;
import fr.ifremer.isisfish.rule.AbstractRule;
import fr.ifremer.isisfish.datastore.SimulationStorage;
import fr.ifremer.isisfish.datastore.ResultStorage;
import fr.ifremer.isisfish.types.Month;
import fr.ifremer.isisfish.simulator.PopulationMonitor;
import scripts.SiMatrix;
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.io.File;
import java.io.FileReader;
import java.util.HashMap;
import java.util.Map;
import org.nuiton.util.FileUtil;
import org.apache.commons.io.FileUtils;
import resultinfos.MatrixGrossValueOfLandingsPerSpeciesPerStrategyMet;

/**
 *Based on RUM_multinomial.java
 *
 * Created: aout 2017
 *
 * @author sigrid et benjamin
 * @version $Revision: 4.4.1.rc1 $
 *
 * Last update: $Date: $
 * by : $Author: sigrid $
 */

/**
 * utilise a multinomial model pour predire a chaque annee la proportion de la flottille chalutière pratiquant chaque strategie sur la base des %revenus par espece
 */
public class StrategyChoice_multinomial_CeresPEL extends AbstractRule {

    /** to use log facility, just put in your code: log.info("..."); */
    static private Log log = LogFactory.getLog(StrategyChoice_multinomial_CeresPEL.class);
    
	public String param_nomfichier_coeff = "";
	
	// To keep and export %nav / strategy per year
	File exportVarFile = new File ("Var.csv");
    String exportVarString = "";
	File exportPercFile = new File("Perc.csv");
    String exportPercString = "";
    
	//Load matrix of coefficients of rum
	// TO DO [choice x var] nb: ref = exit fishery
	
	protected File fileCoeff;
	protected MatrixND matrixCoeff;
	protected MatrixND matrixVar;
	protected MatrixND matrixPerc;
	
	static final protected String Perc_ANE = "percentLandedValueAnchovy";
	static final protected String Perc_PIL = "percentLandedValueSardine";
	static final protected String Perc_BSS = "percentLandedValueSeaBass";
	static final protected String Perc_ALB = "percentLandedValueTuna";
	static final protected String Close_ANE = "AnchovyClosure";
	
	// interdit de faire des set sur les strategies de la map, il faut recuperer les strategies de la date courante
	protected Map<String , Strategy> mesStrategies;

	
    protected String [] necessaryResult = {
            MatrixGrossValueOfLandingsPerSpeciesPerStrategyMet.NAME,
    };
    
    /**
     * @return the 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() {
        return ("TO DO");
    }

  /**
     * AppelÃ© au dÃ©marrage de la simulation, cette mÃ©thode permet d'initialiser
     * des valeurs
     * @param simulation La simulation pour lequel on utilise cette regle
     */
    public void init(SimulationContext context) throws Exception  {

    // load coefficients of rum file in a matrix

   		fileCoeff = new File(param_nomfichier_coeff);		
		matrixCoeff = MatrixFactory.getInstance().create(fileCoeff);
		
		// reccuperation des metiers et strategies
		SiMatrix siMatrix = SiMatrix.getSiMatrix(context);
		List<Strategy> allStrategies = siMatrix.getStrategies(new TimeStep(0));	
		
		//creation de mes strategies qui ne contiennent que les strategies concernées
		mesStrategies = new HashMap<String, Strategy>();
		for(Strategy str : allStrategies) {
			if(str.getSetOfVessels().getName().equals("Chalutiers")) mesStrategies.put(str.getName(), str);
		}
				
	}// fin de init		
	
	
	
	////////////// Matrix of predictor variables
	protected Map<Integer, MatrixND> allMatrixVar = new HashMap<Integer, MatrixND>();
	
	protected MatrixND getMatrixVar(Integer askedDate) {
		MatrixND result = allMatrixVar.get(askedDate);
		// si on demande on matrice qui n existe pas encore : on la cree
		if (result == null) {
			MatrixND matrixVar = createMatrixVar();
			allMatrixVar.put(askedDate, matrixVar);
			result = matrixVar;
		}
		return result;
	}
	
	protected MatrixND createMatrixVar (){
		// Creation d'une nouvelle matrix qui n'existe pas encore ...
		MatrixND matrixVar = MatrixFactory.getInstance().create(
			"matrixVar",
			new List[]{new ArrayList(mesStrategies.values()), Arrays.asList(new String[]{Perc_ANE,Perc_PIL,Perc_BSS,Perc_ALB,Close_ANE})},
            new String[]{"Strategies","Variables"});
		return matrixVar ;
	}	
	
	/**
	* ajoute a file le contenu de la matrice pour la date donnee to DO
	*/
	protected void exportVar(File file, Integer year) throws Exception {
		
		MatrixND mat = allMatrixVar.get(year);
			
		List <Strategy> str = (List<Strategy>) mat.getSemantics()[0];
		for(Strategy s : str){
			double val0 = mat.getValue(s,0);
			double val1 = mat.getValue(s,1);
			double val2 = mat.getValue(s,2);
			double val3 = mat.getValue(s,3);
			double val4 = mat.getValue(s,4);
			exportVarString += year + ";" + s.getName() + ";" + val0 +";" + val1 + ";" + val2 + ";"+val3+";"+val4+"\n";
		}
       FileUtils.writeStringToFile(file,exportVarString);
	}
  
	
	////////////// Matrix of % of fleet per strategy 
	protected Map<Integer, MatrixND> allMatrixPerc = new HashMap<Integer, MatrixND>();
	protected MatrixND getMatrixPerc(Integer askedDate) {
		MatrixND result = allMatrixPerc.get(askedDate);
		// si on demande on matrice qui n existe pas encore : on la cree
		if (result == null) {
			MatrixND matrixPerc = createMatrixPerc();
			allMatrixPerc.put(askedDate, matrixPerc);
			result = matrixPerc;
		}
		return result;
	}
	
	protected MatrixND createMatrixPerc (){
		// Creation d'une nouvelle matrix qui n'existe pas encore ...
		MatrixND matrixPerc = MatrixFactory.getInstance().create(
			"matrixPerc",
			new List[]{new ArrayList(mesStrategies.values())},
            new String[]{"Strategies"});
		return matrixPerc ;
	}	
	
		
	/**
	* ajoute a file le contenu de la matrice pour la date donnee 
	*/
	protected void exportPerc(File file, Integer year) throws Exception {
		MatrixND mat = allMatrixPerc.get(year);
		List <Strategy> str = mat.getSemantic(0);
		for(Strategy s : str){
				exportPercString += year + ";" + s.getName() + ";" + mat.getValue(s) + "\n";
		}
        FileUtils.writeStringToFile(file,exportPercString);

	}

	
	
	/**
     * La condition qui doit etre vrai pour faire les actions
     * @param simulation La simulation pour lequel on utilise cette regle
     * @return vrai si on souhaite que les actions soit faites
     */
	 
    public boolean condition(SimulationContext context, TimeStep step, Metier metier) throws Exception {
        if(step.getStep() >11){
			return true;
		}else return false;
    }
 
    
	
	
	/**
     * Si la condition est vrai alors cette action est executÃ©e avant le pas
     * de temps de la simulation.
     * @param simulation La simulation pour lequel on utilise cette regle
     */
	
    boolean first = true ;
	public void preAction(SimulationContext context, TimeStep step, Metier metier) throws Exception {
	    List<Strategy> strategies;
		// la prÃ©action n est realisee qu une seule fois pour tous les metiers		
		if (first == true){
			first = false ;
			
			if(step.getMonth().getMonthNumber() == 0){ // compute new % per strategy
			
				SiMatrix siMatrix = SiMatrix.getSiMatrix(context);
				ResultStorage Result = context.getSimulationStorage().getResultStorage();
						
				// 1)  reccupere les values de l annee precedente
			
				MatrixND valueMat;
				// date/str/met/pop : summed over the last year
				for (TimeStep dat = new TimeStep(0); dat.before(step); dat = dat.next()) {
					if( dat.getYear() == step.previous().getYear()) {                    				
						MatrixND mat = Result.getMatrix(dat, MatrixGrossValueOfLandingsPerSpeciesPerStrategyMet.NAME);
						valueMat.add(mat);
					}
				}
				MatrixND valueStrPop = valueMat.getSubMatrix(0, new ArrayList(mesStrategies.values()).toArray()); // only strategies of interest : trawls
				valueStrPop = valueStrPop.sumOverDim(1).reduceDims(1); // sum over metiers
					
				// Perc_ANE,Perc_PIL,Perc_BSS,Perc_ALB,Close_ANE
				List<Strategy> strats = valueStrPop.getSemantic(0);
				List<Species> sps = valueStrPop.getSemantic(1);
				for(Strategy strIndex : strats){
					double totValue = valueStrPop.getSubMatrix(0,strIndex).sumAll();
					for (Species sp : sps){
						if(sp.getName().equals("Anchois")){
							getMatrixVar(step.previous().getYear()).setValue(strIndex,Perc_ANE,valueStrPop.getValue(strIndex,sp)/totValue);
						}else if (sp.getName().equals("Sardine")){
							getMatrixVar(step.previous().getYear()).setValue(strIndex,Perc_PIL,valueStrPop.getValue(strIndex,sp)/totValue);
						}else if (sp.getName().equals("Bar")){
							getMatrixVar(step.previous().getYear()).setValue(strIndex,Perc_BSS,valueStrPop.getValue(strIndex,sp)/totValue);
						}else if (sp.getName().equals("Thon Germon")){
							getMatrixVar(step.previous().getYear()).setValue(strIndex,Perc_ALB,valueStrPop.getValue(strIndex,sp)/totValue);
						}
					}
					getMatrixVar(step.previous().getYear()).setValue(strIndex,Close_ANE,0);
	
				}
				//System.out.print("MatrixVar "+ getMatrixVar(step.previous().getYear()));	

				MatrixND matrixVarPreviousYear = getMatrixVar(step.previous().getYear());
				
				// 3) calcul des pourcentages de nav par strategie 
                strategies = new ArrayList(mesStrategies.values());
				MatrixND probaChange = MatrixFactory.getInstance().create( // strategy x strategy
					new List[]{strategies,strategies}
            	);
				//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
				///////////////////// TO DO /////////////////////////////////
				/////////////////////////////////////////////////////////////
				//  map temporaire de stoquage des valeurs de sum cum calculees pour les met de la strategie.
				for (Strategy str_dep : strategies){
					for(Strategy str_arr : strategies){
						//Perc_ANE,Perc_PIL,Perc_BSS,Perc_ALB,Close_ANE
						double perc =	matrixCoeff.getValue(str_dep,Perc_ANE) * matrixVarPreviousYear.getValue(str_dep,Perc_ANE)
							+ matrixCoeff.getValue(str_dep,Perc_BSS) * matrixVarPreviousYear.getValue(str_dep,Perc_BSS)
							+ matrixCoeff.getValue(str_dep,Perc_PIL) * matrixVarPreviousYear.getValue(str_dep,Perc_PIL)
							+ matrixCoeff.getValue(str_dep,Perc_ALB) * matrixVarPreviousYear.getValue(str_dep,Perc_ALB)
							+ matrixCoeff.getValue(str_dep,Close_ANE) * matrixVarPreviousYear.getValue(str_dep,Close_ANE);
						
						probaChange.setValue(str_dep,str_arr,perc);
					}
				}
					
					// Code pour passer de la matrice de changement au % de nav par strategie
				for(Strategy strCurr : strategies){ // calcul pour chaque str
					int nbVessFlt = strCurr.getSetOfVessels().getNumberOfVessels() ;					
					double newNavs = 0;
					
					for(Strategy strs : strategies){ // on reccupere les proba de changement pour str en cours et vers str curr
						double percLastYear = strs.getProportionSetOfVessels();
						if(step.getYear() != 1) percLastYear = allMatrixPerc.get(step.previous().getYear()).getValue(strs);
						double nbVessLastYear = nbVessFlt * percLastYear;
						double probaCome = probaChange.getValue(strs,strCurr); //if strs == strCurr <=> probaStay
						newNavs += nbVessLastYear * probaCome; 
					}
					
					double newPerc = newNavs/nbVessFlt;
					allMatrixPerc.get(step.getYear()).setValue(strCurr,newPerc);					
				}
					
				exportVar(exportVarFile, step.getYear());
				exportPerc(exportPercFile, step.getYear());
					
			} // %already computed			
					
			for(Strategy strIndex : strategies){		
				double prop = allMatrixPerc.get(step.getYear()).getValue(strIndex);		
				strIndex.setProportionSetOfVessels(prop);
			}
			
			
		}
	}
	
 
    /**
     * Si la condition est vrai alors cette action est executÃ©e apres le pas
     * de temps de la simulation.
     * @param simulation La simulation pour lequel on utilise cette regle
     */
    public void postAction(SimulationContext context, TimeStep step, Metier metier) throws Exception {
    first = true ;   
      
    }

}
