/*
 * 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 scripts.ResultName;

/**
 * PlanSocioec.java
 *
 * Created: 9 septembre 2014
 *
 * @author slehuta <user.name@vcs.hostName>
 * @version $Revision: 1545 $
 * Last update: $Date: 9 septembre 2014 $
 * by : $Author: slehuta $
 */
public class PlanRegressionTrees implements SimulationPlanIndependent {

    /** to use log facility, just put in your code: log.info("..."); */
    private static Log log = LogFactory.getLog(PlanRegressionTrees.class);
    
    static private String MATRIX = "Plan18pLHS1000";
    private MatrixND matrix = null;
	
	static private final String multRsol = "multRSol";
	static private final String multRple = "multRPle";
	static private final String multRcod = "multRCod";
    static private final String multRwhg = "multRWhg";
	
	static private final String effinitSol = "effinitSol";
	static private final String effinitPle = "effinitPle";
	static private final String effinitCod = "effinitCod";
	static private final String effinitWhg = "effinitWhg";	


	// mesures de gestion
    static private final String MLSsol = "MLSsole";
    static private final String MLSple = "MLSplaice";
    static private final String MLScod = "MLScod"; 
    static private final String MLSwhg = "MLSwhiting";
	static private final String propSol = "propSole";
	static private final String propPle = "propPlaice";
	static private final String propCod = "propCod";
	static private final String propWhg = "propWhiting";

	static private final String opport = "opportunism";
	static private final String effort = "effort";

    
    static private final int parameterNumber = 18;
    static private final int matrix_size = 1000;//3636; 
    public int param_first = 0;
    public int param_simulationNumber = 1000;
    public String param_directory = "InputMixChannel/PlanRegressionTrees/";

    
    protected String[] necessaryResult = {
        // put here all necessary result for this rule
        // example:
        // ResultName.MATRIX_BIOMASS,
        // ResultName.MATRIX_NET_VALUE_OF_LANDINGS_PER_STRATEGY_MET,
    };

    @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 {
        // TODO change description
        return "Plan LHS regression trees";
    }

    /**
     * Called once before {@code beforeSimulation} call.
     * 
     * @param context plan context
     */
    @Override
    public void init(SimulationPlanContext context) throws Exception {
        
        matrix = MatrixFactory.getInstance().create(new int[]{matrix_size, parameterNumber});
        matrix.importCSV(new FileReader(new File(param_directory, MATRIX + ".txt")), new int[]{0,0});
        List<Integer> dim0 = new ArrayList<Integer>();
        for (int i=0; i < matrix_size; i++) {
            dim0.add(i);
        }
        matrix.setSemantic(0, dim0);
        matrix.setSemantic(1, Arrays.asList(new String[]{multRsol,multRple,multRcod,multRwhg,effinitSol,effinitPle,effinitCod,effinitWhg,MLSsol,MLSple,MLScod,MLSwhg,propSol,propPle,propCod,propWhg,opport,effort}));
		context.getParam().addExtraRules("TACpoidsTailleMin","GravityModelMixChannel");

    }
    /**
     * @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;
        if (nextSimulation.getParameter().getSimulationPlanNumber() < param_simulationNumber) {
			
			TopiaContext db = nextSimulation.getStorage().beginTransaction();
            List<Population> pops = nextSimulation.getParameter().getPopulations();
            
            //////////////////////////////////////////////////////////////////////////////////////////

		// Modif rules
		List<Rule> paramRules = nextSimulation.getParameter().getRules();
		
            double mlssol	= matrix.getValue(simNum,MLSsol);
            double mlsple = matrix.getValue(simNum,MLSple);
            double mlscod = matrix.getValue(simNum,MLScod);
			double mlswhg = matrix.getValue(simNum,MLSwhg);
            double propsol = matrix.getValue(simNum,propSol);
            double propple = matrix.getValue(simNum,propPle);
            double propcod = matrix.getValue(simNum,propCod);
			double propwhg = matrix.getValue(simNum,propWhg);

            double mintac =0 ;
            double maxtac = 0;
            double meantac = 0;
            double minssb = 0;// prop,maxtac, mintac,meantac,minssb,mls
            String popname = "";
            double prop = 0;
            double mls = 0;
            int ruleNum = 0;
 
		for(Population pop : pops){
            boolean doit = false;
			if("Sole".equals(pop.getName())){
				double [] paramsol = {3483.0,6590.0,5306.0,8143.0};
                mintac=paramsol[0];
                maxtac=paramsol[1];
                meantac=paramsol[2];
                minssb=paramsol[3];
                prop = propsol;
                mls = mlssol; 	
                doit = true;
                ruleNum = 0;
				//popname = "fr.ifremer.isisfish.entities.Population\#1370350071420\#0.07960912867432401\";
			} else if("Plaice".equals(pop.getName())){
				double[] parample = {4274,6400,5050,13612};
                mintac=parample[0];
                maxtac=parample[1];
                meantac=parample[2];
                minssb=parample[3];
                prop = propple;
                mls = mlsple;
                doit = true;
                ruleNum = 1;
				//popname = "fr.ifremer.isisfish.entities.Population\#1370438889822\#0.4072784420136317\";
			} else if("Cod7d".equals(pop.getName())){
				double [] paramcod = {959.341,1349.305,1201.752,3562.711};
                mintac=paramcod[0];
                maxtac=paramcod[1];
                meantac=paramcod[2];
                minssb=paramcod[3];              
				prop = propcod;
                mls = mlscod;
                doit = true;
                ruleNum = 2;
				//popname = "fr.ifremer.isisfish.entities.Population\#1456587345303\#0.8352983459523022\:Cod7d";
			} else if("Whiting7d".equals(pop.getName())){
				double[] paramwhg = {7817.692,18395.334,13128.870,41053.925};
                mintac=paramwhg[0];
                maxtac=paramwhg[1];
                meantac=paramwhg[2];
                minssb=paramwhg[3];                 
				prop = propwhg;
                mls = mlswhg;
                doit = true;
                ruleNum = 3;
				//popname = "fr.ifremer.isisfish.entities.Population\#1455117672712\#0.2949196148858023\:Whiting7d";
			}			
            if(doit){
			
				String ruleName = "TACpoidsTailleMin";
				Properties propert = new Properties();
             
				propert.put("rule."+ruleNum+".parameter.beginStep", ""+0);
				propert.put("rule."+ruleNum+".parameter.endStep", ""+500);
				propert.put("rule."+ruleNum+".parameter.pop", pop.getTopiaId());
				propert.put("rule."+ruleNum+".parameter.propSurvie", ""+0);
				propert.put("rule."+ruleNum+".parameter.tacInTons", ""+0);
				propert.put("rule."+ruleNum+".parameter.propTac", ""+prop);
				propert.put("rule."+ruleNum+".parameter.minTAC", ""+mintac);
				propert.put("rule."+ruleNum+".parameter.maxTAC", ""+maxtac);
				propert.put("rule."+ruleNum+".parameter.meanTAC", ""+meantac);
				propert.put("rule."+ruleNum+".parameter.minSSB", ""+minssb);
				propert.put("rule."+ruleNum+".parameter.TailleMin",""+mls);
             
				RuleStorage ruleStorage = RuleStorage.getRule(ruleName);
				Rule rule = ruleStorage.getNewInstance();
				RuleHelper.populateRule(ruleNum, nextSimulation.getStorage(), rule, propert); 
				paramRules.add(rule);
            }

		}
		
		

        /*Properties propert = new Properties();
        propert.load(new BufferedReader(new FileReader(new File("InputMixChannel/PlanRegressionTrees/RulesPlan.txt"))));
        
        String ruleName = propert.getProperty(""+0);
        RuleStorage ruleStorage = RuleStorage.getRule(ruleName);
        Rule rule = ruleStorage.getNewInstance();
        RuleHelper.populateRule(0, nextSimulation.getStorage(), rule, propert);*/
		
		
		// gravité
		double gravity = matrix.getValue(simNum,opport);
		// mettre la valeur en tag
		nextSimulation.getParameter().getTagValue().put("gravity",String.valueOf(gravity));

	
			
			// modif eff init

			for(Population pp : pops){
				MatrixND mat = nextSimulation.getParameter().getNumberOf(pp);
				double alpha = 1;
				if ("Sole".equals(pp.getName())){
					alpha = matrix.getValue (simNum,effinitSol);
				}else if ("Plaice".equals(pp.getName())){
					alpha = matrix.getValue (simNum,effinitPle);
				}else if ("Cod7d".equals(pp.getName())){
					alpha = matrix.getValue (simNum,effinitCod);
				}else if ("Whiting7d".equals(pp.getName())){
					alpha = matrix.getValue (simNum,effinitWhg);
				}
				
				mat = mat.mults(alpha) ;	
			}

	
			            
			
			// change % strategie
			double eff	= matrix.getValue (simNum,effort);
			StrategyDAO strategyDAO = IsisFishDAOHelper.getStrategyDAO(db) ;
            List<Strategy> strategies = strategyDAO.findAll();
			for (Strategy strategy : strategies) {
				double oldProp = strategy.getProportionSetOfVessels();
				double newProp = oldProp *eff;
				//System.out.println(strategy.getName() +" new prop : "+ newProp);
				 strategy.setProportionSetOfVessels(newProp);
				 System.out.println(strategy.getProportionSetOfVessels());
			}
			
			
			// change Rec
			double recsol	= matrix.getValue (simNum,multRsol);
            double recple	= matrix.getValue (simNum,multRple);
            double reccod	= matrix.getValue (simNum,multRcod);
            double recwhg	= matrix.getValue (simNum,multRwhg);
			
			for(Population pop :pops){
				double rec = 1;
                boolean doit = false;
				if(pop.getName().equals("Sole")){
					rec = recsol;
                   doit = true;
				}else if(pop.getName().equals("Plaice")){
					rec = recple;
                   doit = true;
				}else if(pop.getName().equals("Cod7d")){
					rec = reccod;
                   doit = true;
				}else if(pop.getName().equals("Whiting7d")){
					rec = recwhg;
                   doit = true;
				} else doit = false;
               if(doit){
				Population po = (Population) db.findByTopiaId(pop.getTopiaId());
				Equation eqm = po.getRecruitmentEquation();
				String eqmS = eqm.getContent();
				String[] eqmSplit = eqmS.split("mult = 1");
//System.out.println("split :"+eqmSplit +" split0 : "+eqmSplit[0]);
				String eqmNew = eqmSplit[0] + "mult = " + rec + eqmSplit[1];
				eqm.setContent(eqmNew);
				System.out.println("eqRec : "+pop.getName()+eqmNew);
               }
			}
			
            db.commitTransaction();    
			db.closeContext();
	
	
	
	
			
            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;
    }
}
