package simulationplans;

import static org.nuiton.i18n.I18n._;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.*;
import java.io.File;
import java.io.FileReader;
import java.util.Arrays;
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.*;
import fr.ifremer.isisfish.types.*;
import fr.ifremer.isisfish.rule.Rule;
import fr.ifremer.isisfish.rule.RuleHelper;
import fr.ifremer.isisfish.entities.*;
import fr.ifremer.isisfish.simulator.AnalysePlanIndependent;
import fr.ifremer.isisfish.simulator.AnalysePlanContext;
import fr.ifremer.isisfish.simulator.SimulationPlanContext;
import fr.ifremer.isisfish.simulator.SimulationPlan;
import fr.ifremer.isisfish.datastore.ResultStorage;
import fr.ifremer.isisfish.datastore.RuleStorage;
import fr.ifremer.isisfish.datastore.SimulationStorage;
import fr.ifremer.isisfish.simulator.SimulationPlanIndependent;
import fr.ifremer.isisfish.util.Doc;
import scripts.ResultName;
import org.apache.commons.io.FileUtils;
/*
 * SensitivityCapturabilite3ParamP1.java
 *
 * Created: 10/09/09
 *
 * @author slehuta
 * @version $Revision: version 3.2.
 *
 * Last update: $Date:  $
 * by : $Author:sigrid $
 */

public class SensitivityCapturabilite3ParamP1 implements SimulationPlanIndependent {

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

   static private final String MATRIX = "matrix";
    private MatrixND matrix = null;
	
	
	
    @Doc("Population which parameters are calibrated")
    public Population param_Population = null;
    int param_parameterNumber = 10;
    public int param_first = 0;
    public int param_simulationNumber = 1000;
    public String param_directory = "Input_essai_recuit/LHS3ParamP1";
    public String param_exportPath = "Output_essai_recuit/FonctionObjectifLHS3ParamP1.csv";
    protected String exportHisto = "";
    public String param_nomfichier_debarquements = "Input_essai_recuit/Inputlandings.csv";
    protected File debarquementsObserves;
    protected MatrixND matrixDebarquement;
    
    int compteurSimus;
    public String [] necessaryResult = {
    };
    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
     */
    public String getDescription() throws Exception {
        // TODO
        return _("fait experiences d'une maytrice pour x parametres");
    }
 
    /**
     * Appelmarrage de la simulation, cette methode permet d'initialiser
     * des valeurs
     * @param simulation La simulation pour lequel on utilise cette regle
     */
    public void init(SimulationPlanContext 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 + ".csv")), new int[]{0,0});
        if (param_nomfichier_debarquements == null
            || "".equals(param_nomfichier_debarquements)) {
        debarquementsObserves = FileUtil.getFile(".*.csv",
               "fichier csv separateur ';'");
        } else {
        		debarquementsObserves = new File(param_nomfichier_debarquements);
        }
  	   // ***Create the matrix named matrixDebarquement that will contain your observed landings
  	   int[] dimMatrix = {12,1};
  	   matrixDebarquement=MatrixFactory.getInstance().create(dimMatrix); //date est un int donnant le nombre de mois sur lequel on a simule.
  	   // ***Then import your file in it
  	   matrixDebarquement.importCSV(new FileReader(debarquementsObserves),new int []{0,0});
  	   matrixDebarquement= matrixDebarquement.reduce(); //Indispensable pour pouvoir travailler sur la matrice et avoir les memes dimensions que les simulations.
   	   //log.info("MatrixDebarquement : " + matrixDebarquement);
	 }
     

    
    /**
     * 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
     */
    public boolean beforeSimulation(SimulationPlanContext context, SimulationStorage nextSimulation) throws Exception {
        int simNum = nextSimulation.getParameter().getSimulationPlanNumber();
        //int simNum = context.getNumber();
        if (simNum + param_first < param_simulationNumber) {
            
      

           
            
            ///////////////////////////////////////////////////////////////////////////////////////////
            // Modif database
		TopiaContext db = nextSimulation.getStorage().beginTransaction();//ouvrir un context pour modifier les donnees
        Population pop = (Population) db.findByTopiaId(param_Population.getTopiaId());			
            

             int ligne = simNum + param_first;
	


            // modif la capturabilite
            MatrixND c = pop.getCapturability();
            for (MatrixIterator i = c.iterator(); i.hasNext();){
                    i.next() ;
                    Object [] sem = i.getSemanticsCoordinates();
                    PopulationGroup group = (PopulationGroup)sem[0];
                    PopulationSeasonInfo season = (PopulationSeasonInfo)sem[1];
          		if (season.getFirstMonth().after(Month.JULY)){ 
          	     	double q = (double)matrix.getValue(ligne,2); 
          	     	i.setValue(q);
         		 	} else if (season.getFirstMonth().after(Month.MARCH)) {
         	      		double q = (double)matrix.getValue(ligne,1); 
          	     	i.setValue(q);
         	 	 	} else {
                		double q = (double)matrix.getValue(ligne,0); 
          	     	i.setValue(q);
            		}  
            }
		 


			
            db.commitTransaction();
            return true;
        } else {
            return false;
        }
    }

    /**
     * Call after 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
     */
    public boolean afterSimulation(SimulationPlanContext context,
            SimulationStorage lastSimulation) throws Exception{
    		File exportHistoric = new File(param_exportPath);
		ResultStorage result = lastSimulation.getResultStorage();
		boolean bool=true;
		compteurSimus=getIteration(lastSimulation); //numero de la simulation qui vient de se terminer
		double obj=calculFonctionObjectif(result);
		exportHisto+=toCSV(obj);
		FileUtils.writeStringToFile(exportHistoric,exportHisto);
		return true;
    }
	
	/**
	*Calcule la valeur de la fonction d'objectif de la parametrisation utilisee pour la simulation qui vient de s'achever.
	*@param result le numero de la simulation venant de s'achever
	*@return la valeur de fonction d'objectif
	*/
    public double calculFonctionObjectif(ResultStorage result) {//Inclut l'import des donnees, des resultats de la simulation et le calcul de la fonction d'objectif;
        /////***import the matrix of simulated data (here landings) from the simulation result
        MatrixND L = result.getMatrix(param_Population,
                ResultName.MATRIX_CATCH_WEIGHT_PER_STRATEGY_MET_PER_ZONE_POP);
        /////*** extract, sum, etc to obtain the same format/data as your observation matrix
        // useful methods to work on matrix : sumOverDim(), getSubMatrix(), reduce()
        //Somme sur les strategies, metiers, groupes et zones
        L= L.sumOverDim(1);
        L= L.sumOverDim(2);
        L= L.sumOverDim(3);
        L= L.sumOverDim(4);
        L= L.reduce(); //Indispensable pour pouvoir travailler sur la matrice et avoir les memes dimensions que les observations.
        ///////////////////Calcul du critere//////////////////
        log.info("calcul de la fonction objectif");
        log.info("dim de L" + " " + Arrays.toString(L.getDim()));
        log.info("dim de obs" + " "
                + Arrays.toString(matrixDebarquement.getDim()));
		int annee = 0;
		double obser = 0;//Pour 1 an, la somme des d��������barquements, tous groupes confondus		
		double obj = 0;
        double obj1 = 0;
		double obj2 = 0;
        // *** using the matrixIterator assumes that the observation and simulated
        // matrix are organised in the exact same way ( columns and rows corresponding in each of them)
        for (MatrixIterator g = L.iterator(); g.hasNext();) {// Les simus durent un an, obser sera toujours bon.
		
            g.next();
            int[] dim = g.getCoordinates();
			log.info(dim);
            double obs = matrixDebarquement.getValue(dim);
			obser +=obs;
		}
        for (MatrixIterator g = L.iterator(); g.hasNext();) {
            g.next();
            int[] dim = g.getCoordinates();
            double obs = matrixDebarquement.getValue(dim);
            double simules = g.getValue();
            obj1 = Math.pow(0.001*(obs - simules), 2);// On prend le millieme au carre pour eviter les valeurs trop grandes
			obj2 = Math.pow(0.001*(obser), 2);
			obj += obj1/obj2;
        }
		
        return obj;
    }
	
	public String toCSV(double obj) {
		String saut = "\n";
		String result = "";
		result += obj + saut;
		return result;
	}
	
	public String toCSV10FO(double [] obj) {
		String sep = ";";
		String result = "";
		for (int i=0; i<10; i++){
			result += obj[i] + sep;
		}
		return result;
	}
	
	/**
	*Recupere le numero de la simulation renseignee
	*@param simulation storage de la simulationr enseignee
	*@return numero de la simulation renseignee
	*/
	public int getIteration(SimulationStorage simulation){			//Il n'y a pas de setIteration
		return simulation.getParameter().getSimulationPlanNumber();
	}
}
