/*
 * Copyright (C) 2012 jandre
 *
 * 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 2
 * 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
 
package analyseplans;

import static org.nuiton.i18n.I18n._;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import scripts.ResultName;

import java.io.*;
import java.io.File;
import java.io.FileReader;
import java.io.Writer;
import java.util.*;
import java.util.List;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;


import org.nuiton.math.matrix.*;
import org.nuiton.util.*;
import org.nuiton.topia.*;

import fr.ifremer.isisfish.util.Doc;
import fr.ifremer.isisfish.*;
import fr.ifremer.isisfish.types.*;
import fr.ifremer.isisfish.rule.Rule;
import fr.ifremer.isisfish.rule.RuleHelper;
import fr.ifremer.isisfish.simulator.SimulationContext;
import fr.ifremer.isisfish.types.Date;
import fr.ifremer.isisfish.entities.*;
import fr.ifremer.isisfish.simulator.AnalysePlan;
import fr.ifremer.isisfish.simulator.AnalysePlanIndependent;
import fr.ifremer.isisfish.simulator.AnalysePlanContext;
import fr.ifremer.isisfish.simulator.SimulationParameter;
import fr.ifremer.isisfish.datastore.RegionStorage;
import fr.ifremer.isisfish.datastore.RuleStorage;
import fr.ifremer.isisfish.datastore.SimulationStorage;
import fr.ifremer.isisfish.datastore.ResultStorage;
//import fr.ifremer.isisfish.util.TopiaEntityConverter;


/**
 * Future_Catch_biomass_independantPlan.java
 *
 * Created: 20 juin 2012
 *
 * @author jandre <user.name@vcs.hostName>
 * @version $Revision: 1545 $
 * Last update: $Date: 20 juin 2012 $
 * by : $Author: jandre $
 */
public class Future_Catch_biomass_independantPlan2 implements AnalysePlanIndependent {

    /** to use log facility, just put in your code: log.info("..."); */
    private static Log log = LogFactory.getLog(Future_Catch_biomass_independantPlan2.class);

    @Doc("Population BMW")
    public Population param_Population_BMW = null;
    @Doc("Population Calamari")
    public Population param_Population_Calamari = null;
    @Doc("Population Wrasse")
    public Population param_Population_Wrasse = null;
    @Doc("Population Garfish")
    public Population param_Population_Garfish = null;
    	
    static private final String MATRIX = "matrix";
    static private final String RECRUITBMW = "recruitBMW";
    static private final String RECRUITCAL = "recruitCal";
    static private final String RECRUITWRAS = "recruitWras";
    static private final String RECRUITGAR = "recruitGar";

    static private final String RuleRECRUITBMW30Years = "RuleBMWRecruit";
    static private final String RuleRECRUITGAR30Years = "RuleGarRecruit";
    static private final String RuleRECRUITCAL30Years = "RuleCalRecruit";
    static private final String RuleRECRUITWRA30Years = "RuleWraRecruit";
    
    public int param_parameterNumber = 4;//1 list of recruitment for each species
    public int param_first = 0;
    public int param_simulationNumber = 499; //actually, it is 500 but 1st simulation is called 0
    // !!! change the directory folder...
    public String param_directory = "C:/Program Files/isis-fish-3.3.0.7/ParamInit/30YearProjection/";//"C:/Documents and Settings/jandre/isis-database-3/analyseplans/";
    
    @Doc("path to create the biomass storage files")
    public String param_BMWnameFutFileB = "ParamInit/30YearProjection/BMWFutureB.csv";
    public String param_GARnameFutFileB = "ParamInit/30YearProjection/GarFutureB.csv";
    public String param_CALnameFutFileB = "ParamInit/30YearProjection/CalFutureB.csv";
    public String param_WRAnameFutFileB = "ParamInit/30YearProjection/WraFutureB.csv";
    public String param_nameFutFileB = "";
      // public String param_nameHistoFileB = "ParamInit/30YearProjection/HistoricB.csv";
    protected String exportBMWFutB = "";
    protected String exportGarFutB = "";
    protected String exportCalFutB = "";
    protected String exportWraFutB = "";
    protected String exportFutB = "";
	
    @Doc("path to create the catch storage file")
    //public String param_nameHistoFileCatch = "ParamInit/30YearProjection/HistoricCatch.csv";
    public String param_BMWnameFutFileCatch = "ParamInit/30YearProjection/BMWFutureCatch.csv";
    public String param_GARnameFutFileCatch = "ParamInit/30YearProjection/GarFutureCatch.csv";
    public String param_CALnameFutFileCatch = "ParamInit/30YearProjection/CalFutureCatch.csv";
    public String param_WRAnameFutFileCatch = "ParamInit/30YearProjection/WraFutureCatch.csv";
    public String param_nameFutFileCatch = "";
    protected String exportBMWFutCatch = "";
    protected String exportGarFutCatch = "";
    protected String exportCalFutCatch = "";
    protected String exportWraFutCatch = "";
    protected String exportFutCatch = "";

    protected List<Double> AvYearlyBiomass = new ArrayList<Double>();
    
    private MatrixND matrix = null;
       
    public String [] necessaryResult = {
	ResultName.MATRIX_DISCARDS_WEIGHT_PER_STR_MET_PER_ZONE_POP,
	ResultName.MATRIX_CATCH_WEIGHT_PER_STRATEGY_MET_PER_ZONE_POP,
	ResultName.MATRIX_BIOMASS,
	ResultName.MATRIX_ABUNDANCE_BEGIN_MONTH
    };

    @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 _("Tries various combinations of recruitment for the 4 species over 30 years");
    }
//**********************************************************************************************
    /**
     * Appele au demarrage de la simulation, cette methode permet d''initialiser
     * des valeurs
     * @param simulation La simulation pour lequel on utilise cette regle
     */
    @Override
    public void init(AnalysePlanContext context) throws Exception {
        File dir = new File(param_directory);
        
        matrix = MatrixFactory.getInstance().create(new int[]{param_simulationNumber, param_parameterNumber});
        matrix.importCSV(new FileReader(new File(dir, MATRIX + ".txt")), new int[]{0,0});
        matrix.setSemantic(1, Arrays.asList(new String[]{RECRUITBMW,RECRUITCAL,RECRUITWRAS,RECRUITGAR}));
        log.info("Matrix: " + matrix);
    }  
//**********************************************************************************************    
    /**
     * @param name the name of the element to get
     * @param simulation the simulation number
     * @return
     */
   private double getDouble(String name, 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"))));
        
	   // Go and gets in the matrix which modality is defined for the factor (-1,0,1...) 
        int ligne = simulation + param_first;
	   int mod = (int)matrix.getValue(ligne, name);
        double result = Double.parseDouble(prop.getProperty(""+mod));
        return result;
    }

    private double [] getList(String name, 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, name);
        double [] result = StringUtil.toArrayDouble(prop.getProperty(""+mod).split(";"));
        return result;

    }
//**********************************************************************************************  
    /**
     * @param name the name of the element to get
     * @param simulation the simulation number
     * @return
     */
    private String getString(String name, 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 , name);
        String result = prop.getProperty(""+mod);
        return result;
    }

    private Rule getRule(RegionStorage regionStorage, String name, int simulation) throws Exception {
    //private Rule getRule(TopiaContext topiacontext, String name, 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 , name);

        String ruleName = prop.getProperty(""+mod);
	   System.out.println("ruleName : "+ ruleName);
        RuleStorage ruleStorage = RuleStorage.getRule(ruleName);
        Rule rule = ruleStorage.getNewRuleInstance();
        RuleHelper.populateRule(mod, regionStorage.getStorage(), rule, prop);//!!!! check that OK
        
        return rule;
    }

//**********************************************************************************************
    /**
     * 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(AnalysePlanContext context,
        	SimulationStorage nextSimulation) throws Exception {
           
           
     	 int simNum = nextSimulation.getParameter().getAnalysePlanNumber();//!!!! Why is that not working?!
        	 if (simNum < param_simulationNumber) {
/*/
            double [] BMWrecruits    = getList (RECRUITBMW , simNum);
            double [] Calrecruits    = getList (RECRUITCAL , simNum);
            double [] Wrasrecruits   = getList (RECRUITWRAS , simNum);
            double [] Garrecruits    = getList (RECRUITGAR , simNum);

            log.info("BMWRecruits ("+simNum+")= " + BMWrecruits[0] + " ; " + BMWrecruits[6]);// just to check that it is taking different recruitment
            log.info("CalRecruits ("+simNum+")= " + Calrecruits[0] + " ; " + Calrecruits[6]);
            log.info("WrasRecruits ("+simNum+")= " + Wrasrecruits[0] + " ; " + Wrasrecruits[6]);
            log.info("GarRecruits ("+simNum+")= " + Garrecruits[0] + " ; " + Garrecruits[6]);
/*/
            Rule RuleBMWRecruit = getRule(context.getParam().getRegion(), RuleRECRUITBMW30Years, simNum);// Je vois le mismatch mais j'arrive pas a corriger...
            Rule RuleGarRecruit = getRule(context.getParam().getRegion(), RuleRECRUITGAR30Years, simNum);
		  Rule RuleCalRecruit = getRule(context.getParam().getRegion(), RuleRECRUITCAL30Years, simNum);
            Rule RuleWraRecruit = getRule(context.getParam().getRegion(), RuleRECRUITWRA30Years, simNum);
            
		  // Modification of the recruitment rules for the 4 species
		  // !!! Careful, you need to load the rules in the simulation interface but with such parameters
		  // that the rule doesn't actually apply!!!
		  //
		  // We remove the rules from the previous simulation
		  List<Rule> paramRules = nextSimulation.getParameter().getRules();
            paramRules.remove(context.getValue("lastRuleBMWRecruit"));
		  paramRules.remove(context.getValue("lastRuleGarRecruit"));
            paramRules.remove(context.getValue("lastRuleWraRecruit"));
		  paramRules.remove(context.getValue("lastRuleCalRecruit"));   
	       //
  		  // We keep the rules that we want to add
  		  context.setValue("lastRuleBMWRecruit", RuleBMWRecruit);
		  context.setValue("lastRuleGarRecruit", RuleGarRecruit);
		  context.setValue("lastRuleWraRecruit", RuleWraRecruit);
		  context.setValue("lastRuleCalRecruit", RuleCalRecruit);
		  //
            // We add the rules
            paramRules.add(RuleBMWRecruit);
		  paramRules.add(RuleGarRecruit);
            paramRules.add(RuleWraRecruit);
            paramRules.add(RuleCalRecruit);

            TopiaContext tx = nextSimulation.getStorage().beginTransaction();
            


            tx.commitTransaction();
            return true;
        } else {
            return false;
        } 
     	     
    }
//**********************************************************************************************
     /**
     * Modify nextSimulation database with q in exp.
     * @param exp
     * @param nextSimulation
     * @throws Exception
     */


   	protected void RecordResult(Population param_Population, SimulationStorage lastSimulation) throws Exception {
	//methode appelee dans after simualtion
		
		if (param_Population.equals("garfish_east")){
			exportFutCatch = exportGarFutCatch;
			exportFutB = exportGarFutB;
			param_nameFutFileB = param_GARnameFutFileB;		
			param_nameFutFileCatch = param_GARnameFutFileCatch;
   		}else if (param_Population.equals("BMW_SSE")){ 			
			exportFutCatch = exportBMWFutCatch;
			exportFutB = exportBMWFutB;
			param_nameFutFileB = param_BMWnameFutFileB;
			param_nameFutFileCatch = param_BMWnameFutFileCatch;
		}else if (param_Population.equals("Wrasse_SSE")){ 			
			exportFutCatch = exportWraFutCatch;
			exportFutB = exportWraFutB;
			param_nameFutFileB = param_WRAnameFutFileB;
			param_nameFutFileCatch = param_WRAnameFutFileCatch;
		}else { 	// "Calamari_SSE"		
			exportFutCatch = exportCalFutCatch;
			exportFutB = exportCalFutB;
			param_nameFutFileB = param_CALnameFutFileB;
			param_nameFutFileCatch = param_CALnameFutFileCatch;
		}
					
		ResultStorage result = lastSimulation.getResultStorage();

        	//////////////// Import of the catch by weight matrix//////////////////
        	MatrixND C2 = result.getMatrix(param_Population, ResultName.MATRIX_CATCH_WEIGHT_PER_STRATEGY_MET_PER_ZONE_POP);   		
		System.out.println("Imports catch matrix");

		MatrixND C = C2.copy();
		
		C = C.sumOverDim(1); // Sum over strategies
		C = C.sumOverDim(2);// sum over metiers
        	C = C.sumOverDim(3);// sum over groups
        	C = C.sumOverDim(4);// sum over zones
		C = C.reduce(); // erases the dimensions of size 0

		//////////////// Import of the discards by weight matrix//////////////////
        	MatrixND D2 = result.getMatrix(param_Population,ResultName.MATRIX_DISCARDS_WEIGHT_PER_STR_MET_PER_ZONE_POP);   		
		System.out.println("Imports discard matrix");
     	
     	MatrixND D = D2.copy();	
		D = D.sumOverDim(1); // Sum over strategies
		D = D.sumOverDim(2);// sum over metiers
        	D = D.sumOverDim(3);// sum over groups
        	D = D.sumOverDim(4);// sum over zones
		D = D.reduce(); // erases the dimensions of size 0

		//////////////// Calculation of the landing by weight matrix//////////////////
		// The Landing matrix outputed by the model is in number of individuals, I need it in weight to compare 
		// model outputs with our observed reported catch(=real catch minus the discards because of undersized animals)
		MatrixND L = C.copy();	
		L = L.minus(D);	
		System.out.println("Calculation of landing matrix");

		//////////////// Import of the biomass matrix//////////////////
		MatrixND B2 = result.getMatrix(param_Population, ResultName.MATRIX_BIOMASS);
		System.out.println("imports biomass matrix");
		// dim biomass matrix: 	0.Date
		//					1. Group
		//					2. Zone
		MatrixND B = B2.copy();
		
		B = B.sumOverDim(1); // Sum over groups
		B = B.sumOverDim(2);// sum over zones
		B = B.reduce(); // erases the dimensions of size 0
		
		MatrixND AvB = B.copy();

		// Need to get the average yearly biomass
		for(int j=0;j<30*12;j+=12){//in increments of 12			
			double [] numsToSum = {AvB.getValue(j),AvB.getValue(j+1),AvB.getValue(j+2),AvB.getValue(j+3),AvB.getValue(j+4)
			,AvB.getValue(j+5),AvB.getValue(j+6),AvB.getValue(j+7),AvB.getValue(j+8),AvB.getValue(j+9),AvB.getValue(j+10)
			,AvB.getValue(j+11)};
			double sum = 0.0;
			double value = 0.0;  
		
			for(int i = 0; i <11; i++) { 
				sum += numsToSum[i];
			}
			value = sum/12/1000; //divide by 1000 to get the biomass in tones as it is currently in kg
			AvYearlyBiomass.add(value);
		}
		
		//Writing of the Future table
		FileWriter exportFutureB = new FileWriter (param_nameFutFileB);
		for (int k=0;k<30;k++){// 30 years
			exportFutureB.append(Double.toString(AvYearlyBiomass.get(k)));
			exportFutureB.append(';');
		}		
		exportFutureB.append('\n');
		exportFutureB.flush();
	     exportFutureB.close();
	
		// Writing the catch File		
		FileWriter exportFutureCatch = new FileWriter (param_nameFutFileCatch);

		for (int k=0;k<(30*12)-1;k++){// 30 years * 12 months
			exportFutureCatch.append(Double.toString(L.getValue(k)));
			exportFutureCatch.append(';');
		}		
		exportFutureCatch.append('\n');
		exportFutureCatch.flush();
	     exportFutureCatch.close();
	
		
   	}// end RecordResult//**********************************************************************************************
   
    /**
     * 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(AnalysePlanContext context,
            SimulationStorage lastSimulation) throws Exception {
        		int number = lastSimulation.getParameter().getAnalysePlanNumber();
		System.out.println("Recording Catch and Biomass sim " + number);

		RecordResult(param_Population_BMW,lastSimulation);
		RecordResult(param_Population_Calamari,lastSimulation);
		RecordResult(param_Population_Garfish,lastSimulation);
		RecordResult(param_Population_Wrasse,lastSimulation);
		
       return true;
    }// end after simulation
        
    
}