/*
 * 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 simulationplans;
import fr.ifremer.isisfish.*;
import fr.ifremer.isisfish.datastore.ResultStorage;
import fr.ifremer.isisfish.datastore.RuleStorage;
import fr.ifremer.isisfish.datastore.SimulationStorage;
import fr.ifremer.isisfish.entities.*;
import fr.ifremer.isisfish.rule.Rule;
import fr.ifremer.isisfish.rule.RuleHelper;
import fr.ifremer.isisfish.simulator.SimulationContext;
import fr.ifremer.isisfish.simulator.SimulationParameter;
import fr.ifremer.isisfish.simulator.SimulationPlanIndependent;
import fr.ifremer.isisfish.simulator.SimulationPlanContext;
import fr.ifremer.isisfish.types.TimeStep;
import fr.ifremer.isisfish.util.Doc;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuiton.math.matrix.*;
import org.nuiton.topia.*;
import org.nuiton.util.*;
//import org.apache.commons.lang3.StringUtils.splitByWholeSeparator;


/**
 * PlanGalion.java
 *
 * Created: 30 Novembre 2018
 *
 * @author slehuta <user.name@vcs.hostName>
 * @version $Revision: 1 $
 * Last update: $Date: 30 novembre 2018 $
 * by : $Author: slehuta $
 */
public class PlanGalion implements SimulationPlanIndependent {

    /** to use log facility, just put in your code: log.info("..."); */
    private static Log log = LogFactory.getLog(PlanGalion.class);
    
    static private String MATRIX = "CombinaisonSimulation";
    private MatrixND matrix = null;

	// Matrix columns
    static private final String EFFECT = "EffectifsInitiaux";
	static private final String RECRUT = "Recrutement";
	static private final String CONNEC = "Connectivite";
	static private final String RULE = "Rule";
	
    
    static private final int parameterNumber = 4;
    static private final int matrix_size = 216; 
    public int param_first = 0;
    public int param_simulationNumber = 216;
    public String param_directory = "F:/CDD_Ifremer_Nantes/Galion/MathieuGenu/Data/CombinaisonSimulation/";

	private MatrixND mateffInit0 = null;
    private MatrixND mateffInit1 = null;
    private MatrixND mateffInit2 = null;
	private MatrixND mateffInit3 = null;
	// TO DO enter correct matrix path and names
	static private final String effInit0 = "F:/CDD_Ifremer_Nantes/Galion/MathieuGenu/Data/Matrice_Observation_ISIS/AbondanceRecalculee/abondInit.csv";
	static private final String effInit1 = "F:/CDD_Ifremer_Nantes/Galion/MathieuGenu/Data/Matrice_Observation_ISIS/AbondanceRecalculee/abond_z1.csv";
	static private final String effInit2 = "F:/CDD_Ifremer_Nantes/Galion/MathieuGenu/Data/Matrice_Observation_ISIS/AbondanceRecalculee/abond_z0.5.csv";
	static private final String effInit3 = "F:/CDD_Ifremer_Nantes/Galion/MathieuGenu/Data/Matrice_Observation_ISIS/AbondanceRecalculee/abond_z2.csv";
    
    protected String[] necessaryResult = {

    };

    @Override
    public String[] getNecessaryResult() {
        return this.necessaryResult;
    }

    /**
     * Permet d'afficher a l'utilisateur une aide sur le plan.
     * @return L''aide ou la description du plan
     */
    @Override
    public String getDescription() throws Exception {
        return "Galion management scenarios and main uncertainty hypotheses for GDL Hake";
    }

    /**
     * Called once before {@code beforeSimulation} call.
     * 
     * @param context plan context
     */
    @Override
    public void init(SimulationPlanContext context) throws Exception {
        System.out.println("etape0");
		// Load simulation plan matrix
        matrix = MatrixFactory.getInstance().create(new int[]{matrix_size, parameterNumber});
        matrix.importCSV(new FileReader(new File(param_directory, MATRIX + ".csv")), new int[]{0,0});
        List<Integer> dim0 = new ArrayList<Integer>();
        for (int i=0; i < matrix_size; i++) {
            dim0.add(i);
        }
        System.out.println("etape1 : lecture plan de combinaison");
        matrix.setSemantic(0, dim0);
        matrix.setSemantic(1, Arrays.asList(new String[]{EFFECT,RECRUT,CONNEC,RULE}));
		
		// To DO Add necessary rules to simulation context
		context.getParam().addExtraRules("Effort_2015_2017_GDL");

		// Load initial abundance matrices
		mateffInit0 = MatrixFactory.getInstance().create(new int[]{6,2});
        mateffInit0.importCSV(new FileReader(new File(effInit0)), new int[]{0,0});
        mateffInit1 = MatrixFactory.getInstance().create(new int[]{6,2});
        mateffInit1.importCSV(new FileReader(new File(effInit1)), new int[]{0,0});
        mateffInit2 = MatrixFactory.getInstance().create(new int[]{6,2});
        mateffInit2.importCSV(new FileReader(new File(effInit2)), new int[]{0,0});
        mateffInit3 = MatrixFactory.getInstance().create(new int[]{6,2});
        mateffInit3.importCSV(new FileReader(new File(effInit3)), new int[]{0,0});		
		
		System.out.println("etape2 : lecture plan des abondances initiales");
    }
    /**
     * @param name le nom de l'element a recuperer
     * @param simulation le numero de la simulation
     * @return
     */
    private double getDouble(String name,String colname, int simulation) throws Exception {
        File dir = new File(param_directory);
        Properties prop = new Properties();
        prop.load(new BufferedReader(new FileReader(new File(dir, name + ".txt"))));
        int ligne = simulation + param_first;
		int mod = (int)matrix.getValue(ligne, colname);
		System.out.println("nom "+name+"mod :"+mod);
        double result = Double.parseDouble(prop.getProperty(""+mod));
        
        return result;
    }


    /**
     * Call before each simulation.
     * 
     * @param context plan context
     * @param nextSimulation storage used for next simulation
     * @return true if we must do next simulation, false to stop plan
     * @throws Exception
     */
    @Override
    public boolean beforeSimulation(SimulationPlanContext context,
        SimulationStorage nextSimulation) throws Exception {
        
		int simNum = nextSimulation.getParameter().getSimulationPlanNumber() + param_first;
        System.out.println("etape3 : determiner getDouble valeur simNum : " + simNum);

        TopiaContext db = nextSimulation.getStorage().beginTransaction();
        String populationId = nextSimulation.getParameter().getPopulations().get(0).getTopiaId();
        PopulationDAO populationDAO = IsisFishDAOHelper.getPopulationDAO(db);
        Population pop = populationDAO.findByTopiaId(populationId);
                
        if (simNum < param_simulationNumber+param_first){
			
            
            //////////////////////////////////////////////////////////////////////////////////////////

			// Modif rules
			List<Rule> paramRules = nextSimulation.getParameter().getRules();

            double eff = matrix.getValue(simNum,EFFECT);
			double rec = matrix.getValue(simNum,RECRUT);
            double con = matrix.getValue(simNum,CONNEC);
			double rul = matrix.getValue(simNum,RULE);
            
            int ruleNum = 0;
            System.out.println("etape4 , " + eff + rec + con + rul);
				
				// Param��tres des r��gles �� modifier pour chaque sc��nario 
			if(rul == 1){
                String centmetre = "fr.ifremer.isisfish.entities.Zone\\#1539588412072\\#0.030127532086065556\\:100m";
                String FRA = "fr.ifremer.isisfish.entities.Zone\\#1479284290034\\#0.8150591457075589\\:FRA";
                //String OTB = "fr.ifremer.isisfish.entities.Gear\\#1519986516248\\#0.4012180021578676\\:OTB";
                Gear OTB = IsisFishDAOHelper.getGearDAO(db).findByName("OTB");
                String OTM = "fr.ifremer.isisfish.entities.Gear\\#1532079299323\\#0.29585681262999797\\:OTM";
                String OTT = "fr.ifremer.isisfish.entities.Gear\\#1532079016748\\#0.9759825947893644\\:OTT";
                
                // OTB
                String ruleName1 = "Cantonnement"; 
                Properties propert1 = new Properties();
                propert1.put("rule."+ruleNum+".parameter.zone", "" +centmetre);
                propert1.put("rule."+ruleNum+".parameter.gear", ""+OTB);
                propert1.put("rule."+ruleNum+".parameter.beginStep", ""+0);
                propert1.put("rule."+ruleNum+".parameter.endStep", ""+119);
                propert1.put("rule."+ruleNum+".parameter.beginMonth", ""+4);
                propert1.put("rule."+ruleNum+".parameter.endMonth", ""+6);
                
                RuleStorage ruleStorage1 = RuleStorage.getRule(ruleName1);
                Rule rule1 = ruleStorage1.getNewInstance();
                RuleHelper.populateRule(ruleNum, nextSimulation.getStorage(), rule1, propert1); 
                paramRules.add(rule1);
                   
                ruleNum+=1;

                // OTM
                String ruleName2 = "Cantonnement"; 
                Properties propert2 = new Properties();
                propert2.put("rule."+ruleNum+".parameter.zone","" +centmetre);
                propert2.put("rule."+ruleNum+".parameter.gear", ""+OTM);
                propert2.put("rule."+ruleNum+".parameter.beginStep", ""+0);
                propert2.put("rule."+ruleNum+".parameter.endStep", ""+119);
                propert2.put("rule."+ruleNum+".parameter.beginMonth", ""+4);
                propert2.put("rule."+ruleNum+".parameter.endMonth", ""+6);
                
                RuleStorage ruleStorage2 = RuleStorage.getRule(ruleName2);
                Rule rule2 = ruleStorage2.getNewInstance();
                RuleHelper.populateRule(ruleNum, nextSimulation.getStorage(), rule2, propert2); 
                paramRules.add(rule2);
                   
                ruleNum+=1;

                // OTT
                String ruleName3 = "Cantonnement"; 
                Properties propert3 = new Properties();
                propert3.put("rule."+ruleNum+".parameter.zone", ""+centmetre);
                propert3.put("rule."+ruleNum+".parameter.gear", ""+OTT);
                propert3.put("rule."+ruleNum+".parameter.beginStep", ""+0);
                propert3.put("rule."+ruleNum+".parameter.endStep", ""+119);
                propert3.put("rule."+ruleNum+".parameter.beginMonth", ""+4);
                propert3.put("rule."+ruleNum+".parameter.endMonth", ""+6);
                
                RuleStorage ruleStorage3 = RuleStorage.getRule(ruleName3);
                Rule rule3 = ruleStorage3.getNewInstance();
                RuleHelper.populateRule(ruleNum, nextSimulation.getStorage(), rule3, propert3); 
                paramRules.add(rule3);
                   
                System.out.println("TOTO 1");

			}else if(rul == 2){		
                String centmetre = "fr.ifremer.isisfish.entities.Zone\\#1539588412072\\#0.030127532086065556\\:100m";
                String FRA = "fr.ifremer.isisfish.entities.Zone\\#1479284290034\\#0.8150591457075589\\:FRA";
                String OTB = "fr.ifremer.isisfish.entities.Gear\\#1519986516248\\#0.4012180021578676\\:OTB";
                String OTM = "fr.ifremer.isisfish.entities.Gear\\#1532079299323\\#0.29585681262999797\\:OTM";
                String OTT = "fr.ifremer.isisfish.entities.Gear\\#1532079016748\\#0.9759825947893644\\:OTT";
			    // OTB
                String ruleName1 = "Cantonnement"; 
                Properties propert1 = new Properties();
                propert1.put("rule."+ruleNum+".parameter.zone", FRA);
                propert1.put("rule."+ruleNum+".parameter.gear", OTB);
                propert1.put("rule."+ruleNum+".parameter.beginStep", ""+24);
                propert1.put("rule."+ruleNum+".parameter.endStep", ""+119);
                propert1.put("rule."+ruleNum+".parameter.beginMonth", ""+0);
                propert1.put("rule."+ruleNum+".parameter.endMonth", ""+11);
                
                RuleStorage ruleStorage1 = RuleStorage.getRule(ruleName1);
                Rule rule1 = ruleStorage1.getNewInstance();
                RuleHelper.populateRule(ruleNum, nextSimulation.getStorage(), rule1, propert1); 
                paramRules.add(rule1);
                   
                ruleNum+=1;
                
                // OTM
                String ruleName2 = "Cantonnement"; 
                Properties propert2 = new Properties();
                propert2.put("rule."+ruleNum+".parameter.zone", FRA);
                propert2.put("rule."+ruleNum+".parameter.gear", OTM);
                propert2.put("rule."+ruleNum+".parameter.beginStep", ""+24);
                propert2.put("rule."+ruleNum+".parameter.endStep", ""+119);
                propert2.put("rule."+ruleNum+".parameter.beginMonth", ""+0);
                propert2.put("rule."+ruleNum+".parameter.endMonth", ""+11);
                
                RuleStorage ruleStorage2 = RuleStorage.getRule(ruleName2);
                Rule rule2 = ruleStorage2.getNewInstance();
                RuleHelper.populateRule(ruleNum, nextSimulation.getStorage(), rule2, propert2); 
                paramRules.add(rule2);
                   
                ruleNum+=1;

                // OTT
                String ruleName3 = "Cantonnement"; 
                Properties propert3 = new Properties();
                propert3.put("rule."+ruleNum+".parameter.zone", FRA);
                propert3.put("rule."+ruleNum+".parameter.gear", OTT);
                propert3.put("rule."+ruleNum+".parameter.beginStep", ""+24);
                propert3.put("rule."+ruleNum+".parameter.endStep", ""+119);
                propert3.put("rule."+ruleNum+".parameter.beginMonth", ""+0);
                propert3.put("rule."+ruleNum+".parameter.endMonth", ""+11);
                
                RuleStorage ruleStorage3 = RuleStorage.getRule(ruleName3);
                Rule rule3 = ruleStorage3.getNewInstance();
                RuleHelper.populateRule(ruleNum, nextSimulation.getStorage(), rule3, propert3); 
                paramRules.add(rule3);
                   
                ruleNum+=1;
                System.out.println("TOTO 2");


			}else if(rul == 3){               
            
                String ruleName = "EffortReduction";
                Properties propert = new Properties();
                double red = 0.1;           
                propert.put("rule."+ruleNum+".parameter.beginStep", ""+24);
                propert.put("rule."+ruleNum+".parameter.endStep", ""+200);
                propert.put("rule."+ruleNum+".parameter.PercentReduction", ""+red);

                RuleStorage ruleStorage = RuleStorage.getRule(ruleName);
                Rule rule = ruleStorage.getNewInstance();
                RuleHelper.populateRule(ruleNum, nextSimulation.getStorage(), rule, propert); 
                paramRules.add(rule);
                
                ruleNum+=1;
                System.out.println("TOTO 3");
                
            }else if(rul == 4){               
            
                String ruleName = "EffortReduction";
                Properties propert = new Properties();
                double red = 0.3;           
                propert.put("rule."+ruleNum+".parameter.beginStep", ""+24);
                propert.put("rule."+ruleNum+".parameter.endStep", ""+200);
                propert.put("rule."+ruleNum+".parameter.PercentReduction", ""+red);

                RuleStorage ruleStorage = RuleStorage.getRule(ruleName);
                Rule rule = ruleStorage.getNewInstance();
                RuleHelper.populateRule(ruleNum, nextSimulation.getStorage(), rule, propert); 
                paramRules.add(rule);
                
                ruleNum+=1;
                System.out.println("TOTO 4");
                
            }else if(rul == 5){               
            
                String ruleName = "EffortReduction";
                Properties propert = new Properties();
                double red = 0.5;           
                propert.put("rule."+ruleNum+".parameter.beginStep", ""+24);
                propert.put("rule."+ruleNum+".parameter.endStep", ""+200);
                propert.put("rule."+ruleNum+".parameter.PercentReduction", ""+red);

                RuleStorage ruleStorage = RuleStorage.getRule(ruleName);
                Rule rule = ruleStorage.getNewInstance();
                RuleHelper.populateRule(ruleNum, nextSimulation.getStorage(), rule, propert); 
                paramRules.add(rule);
                
                ruleNum+=1;
                System.out.println("TOTO 5");
            }
            
            
       
			////////////////////////////////////////////////////////////
			// Modif Recrutement 
			String recEq_part1 = "double recru = 48532;";
			if(rec == 1){
				recEq_part1 = "double recru = 111420;";
			}else if(rec==2){
				recEq_part1 = "double recru = 41187;";
			}
			
			String recEq_part2 = "double[] recZone = {0.165,0.835};";
			if(con ==1){
				recEq_part2 = "double[] recZone = {0.452,0.548};";
			}else if(con ==2){
				recEq_part2 = "double[] recZone = {0.026,0.974};";
			} 
			
			System.out.println("Avant changement recrutement");		
			
            // modif la capturabilite
            Equation eqq = pop.getReproductionEquation();
            String eqqs  = eqq.getContent();
            System.out.println("eqqs : "+eqqs);
            String[] eqqsSplit = org.apache.commons.lang3.StringUtils.splitByWholeSeparator(eqqs, "//genericEquation");
			String eqqNew = recEq_part1+recEq_part2+eqqsSplit[1];
            System.out.println("EqqNew : "+eqqNew);
            eqq.setContent(eqqNew);
            System.out.println("TOTO 6");
			
			////////////////////////////////////////////////////////////
			// Modif effectifs initiaux
			
            MatrixND mat = nextSimulation.getParameter().getNumberOf(pop);
            MatrixND mateffInit = null;
            if(eff == 0){
                mateffInit = mateffInit0;
            }else if(eff == 1){
                mateffInit = mateffInit1;       
            }else if(eff == 2){
                mateffInit = mateffInit2;
            }else if(eff == 3){
				mateffInit = mateffInit3;
			}
            for(MatrixIterator i = mat.iterator(); i.hasNext();){
                i.next();
                int[] dim = i.getCoordinates();
                double val = mateffInit.getValue(dim);
                i.setValue(val);
            }
			
			System.out.println("etape4 : Fin de la mise en place des combinaisons");
			return true;
		}else return false;

	}	
	

    /**
     * Call after each simulation.
     * 
     * @param context plan context
     * @param lastSimulation storage used for simulation
     * @return true if we must do next simulation, false to stop plan
     * @throws Exception
     */
    @Override
    public boolean afterSimulation(SimulationPlanContext context,
            SimulationStorage lastSimulation) throws Exception {
        
        return true;
    }
}
