package analyseplans;

import static org.codelutin.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.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.codelutin.math.matrix.*;
import org.codelutin.util.*;
import org.codelutin.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.simulator.SimulationContext;
import fr.ifremer.isisfish.types.Date;
import fr.ifremer.isisfish.entities.*;
import fr.ifremer.isisfish.simulator.AnalysePlan;
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;

/**
 * Sensibility.java
 *
 * Created: 21 décembre 2008
 *
 * @author HUSSEIN chirine <m475chi@yahoo.fr>
 *
 */
public class Sensibility implements AnalysePlan {

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

    static private final String MATRIX = "matrix"; /// Le fichier est donc matrix.txt
    static private final String VBGF = "growth";
	static private final String FECONDITY = "fecondity";
	static private final String MIGRATION = "migration";
	static private final String MORTALITY = "mortalite";
	static private final String CATCHABILITY = "capturabilite";
	static private final String FSTD = "facteurStd";
    static private final String SELECTIVITY = "selectivity";
    static private final String SELECTIVITYFiletMer = "selectivityFiletMer";
    static private final String SELECTIVITYFiletEtang = "selectivityFiletEtang";
    static private final String SELECTIVITYLignecote = "selectivityLignecote";
    static private final String SELECTIVITYLigneemb = "selectivityLigneemb";
    static private final String SELECTIVITYChalut = "selectivityChalut";
    static private final String SELECTIVITYHarpon = "selectivityHarpon";
    static private final String TARGET = "targetFact";
	static private final String EFFORT = "effort";
	    //static private final String AMP = "amp";
    //static private final String AMP1 = "ampRPrepro_Lignecote";
    //static private final String AMP2 = "ampRPrepro_FiletMer"; 
    ///////////* completer ici avec tous les fichiers .txt des amp
        
    public int param_parameterNumber = 9 ; /// ce paramètre n'est normalement pas modifié
    public int param_first = 0; /// on utilise ce paramètre pour indiquer à quelle ligne 
                /// de la matrice on doit commencer à faire tourner les simulations au cas où on ait dû interompre le plan
    public int param_simulationNumber = 168;/// ce paramètre n'est normalement pas modifié 
    public String param_directory = "C:/Documents and Settings/hussein/Bureau/isis-fish-3.1.3/isis-fish-3.1.3/SACSarfiles//"; /// Il s'agit du chemin vers le dossier 
        ///contenant les fichier de matrice et paramètres, par défaut le repertoire du go.bat 
        /// donc dans ce cas c'est le dossier Exemple_repertory qui est dans le dossier contenant le go.bat
  
	/// Déclaration de la matrice d'expérience 
    private MatrixND matrix = null;

	/// Non utilisé pour ce script mais à ne pas effacer
    public String [] necessaryResult = {
    // put here all necessary result for this rule
    // example: 
    // ResultName.MATRIX_BIOMASS,
    // ResultName.MATRIX_NET_VALUE_OF_LANDINGS_PER_STRATEGY_MET,
    };

    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 {
       
        return _("réalise les expériences d'un plan complet pour 9 parametres à 2 modalités");
    }
 
    /**
     * 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(AnalysePlanContext context) throws Exception {
        /// Création de la matrice d'expérience et chargement :
        File dir = new File(param_directory);
 
        File csvFile = new File(dir, MATRIX + ".txt");
        log.info("CSV file is : " + csvFile.getAbsolutePath());
        if(!csvFile.exists()) {
            log.warn("File doesn't exists");
        }

        matrix = MatrixFactory.getInstance().create(new int[]{param_simulationNumber, param_parameterNumber});
        matrix.importCSV(new FileReader(csvFile), new int[]{0,0});
        matrix.setSemantics(1, Arrays.asList(new String[]{VBGF,FECONDITY,MIGRATION,MORTALITY,CATCHABILITY,FSTD,SELECTIVITY,TARGET,EFFORT}));
        System.out.println(matrix);

	/// ajout des regles qui sont modifiées par le plan
	//context.getParam().addExtraRules("Cantonnement","Cantonnement","Cantonnement","Cantonnement","Cantonnement","Cantonnement");
                ////////////*  ecrire cantonnement 11 fois s'il y a 11 regles cantonnement
    }

         /// Création des méthodes qui réccupère dans la matrice la modalité du paramètre pour l'expérience en cours 
                /// Les arguments des méthodes sont le nom du fichier et le numéro de la simulation
                /// (On détermine l'expérience en cours et donc la ligne de la matrice en sommant le numéro de la simulation et le param_first)
                /// pour un double  

    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"))));        
        int ligne = simulation + param_first;
        int mod = (int)matrix.getValue(ligne, name);
        System.out.println("mod et ligne : "+ mod+" "+ligne);
        double result = Double.parseDouble(prop.getProperty(""+mod));
        System.out.println("result : "+ result);
        return result;
    }
        
                /// pour une liste de doubles

 /**
     * @param name le nom de l'element a recuperer
     * @param simulation le numero de la simulation
     * @return
     */   
    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(";")); // ""+ ca construit une chaine de caractere
        return result;
    }
        
                /// pour une chaine de characteres (equation)
    /**
     * @param name le nom de l'element a recuperer
     * @param simulation le numero de la simulation
     * @return
     */
    private String getString(String name,String col, 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 , col);
        String result = prop.getProperty(""+mod);
        return result;
    }
                /// pour les paramètres d'une règle de gestion
          /**
     * @param name le nom de l'element a recuperer
     * @param simulation le numero de la simulation
     * @return
     */        
    private Rule getRule(RegionStorage regionStorage, String name, String col, 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 , col);
        String ruleName = prop.getProperty(""+mod);
        RuleStorage ruleStorage = RuleStorage.getRule(ruleName);
        Rule rule = ruleStorage.getNewRuleInstance();
        RuleHelper.populateRule(mod, regionStorage, rule, prop); 
        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
     */
    public boolean beforeSimulation(AnalysePlanContext context, SimulationStorage nextSimulation) throws Exception {

        int simNum = context.getNumber()+ param_first; 
        
        if(simNum < param_simulationNumber+1) { /// On vérifie qu'il reste des expériences à faire

                        /// On utilise les méthodes crées pour récupérer les valeurs des modalités pour l'expérience en cours
            double [] croissance    = getList (VBGF , simNum);
			double reproductionRate = getDouble (FECONDITY, simNum);
            double migration = getDouble (MIGRATION,simNum);
			String mortality      = getString (MORTALITY,MORTALITY, simNum);
            double catchability     = getDouble (CATCHABILITY, simNum);
			double standardisation  = getDouble (FSTD, simNum);
            //String selectivity      = getString (SELECTIVITY, simNum);
            String selectivityFiletMer      = getString (SELECTIVITYFiletMer,SELECTIVITY, simNum);
            String selectivityFiletEtang      = getString (SELECTIVITYFiletEtang,SELECTIVITY, simNum);
            String selectivityLignecote      = getString (SELECTIVITYLignecote,SELECTIVITY, simNum);
            String selectivityLigneemb      = getString (SELECTIVITYLigneemb,SELECTIVITY, simNum);
            String selectivityChalut      = getString (SELECTIVITYChalut,SELECTIVITY, simNum);
            String selectivityHarpon      = getString (SELECTIVITYHarpon,SELECTIVITY, simNum);
        	double targetFactor     = getDouble (TARGET, simNum);
			double effort           = getDouble (EFFORT, simNum);
                        //Rule amp1 = getRule(context.getParam().getRegion(), AMP1, AMP, simNum);
                        //Rule amp2 = getRule(context.getParam().getRegion(), AMP2, AMP, simNum);
                        ///////////////* à completer avec le reste des amp*/
                        
                        /// On peut afficher des informations dans les logs
  			nextSimulation.getInformation().addInformation("Fecondites ("+context.getNumber()+")= " + reproductionRate);
           //nextSimulation.getInformation().addInformation("Selectivites ("+context.getNumber()+")= " + selectivityEquation);

                        /// Il faut maintenant modifier la valeur des paramètres dans la base de données et les paramètres des règle de la simulation
                                /// modif les parametres des regles de gestion :
            
                        // on enleve la regle de la simulation passé (elle est stoquée sous le nom LastAMP) 
            //List<Rule> paramRules = nextSimulation.getParameter().getRules();
            
                        //paramRules.remove(context.getValue("lastAMP1"));
            // on nomme la regle que l'on va ajouter LastAMP (pour pouvoir la retrouver a la simulation suivante)
                        //context.setValue("lastAMP1", amp1);
            // on ajoute la regle à la simulation
           // paramRules.add(amp1);
                        
                        //paramRules.remove(context.getValue("lastAMP2"));
                        //context.setValue("lastAMP2", amp2);
            //paramRules.add(amp2);
                        /////////////////* à completer avec les autres amp */
            
                        
                                /// modif les parametres dans la base de données 

            TopiaContext tx = nextSimulation.getStorage().beginTransaction();
            /// On récupère les objets ISIS a modifier dans la base de données
            PopulationDAO popDAO = IsisFishDAOHelper.getPopulationDAO(tx);                        
            Population pop = popDAO.findByName("CSar");
            List<PopulationGroup> groups = pop.getPopulationGroup();
              GearDAO gearDAO = IsisFishDAOHelper.getGearDAO(tx);
            List<Gear> gears = gearDAO.findAll();

                



                                        ///modif fecondité et  borne des classes de taille
                                        /// On remplace les bornes par les éléments de la liste

            for (PopulationGroup group : groups) {
				group.setReproductionRate(group.getReproductionRate() * reproductionRate);
                
				group.setMinLength(croissance[group.getId()]) ;
                group.setMaxLength(croissance[group.getId()+1]) ;
            }
            
               // modif du coeff de migration de reproduction

			List<PopulationSeasonInfo> saison = pop.getPopulationSeasonInfo();
			for(PopulationSeasonInfo sea : saison){            
								
					MatrixND mig = sea.getMigrationMatrix();
					for (MatrixIterator i = mig.iterator(); i.hasNext();){
					i.next();
			Object [] sem = i.getSemanticsCoordinates();
			PopulationGroup group = (PopulationGroup)sem[0];
			Zone Zdep = (Zone)sem[1];
			Zone Zarr = (Zone)sem[2];
						
		double multi = 0;
           	if (migration == 0.8) {multi = 1 ;
			}else if(migration == 1.2){multi = -1 ;				
			}	

			if (sea.getFirstMonth().equals(Month.APRIL)){	
				if (group.getId() > 3){
			
				if (Zdep.getName().equals("Spresence")){
						
							if (Zarr.getName().equals("RreproMreserve")){
								i.setValue(i.getValue()*migration);

							}else if (Zarr.getName().equals("Srepro")){		
							double v = i.getValue()+ multi*0.0051614 ;
								if (v > 0){
								i.setValue(v);
								}else {
								i.setValue(0);
								} 			
		
							} 
					}
				
			
		if (Zdep.getName().equals("RpresenceMreserve")){
					
							if (Zarr.getName().equals("Srepro")){
								i.setValue(i.getValue()*migration);
							
							}else if (Zarr.getName().equals("RreproMreserve")) {		
							double v = i.getValue()+ multi*0.02105263 ;
								if (v > 0){
								i.setValue(v);
								}else {
								i.setValue(0);
								} 			
			              }else if (Zarr.getName().equals("RPrepro")) { 							
                          double v = i.getValue()+ multi*0.02105263;
								if (v > 0){
								i.setValue(v);
								}else {
								i.setValue(0);
								}
							}
							} 
						
					
			if (Zdep.getName().equals("RI")){
						
							if (Zarr.getName().equals("RPrepro")){
								i.setValue(i.getValue()*migration);
							
							}else if (Zarr.getName().equals("RreproMreserve")){		
							double v = i.getValue()+ multi*0.1 ;
								if (v > 0){
								i.setValue(v);
								}else {
								i.setValue(0);
								} 			
			
							}
						}
					
			if (Zdep.getName().equals("RPjeunes")){
						
							if (Zarr.getName().equals("RPrepro")){
								i.setValue(i.getValue()*migration);
							
							}else if (Zarr.getName().equals("RreproMreserve")){		
							double v = i.getValue()+ multi*0.06 ;
								if (v > 0){
								i.setValue(v);
								}else {
								i.setValue(0);
								} 			
			
							}
						}
				}
 							// modif du coeff de migration de retour vers les zones de presence en mai
						 if (sea.getFirstMonth().equals(Month.MAY)){
							if (group.getId() > 3){
				if (Zdep.getName().equals("Srepro")){
					
							if (Zarr.getName().equals("Spresence")){
								i.setValue(i.getValue()*migration);
							
							}else if (Zarr.getName().equals("RpresenceMreserve")){		
							double v = i.getValue()+ multi*0.14 ;
								if (v > 0){
								i.setValue(v);
								}else {
								i.setValue(0);
								} 
							}
					}
										
		if (Zdep.getName().equals("RreproMreserve")){
						
							if (Zarr.getName().equals("RpresenceMreserve")){
								i.setValue(i.getValue()*migration);
							
							}else if (Zarr.getName().equals("Spresence")){		
							double v = i.getValue()+ multi*0.15384614 ;
								if (v > 0){
								i.setValue(v);
								}else {
								i.setValue(0);
								} 				
					}
				}
			
			if (Zdep.getName().equals("RPrepro")){
						
							if (Zarr.getName().equals("RpresenceMreserve")){
								i.setValue(i.getValue()*migration);
							
							}else if (Zarr.getName().equals("RI")) {		
							double v = i.getValue()+ multi*0.02222 ;
								if (v > 0){
								i.setValue(v);
								}else {
								i.setValue(0);
								} 				
					}else if (Zarr.getName().equals("RP")) {		
							double v = i.getValue()+ multi*0.02222 ;
								if (v > 0){
								i.setValue(v);
								}else {
								i.setValue(0);
								} 				
					}
				}
			}
		}
	
	

			// modif du coeff de migration de Novembre-Fevrier
		 if (sea.getFirstMonth().equals(Month.NOVEMBER)){
			if (group.getId() > 2){
			if (Zdep.getName().equals("Spresence")){
						
							if (Zarr.getName().equals("RpresenceMreserve")){
								i.setValue(i.getValue()*migration );
							
						}
				}
			}
		
		}		
				// modif du coeff de migration SLagune
		
		 if (sea.getFirstMonth().equals(Month.SEPTEMBER)){
		if (group.getId() == 1){	
		if (Zdep.getName().equals("SLagune")){
						
							if (Zarr.getName().equals("Sjeunes")){
								i.setValue(i.getValue()*migration + multi*0.14);
							
						        }
							}
					   }
			      }
	          }
          }
      }


                                        // modif la mortalité naturelle
                                        /// On modifie la valeur de la base en la remplacant par la valeur contenue dans la modalité
                  
            for (PopulationGroup group : groups) {
                 Equation eq = pop.getNaturalDeathRate();
                 eq.setContent(mortality);
            }                             
                                      
									  // modif la capturabilité
                                        /// On modifie la valeur de la base en la multipliant par la valeur contenue dans la modalité
           MatrixND c = pop.getCapturability();
		        for (MatrixIterator i = c.iterator(); i.hasNext();){
			      i.next();
        		i.setValue(i.getValue()* catchability);                                        
}       
      
         				/// modif selectivité et facteur de standardisation
                                        
            for (Gear gear : gears) {

                if ("FiletMer".equals(gear.getName())) {
                    Selectivity sel = gear.getPopulationSelectivity(pop);
                    Equation eq = sel.getEquation();                
                    eq.setContent(selectivityFiletMer);
					gear.setStandardisationFactor(gear.getStandardisationFactor()*standardisation);
                } else  if ("FiletEtang".equals(gear.getName())) {
                    Selectivity sel = gear.getPopulationSelectivity(pop);
                    Equation eq = sel.getEquation();                
                    eq.setContent(selectivityFiletEtang);
					gear.setStandardisationFactor(gear.getStandardisationFactor()*standardisation);
                }else  if ("Lignecote".equals(gear.getName())) {
                    Selectivity sel = gear.getPopulationSelectivity(pop);
                    Equation eq = sel.getEquation();                
                    eq.setContent(selectivityLignecote);
					gear.setStandardisationFactor(gear.getStandardisationFactor()*standardisation);
                }else  if ("Ligneemb".equals(gear.getName())) {
                    Selectivity sel = gear.getPopulationSelectivity(pop);
                    Equation eq = sel.getEquation();                
                    eq.setContent(selectivityLigneemb);
					gear.setStandardisationFactor(gear.getStandardisationFactor()*standardisation);
                }else  if ("Chalut".equals(gear.getName())) {
                    Selectivity sel = gear.getPopulationSelectivity(pop);
                    Equation eq = sel.getEquation();                
                    eq.setContent(selectivityChalut);
					gear.setStandardisationFactor(gear.getStandardisationFactor()*standardisation);
                }else  if ("Harpon".equals(gear.getName())) {
                    Selectivity sel = gear.getPopulationSelectivity(pop); 
                    Equation eq = sel.getEquation();                
                    eq.setContent(selectivityHarpon);
					gear.setStandardisationFactor(gear.getStandardisationFactor()*standardisation);
                }  
			}
 							
         
      				 // modif le coeff de ciblage

            MetierDAO metierDAO = IsisFishDAOHelper.getMetierDAO(tx);
            List<Metier> metiers = metierDAO.findAll();
            for(Metier metier : metiers){
			
//log.info("metier = " + metier.getName());
            List<MetierSeasonInfo> seasons = metier.getMetierSeasonInfo();
              	for(MetierSeasonInfo season : seasons){
			  	 //  System.out.println("metier :" +metier.getName());
   				//System.out.println("season : " + season +"ancien target : " + target.getContent());
			Equation target = season.getSpeciesTargetSpecies(pop.getSpecies()).getTargetFactorEquation();
  
    
//log.info("ancien target : " + target.getContent());            	
String [] eqt = target.getContent().split("return ");

//log.info("eqt0 : " + eqt[0]);
//log.info("eqt1 : " + eqt[1]);
String [] coeff = new String [] {"double mult=" + targetFactor + "; return "};
String coef = (String) coeff[0];
//log.info("coef" + coef);
String [] coeff2 = new String [] {coef + eqt[1] } ;
String coef2 = (String) coeff2[0] ;
//log.info("coef2" + coef2);
target.setContent(coef2);
	}

    }      

                                                    
		 							// modif le nb de jours d inactivité
										//log.info("INACTIVITY");

            StrategyDAO strategyDAO = IsisFishDAOHelper.getStrategyDAO(tx) ;
            List<Strategy> strategies = strategyDAO.findAll();
            for (Strategy strategy : strategies) {
            List <StrategyMonthInfo>  monthInfos = strategy.getStrategyMonthInfo();
              for (StrategyMonthInfo month : monthInfos) {
			  double days = month.getMinInactivityDays()*effort;
				int Days = (int) days ; 
//log.info("nb j inact = " + Days) ;
				month.setMinInactivityDays(Days);
			  }               
            }
                                     
            tx.commitTransaction();
            return true;

        }else {
            return false;
        }
    }


 
    /**
     * 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 afterSimulation(AnalysePlanContext context, SimulationStorage lastSimulation) throws Exception {
        return true;
	}

}
