/*
 * #%L
 * IsisFish data
 * %%
 * Copyright (C) 2006 - 2012 Ifremer, Code Lutin, Cédric Pineau, Benjamin Poussin, Chatellier Eric
 * %%
 * 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, see
 * <http://www.gnu.org/licenses/gpl-2.0.html>.
 * #L%
 */

package scripts;

import static org.nuiton.i18n.I18n.n_;

import java.util.Collection;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuiton.math.matrix.MatrixFactory;
import org.nuiton.math.matrix.MatrixND;
import org.nuiton.topia.TopiaContext;
import org.nuiton.topia.TopiaException;

import fr.ifremer.isisfish.datastore.ResultStorage;
import fr.ifremer.isisfish.datastore.StorageException;
import fr.ifremer.isisfish.entities.EffortDescription;
import fr.ifremer.isisfish.entities.Metier;
import fr.ifremer.isisfish.entities.Population;
import fr.ifremer.isisfish.entities.PopulationGroup;
import fr.ifremer.isisfish.entities.SetOfVessels;
import fr.ifremer.isisfish.entities.Strategy;
import fr.ifremer.isisfish.entities.Zone;
import fr.ifremer.isisfish.simulator.SimulationContext;
import fr.ifremer.isisfish.types.TimeStep;
import fr.ifremer.isisfish.types.Month;
import fr.ifremer.isisfish.types.TimeUnit;

/**
 * Gravity model calcul des resultats à basés sur des resultats provenant
 * de SiMatrix ou des resultat précédement calculé disponible via le
 * result manager.
 * 
 * Cette classe sert a calculer des résultats economique (prix, resultat
 * servant a optimiser des cout pour les pécheurs...).
 * 
 * @author poussin
 */
public class GravityModel {

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

    protected SimulationContext context = null;
    protected TopiaContext db = null;
    protected SiMatrix siMatrix = null;
    protected ResultStorage resultManager = null;

    /**
     * Method used to get GravityModel used for simulation
     * 
     * @param context
     *            context simulation
     * @return GravityModel or null if no GravityModel created for simulation
     */
    public static GravityModel getGravityModel(SimulationContext context) {
        GravityModel result = (GravityModel) context
                .getValue(GravityModel.class.getName());
        return result;
    }

    private static void setGravityModel(SimulationContext context,
            GravityModel gravityModel) {
        context.setValue(GravityModel.class.getName(), gravityModel);
    }

    public GravityModel(SimulationContext context, SiMatrix siMatrix)
            throws TopiaException, StorageException {
        this.context = context;
        this.db = context.getDB();
        this.resultManager = context.getSimulationStorage().getResultStorage();
        this.siMatrix = siMatrix;
        setGravityModel(context, this);
    }

    //////////////////////////////////////////////////////////////////////
    // Matrice de Revenue et Cout
    //////////////////////////////////////////////////////////////////////

    //////////////////////////////////////////////////////////////////////
    // MatrixFishingTimePerMonthPerVessel
    //////////////////////////////////////////////////////////////////////

    public MatrixND matrixFishingTimePerMonthPerVessel(TimeStep step)
            throws TopiaException {
        List<Strategy> strategies = siMatrix.getStrategies(step);
        List<Metier> metiers = siMatrix.getMetiers(step);

        MatrixND result = MatrixFactory.getInstance().create(
                ResultName.MATRIX_FISHING_TIME_PER_MONTH_PER_VESSEL,
                new List[] { strategies, metiers },
                new String[] { n_("Strategies"), n_("Metiers") });

        for (int s = 0; s < strategies.size(); s++) {
            Strategy str = strategies.get(s);
            metiers = siMatrix.getMetiers(str, step);
            for (int m = 0; m < metiers.size(); m++) {
                Metier metier = metiers.get(m);
                double value = fishingTimePerMonthPerVessel(str, metier, step);
                result.setValue(str, metier, value);
            }
        }

        return result;
    }

    /**
     * implante suivant document ModifTable3PourBP25-07-2006.doc
     * FishingTimePerMonthPerVessel[str,met,month] =
     * FishingTimePerTrip[str,met,month]NbTripsPerMonth[str,month]
     * 
     * @param str
     * @param metier
     * @param step
     * @return
     */
    private double fishingTimePerMonthPerVessel(Strategy str, Metier metier,
            TimeStep step) {
        Month month = step.getMonth();
        double timePerTrip = siMatrix.fishingTimePerTrip(str, metier, step);
        double nbTrip = str.getStrategyMonthInfo(month).getNumberOfTrips();
        return timePerTrip * nbTrip;
    }

    //////////////////////////////////////////////////////////////////////
    // matrixFuelCostsOfTravelPerVessel
    //////////////////////////////////////////////////////////////////////

    public MatrixND matrixFuelCostsOfTravelPerVessel(TimeStep step)
            throws TopiaException {
        List<Strategy> strategies = siMatrix.getStrategies(step);
        List<Metier> metiers = siMatrix.getMetiers(step);

        MatrixND result = MatrixFactory.getInstance().create(
                ResultName.MATRIX_FUEL_COSTS_OF_TRAVEL_PER_VESSEL,
                new List[] { strategies, metiers },
                new String[] { n_("Strategies"), n_("Metiers") });

        for (int s = 0; s < strategies.size(); s++) {
            Strategy str = strategies.get(s);
            metiers = siMatrix.getMetiers(str, step);
            for (int m = 0; m < metiers.size(); m++) {
                Metier metier = metiers.get(m);
                double value = fuelCostsOfTravelPerVessel(str, metier, step);
                result.setValue(str, metier, value);
            }
        }

        return result;
    }

    /**
     * implante suivant document ModifTable3PourBP25-07-2006.doc
     * FuelCostsOfTravelPerVessel[sov,met,month] =
     * NbTripsPerMonth[str,month]*TravelTimePerTrip[sov,met,month]*UnitFuelCostsOfTravel[vt]
     * 
     * @param str
     * @param metier
     * @param step
     * @return
     */
    private double fuelCostsOfTravelPerVessel(Strategy str, Metier metier,
            TimeStep step) {
        Month month = step.getMonth();
        double nbTrip = str.getStrategyMonthInfo(month).getNumberOfTrips();

        Collection<Zone> zone = metier.getMetierSeasonInfo(month).getZone();
        // TODO: verifier que travelTime est bien en heure, car le unitFuelCost est l'unite par heure
        double travelTime = siMatrix.travelTimePerTrip(str.getSetOfVessels(),
                zone);
        double unitFuelCost = str.getSetOfVessels().getVesselType()
                .getUnitFuelCostOfTravel();

        return nbTrip * travelTime * unitFuelCost;
    }

    //////////////////////////////////////////////////////////////////////
    // matrixCostsOfFishingPerVessel
    //////////////////////////////////////////////////////////////////////

    public MatrixND matrixCostsOfFishingPerVessel(TimeStep step)
            throws TopiaException {
        List<Strategy> strategies = siMatrix.getStrategies(step);
        List<Metier> metiers = siMatrix.getMetiers(step);

        MatrixND result = MatrixFactory.getInstance().create(
                ResultName.MATRIX_COSTS_OF_FISHING_PER_VESSEL,
                new List[] { strategies, metiers },
                new String[] { n_("Strategies"), n_("Metiers") });

        for (int s = 0; s < strategies.size(); s++) {
            Strategy str = strategies.get(s);
            metiers = siMatrix.getMetiers(str, step);
            for (int m = 0; m < metiers.size(); m++) {
                Metier metier = metiers.get(m);
                double value = costsOfFishingPerVessel(str, metier, step);
                result.setValue(str, metier, value);
            }
        }

        return result;
    }

    /**
     * implante suivant document ModifTable3PourBP25-07-2006.doc
     * CostsOfFishingPerVessel[str,met,month]= FishingTimePerMonthPerVessel
     * [str,met,month] {NbFishingOperationsPerDay[sov,met]
     * UnitCostsOfFishing[sov,met] / 24}
     * 
     * @param str
     * @param metier
     * @param step
     * @return
     */
    private double costsOfFishingPerVessel(Strategy str, Metier metier,
            TimeStep step) {
        double fishingTime = fishingTimePerMonthPerVessel(str, metier, step);

        EffortDescription effort = str.getSetOfVessels().getPossibleMetiers(
                metier);
        int nbOperation = 0;
        double unitCostOfFishing = 0;
        if (effort != null) {
            nbOperation = effort.getFishingOperation();
            unitCostOfFishing = effort.getUnitCostOfFishing();
        }

        return fishingTime
                * (nbOperation * unitCostOfFishing / TimeUnit.HOUR_PER_DAY);
    }

    //////////////////////////////////////////////////////////////////////
    // matrixFuelCostsPerVessel
    //////////////////////////////////////////////////////////////////////

    public MatrixND matrixFuelCostsPerVessel(TimeStep step) throws TopiaException {
        List<Strategy> strategies = siMatrix.getStrategies(step);
        List<Metier> metiers = siMatrix.getMetiers(step);

        MatrixND result = MatrixFactory.getInstance().create(
                ResultName.MATRIX_FUEL_COSTS_PER_VESSEL,
                new List[] { strategies, metiers },
                new String[] { n_("Strategies"), n_("Metiers") });

        for (int s = 0; s < strategies.size(); s++) {
            Strategy str = strategies.get(s);
            metiers = siMatrix.getMetiers(str, step);
            for (int m = 0; m < metiers.size(); m++) {
                Metier metier = metiers.get(m);
                double value = fuelCostsPerVessel(str, metier, step);
                result.setValue(str, metier, value);
            }
        }

        return result;
    }

    /**
     * implante suivant document ModifTable3PourBP25-07-2006.doc
     * FuelCostsPerVessel[str,met,month] = FuelCostsOfTravelPerVessel
     * [sov,met,month] + CostsOfFishingPerVessel [str,met,month]
     * 
     * @param str
     * @param metier
     * @param step
     * @return
     */
    private double fuelCostsPerVessel(Strategy str, Metier metier, TimeStep step) {
        double fuelCosts = fuelCostsOfTravelPerVessel(str, metier, step);
        double costsOfFishing = costsOfFishingPerVessel(str, metier, step);

        return fuelCosts + costsOfFishing;
    }

    //////////////////////////////////////////////////////////////////////
    // matrixRepairAndMaintenanceGearCostsPerVessel
    //////////////////////////////////////////////////////////////////////

    public MatrixND matrixRepairAndMaintenanceGearCostsPerVessel(TimeStep step)
            throws TopiaException {
        List<Strategy> strategies = siMatrix.getStrategies(step);
        List<Metier> metiers = siMatrix.getMetiers(step);

        MatrixND result = MatrixFactory.getInstance().create(
                ResultName.MATRIX_REPAIR_AND_MAINTENANCE_GEAR_COSTS_PER_VESSEL,
                new List[] { strategies, metiers },
                new String[] { n_("Strategies"), n_("Metiers") });

        for (int s = 0; s < strategies.size(); s++) {
            Strategy str = strategies.get(s);
            metiers = siMatrix.getMetiers(str, step);
            for (int m = 0; m < metiers.size(); m++) {
                Metier metier = metiers.get(m);
                double value = repairAndMaintenanceGearCostsPerVessel(str,
                        metier, step);
                result.setValue(str, metier, value);
            }
        }
        //        for(Strategy str : strategies){
        //            metiers = siMatrix.getMetiers(str, step);
        //            for(Metier metier : metiers) {
        //                double value = repairAndMaintenanceGearCostsPerVessel(str, metier, step);
        //                result.setValue(str, metier, value);
        //            }
        //        }

        return result;
    }

    /**
     * implante suivant document ModifTable3PourBP25-07-2006.doc
     * RepairAndMaintenanceGearCostsPerVessel[str,met,month] =
     * FishingTimePerMonthPerVessel[str,met,month]
     * *RepairAndMaintenanceGearCostsPerDay[sov,met]/NbHoursPerDay
     * 
     * @param str
     * @param metier
     * @param step
     * @return
     */
    private double repairAndMaintenanceGearCostsPerVessel(Strategy str,
            Metier metier, TimeStep step) {
        double fishingTime = fishingTimePerMonthPerVessel(str, metier, step);

        EffortDescription effort = str.getSetOfVessels().getPossibleMetiers(
                metier);

        double repair = 0;
        if (effort != null) {
            repair = effort.getRepairAndMaintenanceGearCost();
        }
        // FIXME verifier qu'il faut bien retourner 0, si pas d'effort
        return fishingTime * repair / TimeUnit.HOUR_PER_DAY;
    }

    //////////////////////////////////////////////////////////////////////
    // matrixOtherRunningCostsPerVessel
    //////////////////////////////////////////////////////////////////////

    public MatrixND matrixOtherRunningCostsPerVessel(TimeStep step)
            throws TopiaException {
        List<Strategy> strategies = siMatrix.getStrategies(step);
        List<Metier> metiers = siMatrix.getMetiers(step);

        MatrixND result = MatrixFactory.getInstance().create(
                ResultName.MATRIX_OTHER_RUNNING_COSTS_PER_VESSEL,
                new List[] { strategies, metiers },
                new String[] { n_("Strategies"), n_("Metiers") });

        for (int s = 0; s < strategies.size(); s++) {
            Strategy str = strategies.get(s);
            metiers = siMatrix.getMetiers(str, step);
            for (int m = 0; m < metiers.size(); m++) {
                Metier metier = metiers.get(m);
                double value = otherRunningCostsPerVessel(str, metier, step);
                result.setValue(str, metier, value);
            }
        }

        return result;
    }

    /**
     * implante suivant document ModifTable3PourBP25-07-2006.doc
     * OtherRunningCostsPerVessel[str,met,month] =
     * FishingTimePerMonthPerVessel[str,met,month]*OtherRunningCostsPerDay[sov,met]/
     * NbHoursPerDay
     * 
     * @param str
     * @param metier
     * @param step
     * @return
     */
    private double otherRunningCostsPerVessel(Strategy str, Metier metier,
            TimeStep step) {
        double fishingTime = fishingTimePerMonthPerVessel(str, metier, step);

        EffortDescription effort = str.getSetOfVessels().getPossibleMetiers(
                metier);

        double otherCosts = 0;
        if (effort != null) {
            otherCosts = effort.getOtherRunningCost();
        }
        // FIXME verifier qu'il faut bien retourner 0, si pas d'effort
        return fishingTime * otherCosts / TimeUnit.HOUR_PER_DAY;
    }

    //////////////////////////////////////////////////////////////////////
    // matrixSharedNotFixedCostsPerVessel
    //////////////////////////////////////////////////////////////////////

    public MatrixND matrixSharedNotFixedCostsPerVessel(TimeStep step)
            throws TopiaException {
        List<Strategy> strategies = siMatrix.getStrategies(step);
        List<Metier> metiers = siMatrix.getMetiers(step);

        MatrixND result = MatrixFactory.getInstance().create(
                ResultName.MATRIX_SHARED_NOT_FIXED_COSTS_PER_VESSEL,
                new List[] { strategies, metiers },
                new String[] { n_("Strategies"), n_("Metiers") });

        for (int s = 0; s < strategies.size(); s++) {
            Strategy str = strategies.get(s);
            metiers = siMatrix.getMetiers(str, step);
            for (int m = 0; m < metiers.size(); m++) {
                Metier metier = metiers.get(m);
                double value = sharedNotFixedCostsPerVessel(str, metier, step);
                result.setValue(str, metier, value);
            }
        }

        return result;
    }

    /**
     * implante suivant document ModifTable3PourBP25-07-2006.doc
     * SharedNotFixedCostsPerVessel[str,met,month]=FuelCostsPerVessel[str,met,month]
     * +OtherRunningCostsPerVessel[str,met,month]
     * 
     * @param str
     * @param metier
     * @param step
     * @return
     */
    private double sharedNotFixedCostsPerVessel(Strategy str, Metier metier,
            TimeStep step) {
        double fuelCostsPerVessel = fuelCostsPerVessel(str, metier, step);
        double otherRunningCostsPerVessel = otherRunningCostsPerVessel(str,
                metier, step);

        return fuelCostsPerVessel + otherRunningCostsPerVessel;
    }

    //////////////////////////////////////////////////////////////////////
    // matrixGrossValueOfLandingsPerSpeciesPerStrategyMet
    //////////////////////////////////////////////////////////////////////

    public MatrixND matrixGrossValueOfLandingsPerSpeciesPerStrategyMet(TimeStep step)
            throws TopiaException {
        List<Strategy> strategies = siMatrix.getStrategies(step);
        List<Metier> metiers = siMatrix.getMetiers(step);
        List<Population> pops = siMatrix.getPopulations(step);

        MatrixND result = MatrixFactory
                .getInstance()
                .create(
                        ResultName.MATRIX_GROSS_VALUE_OF_LANDINGS_PER_SPECIES_PER_STRATEGY_MET,
                        new List[] { strategies, metiers, pops },
                        new String[] { n_("Strategies"), n_("Metiers"),
                                n_("Populations") });

        for (int p = 0; p < pops.size(); p++) {
            Population pop = pops.get(p);
            for (int s = 0; s < strategies.size(); s++) {
                Strategy str = strategies.get(s);
                metiers = siMatrix.getMetiers(str, step);
                for (int m = 0; m < metiers.size(); m++) {
                    Metier metier = metiers.get(m);
                    double value = grossValueOfLandingsPerSpeciesPerStrategyMet(
                            str, metier, pop, step);
                    result.setValue(str, metier, pop, value);
                }
            }
        }

        return result;
    }

    /**
     * implante suivant document ModifTable3PourBP25-07-2006.doc
     * GrossValueOfLandingsPerSpeciesPerStrategyMet[str,met,pop,month]=sum over
     * classes_cl of [PricePerKg(pop,cl, t)* (CatchWeightPerStrategyMet
     * [str,met,pop,cl,month] ?DiscardsWeightPerStrategyMet [str,met,pop,cl,mo
     * nth] GrossValueOfLandingsPerSpeciesPerStrategyMet[str,met,pop,month] =
     * sum over classes_cl of [PricePerKg(pop,cl, t)* (CatchWeightPerStrategyMet
     * [str,met,pop,cl,month] -DiscardsWeightPerStrategyMet [str,met,pop,cl,mo
     * nth])]
     * 
     * @param str
     * @param metier
     * @param pop
     * @param step
     * @return
     */
    private double grossValueOfLandingsPerSpeciesPerStrategyMet(Strategy str,
            Metier metier, Population pop, TimeStep step) {
        List<PopulationGroup> groups = pop.getPopulationGroup();
        double result = 0;
        for (PopulationGroup group : groups) {
            double price = group.getPrice();
            Collection<Zone> zones = pop.getPopulationZone();
            for (Zone zone : zones) {
                double catchWeight = getCatchWeightPerStrMetPerZonePop(str,
                        metier, group, zone, step);
                double discardsWeight = getDiscardsWeightPerStrMet(str, metier,
                        group, zone, step);
                // FIXME demander/verifier que ce le bon calcule
                result += price * (catchWeight - discardsWeight);
            }
        }
        return result;
    }

    /**
     * @param str
     * @param metier
     * @param group
     * @param zone
     * @param step
     * @return
     */
    private double getCatchWeightPerStrMetPerZonePop(Strategy str,
            Metier metier, PopulationGroup group, Zone zone, TimeStep step) {
        MatrixND mat = resultManager.getMatrix(step, group.getPopulation(),
                ResultName.MATRIX_CATCH_WEIGHT_PER_STRATEGY_MET_PER_ZONE_POP);
        double result = 0;
        if (mat != null) {
            result = mat.getValue(str, metier, group, zone);
        }
        return result;
    }

    /**
     * @param str
     * @param metier
     * @param group
     * @param zone
     * @param step
     * @return
     */
    private double getDiscardsWeightPerStrMet(Strategy str, Metier metier,
            PopulationGroup group, Zone zone, TimeStep step) {
        MatrixND mat = resultManager.getMatrix(step, group.getPopulation(),
                ResultName.MATRIX_DISCARDS_WEIGHT_PER_STR_MET_PER_ZONE_POP);
        double result = 0;
        if (mat != null) {
            result = mat.getValue(str, metier, group, zone);
        }
        return result;
    }

    //////////////////////////////////////////////////////////////////////
    // matrixGrossValueOfLandingsPerStrategyMet
    //////////////////////////////////////////////////////////////////////

    public MatrixND matrixGrossValueOfLandingsPerStrategyMet(TimeStep step)
            throws TopiaException {
        List<Strategy> strategies = siMatrix.getStrategies(step);
        List<Metier> metiers = siMatrix.getMetiers(step);

        MatrixND result = MatrixFactory.getInstance().create(
                ResultName.MATRIX_GROSS_VALUE_OF_LANDINGS_PER_STRATEGY_MET,
                new List[] { strategies, metiers },
                new String[] { n_("Strategies"), n_("Metiers") });

        for (int s = 0; s < strategies.size(); s++) {
            Strategy str = strategies.get(s);
            metiers = siMatrix.getMetiers(str, step);
            for (int m = 0; m < metiers.size(); m++) {
                Metier metier = metiers.get(m);
                double value = grossValueOfLandingsPerStrategyMet(str, metier,
                        step);
                result.setValue(str, metier, value);
            }
        }

        return result;
    }

    /**
     * @param str
     * @param metier
     * @param step
     * @return
     * @throws TopiaException
     */
    private double grossValueOfLandingsPerStrategyMet(Strategy str,
            Metier metier, TimeStep step) throws TopiaException {
        List<Population> pops = siMatrix.getPopulations(step);
        double result = 0;
        for (int i = 0; i < pops.size(); i++) {
            Population pop = (Population) pops.get(i);
            result += grossValueOfLandingsPerSpeciesPerStrategyMet(str, metier,
                    pop, step);
        }
        return result;
    }

    //////////////////////////////////////////////////////////////////////
    // matrixGrossValueOfLandingsPerStrategyMetPerVessel
    //////////////////////////////////////////////////////////////////////

    public MatrixND matrixGrossValueOfLandingsPerStrategyMetPerVessel(TimeStep step)
            throws TopiaException {
        List<Strategy> strategies = siMatrix.getStrategies(step);
        List<Metier> metiers = siMatrix.getMetiers(step);

        MatrixND result = MatrixFactory
                .getInstance()
                .create(
                        ResultName.MATRIX_GROSS_VALUE_OF_LANDINGS_PER_STRATEGY_MET_PER_VESSEL,
                        new List[] { strategies, metiers },
                        new String[] { n_("Strategies"), n_("Metiers") });

        for (int s = 0; s < strategies.size(); s++) {
            Strategy str = strategies.get(s);
            metiers = siMatrix.getMetiers(str, step);
            for (int m = 0; m < metiers.size(); m++) {
                Metier metier = metiers.get(m);
                double value = grossValueOfLandingsPerStrategyMetPerVessel(str,
                        metier, step);
                result.setValue(str, metier, value);
            }
        }

        return result;
    }

    /**
     * implante suivant document ModifTable3PourBP25-07-2006.doc
     * GrossValueOfLandingsPerStrategyMetPerVessel[str,met,month]=
     * GrossValueOfLandingsPerStrategyMet[str,met,month]/[PropNbVessels(str,sov)*NbVesselsSetOfVessels(sov)]
     * 
     * @param str
     * @param metier
     * @param step
     * @return
     * @throws TopiaException
     */
    private double grossValueOfLandingsPerStrategyMetPerVessel(Strategy str,
            Metier metier, TimeStep step) throws TopiaException {
        double grossValueOfLandingsPerStrategyMet = grossValueOfLandingsPerStrategyMet(
                str, metier, step);
        // FIXME verifier que c bien cette donn�e qu'il faut utiliser dans le doc: PropNbVessels(str, sov)
        double proportionSetOfVessels = str.getProportionSetOfVessels();
        double numberOfVessels = str.getSetOfVessels().getNumberOfVessels();

        double grossValueOfLandingsOtherSpeciesPerStrategyMet = grossValueOfLandingsOtherSpeciesPerStrategyMet(
                str, metier, step);

        return (grossValueOfLandingsPerStrategyMet + grossValueOfLandingsOtherSpeciesPerStrategyMet)
                / (proportionSetOfVessels * numberOfVessels);
    }

    //////////////////////////////////////////////////////////////////////
    // matrixNetValueOfLandingsPerStrategyMet
    //////////////////////////////////////////////////////////////////////

    public MatrixND matrixNetValueOfLandingsPerStrategyMet(TimeStep step)
            throws TopiaException {
        List<Strategy> strategies = siMatrix.getStrategies(step);
        List<Metier> metiers = siMatrix.getMetiers(step);

        MatrixND result = MatrixFactory.getInstance().create(
                ResultName.MATRIX_NET_VALUE_OF_LANDINGS_PER_STRATEGY_MET,
                new List[] { strategies, metiers },
                new String[] { n_("Strategies"), n_("Metiers") });

        for (int s = 0; s < strategies.size(); s++) {
            Strategy str = strategies.get(s);
            metiers = siMatrix.getMetiers(str, step);
            for (int m = 0; m < metiers.size(); m++) {
                Metier metier = metiers.get(m);
                double value = netValueOfLandingsPerStrategyMet(str, metier,
                        step);
                result.setValue(str, metier, value);
            }
        }

        return result;
    }

    /**
     * implante suivant document ModifTable3PourBP25-07-2006.doc
     * NetValueOfLandingsPerStrategyMet[str,met,month] =
     * GrossValueOfLandingsPerStrategyMet[str,met,month]
     * (1-LandingCostRate[str,met])
     * 
     * @param str
     * @param metier
     * @param step
     * @return
     * @throws TopiaException
     */
    private double netValueOfLandingsPerStrategyMet(Strategy str,
            Metier metier, TimeStep step) throws TopiaException {
        double grossValue = grossValueOfLandingsPerStrategyMet(str, metier,
                step);
        EffortDescription effort = str.getSetOfVessels().getPossibleMetiers(
                metier);
        double landingCost = 1;
        // FIXME demander si le metier n'a pas d'effort s'il faut que landingCost soit bien a 1
        if (effort != null) {
            landingCost -= effort.getLandingCosts();
        }

        double grossValueOfLandingsOtherSpeciesPerStrategyMet = grossValueOfLandingsOtherSpeciesPerStrategyMet(
                str, metier, step);

        double result = (grossValue + grossValueOfLandingsOtherSpeciesPerStrategyMet)
                * landingCost;
        return result;
    }

    private double grossValueOfLandingsOtherSpeciesPerStrategyMet(Strategy str,
            Metier metier, TimeStep step) throws TopiaException {
        // FIXME evaluer l'equation dans suivant le doc des equations
        return 0;
    }

    //////////////////////////////////////////////////////////////////////
    // matrixNetValueOfLandingsPerStrategyMetPerVessel
    //////////////////////////////////////////////////////////////////////

    public MatrixND matrixNetValueOfLandingsPerStrategyMetPerVessel(TimeStep step)
            throws TopiaException {
        List<Strategy> strategies = siMatrix.getStrategies(step);
        List<Metier> metiers = siMatrix.getMetiers(step);

        MatrixND result = MatrixFactory
                .getInstance()
                .create(
                        ResultName.MATRIX_NET_VALUE_OF_LANDINGS_PER_STRATEGY_MET_PER_VESSEL,
                        new List[] { strategies, metiers },
                        new String[] { n_("Strategies"), n_("Metiers") });

        for (int s = 0; s < strategies.size(); s++) {
            Strategy str = strategies.get(s);
            metiers = siMatrix.getMetiers(str, step);
            for (int m = 0; m < metiers.size(); m++) {
                Metier metier = metiers.get(m);
                double value = netValueOfLandingsPerStrategyMetPerVessel(str,
                        metier, step);
                result.setValue(str, metier, value);
            }
        }

        return result;
    }

    /**
     * implante suivant document ModifTable3PourBP25-07-2006.doc
     * NetValueOfLandingsPerStrategyMetPerVessel[str,met,month] =
     * NetValueOfLandingsPerStrategyMet[str,met,month]
     * /[PropNbVessels(str,sov)*NbVesselsSetOfVessels(sov)]
     * 
     * @param str
     * @param metier
     * @param step
     * @return
     * @throws TopiaException
     */
    private double netValueOfLandingsPerStrategyMetPerVessel(Strategy str,
            Metier metier, TimeStep step) throws TopiaException {
        double netValueOfLandingsPerStrategyMet = netValueOfLandingsPerStrategyMet(
                str, metier, step);
        // FIXME verifier que c bien cette donnée qu'il faut utiliser dans le doc: PropNbVessels(str, sov)
        double proportionSetOfVessels = str.getProportionSetOfVessels();
        double numberOfVessels = str.getSetOfVessels().getNumberOfVessels();

        return netValueOfLandingsPerStrategyMet
                / (proportionSetOfVessels * numberOfVessels);
    }

    //////////////////////////////////////////////////////////////////////
    // matrixNetRenevueToSharePerStrategyMetPerVessel
    //////////////////////////////////////////////////////////////////////

    public MatrixND matrixNetRenevueToSharePerStrategyMetPerVessel(TimeStep step)
            throws TopiaException {
        List<Strategy> strategies = siMatrix.getStrategies(step);
        List<Metier> metiers = siMatrix.getMetiers(step);

        MatrixND result = MatrixFactory
                .getInstance()
                .create(
                        ResultName.MATRIX_NET_RENEVUE_TO_SHARE_PER_STRATEGY_MET_PER_VESSEL,
                        new List[] { strategies, metiers },
                        new String[] { n_("Strategies"), n_("Metiers") });

        for (int s = 0; s < strategies.size(); s++) {
            Strategy str = strategies.get(s);
            metiers = siMatrix.getMetiers(str, step);
            for (int m = 0; m < metiers.size(); m++) {
                Metier metier = metiers.get(m);
                double value = netRenevueToSharePerStrategyMetPerVessel(str,
                        metier, step);
                result.setValue(str, metier, value);
            }
        }

        return result;
    }

    /**
     * implante suivant document ModifTable3PourBP25-07-2006.doc
     * NetRevenueToSharePerStrategyMetPerVessel[str,met,month] =
     * NetValueOfLandingsPerStrategyMetPerVessel[str,met,month] -
     * SharedNotFixedCostsPerVessel [str,met,month]*PropStr(str,met,month) -
     * SharedFixedCostsPerVessel PerMet[str,month]
     * 
     * @param str
     * @param metier
     * @param step
     * @return
     * @throws TopiaException
     */
    private double netRenevueToSharePerStrategyMetPerVessel(Strategy str,
            Metier metier, TimeStep step) throws TopiaException {
        double netRenevueToSharePerStrategyMetPerVessel = netValueOfLandingsPerStrategyMetPerVessel(
                str, metier, step);
        double sharedNotFixedCostsPerVessel = sharedNotFixedCostsPerVessel(str,
                metier, step);
        double propStr = str.getStrategyMonthInfo(step.getMonth())
                .getProportionMetier(metier);

        double result = netRenevueToSharePerStrategyMetPerVessel
                - sharedNotFixedCostsPerVessel * propStr;
        return result;
    }

    //////////////////////////////////////////////////////////////////////
    // matrixCrewSharePerStrategyMetPerVessel
    //////////////////////////////////////////////////////////////////////

    public MatrixND matrixCrewSharePerStrategyMetPerVessel(TimeStep step)
            throws TopiaException {
        List<Strategy> strategies = siMatrix.getStrategies(step);
        List<Metier> metiers = siMatrix.getMetiers(step);

        MatrixND result = MatrixFactory.getInstance().create(
                ResultName.MATRIX_CREW_SHARE_PER_STRATEGY_MET_PER_VESSEL,
                new List[] { strategies, metiers },
                new String[] { n_("Strategies"), n_("Metiers") });

        for (int s = 0; s < strategies.size(); s++) {
            Strategy str = strategies.get(s);
            metiers = siMatrix.getMetiers(str, step);
            for (int m = 0; m < metiers.size(); m++) {
                Metier metier = metiers.get(m);
                double value = crewSharePerStrategyMetPerVessel(str, metier,
                        step);
                result.setValue(str, metier, value);
            }
        }

        return result;
    }

    /**
     * implante suivant document ModifTable3PourBP25-07-2006.doc
     * CrewSharePerStrategyMetPerVessel[str,met,month] =
     * NetRevenueToSharePerStrategyMetPerVessel[str,met,month]*CrewShareRate[sov,met]
     * 
     * @param str
     * @param metier
     * @param step
     * @return
     * @throws TopiaException
     */
    private double crewSharePerStrategyMetPerVessel(Strategy str,
            Metier metier, TimeStep step) throws TopiaException {
        double netRenevueToShare = netRenevueToSharePerStrategyMetPerVessel(
                str, metier, step);

        EffortDescription effort = str.getSetOfVessels().getPossibleMetiers(
                metier);

        double crewShareRate = 0;
        if (effort != null) {
            crewShareRate = effort.getCrewShareRate();
        }
        // FIXME verifier qu'il faut bien retourner 0, si pas d'effort
        return netRenevueToShare * crewShareRate;
    }

    //////////////////////////////////////////////////////////////////////
    // matrixOwnerMarginOverVariableCostsPerStrategyMetPerVessel
    //////////////////////////////////////////////////////////////////////

    public MatrixND matrixOwnerMarginOverVariableCostsPerStrategyMetPerVessel(
            TimeStep step) throws TopiaException {
        List<Strategy> strategies = siMatrix.getStrategies(step);
        List<Metier> metiers = siMatrix.getMetiers(step);

        MatrixND result = MatrixFactory
                .getInstance()
                .create(
                        ResultName.MATRIX_OWNER_MARGIN_OVER_VARIABLE_COSTS_PER_STRATEGY_MET_PER_VESSEL,
                        new List[] { strategies, metiers },
                        new String[] { n_("Strategies"), n_("Metiers") });

        for (int s = 0; s < strategies.size(); s++) {
            Strategy str = strategies.get(s);
            metiers = siMatrix.getMetiers(str, step);
            for (int m = 0; m < metiers.size(); m++) {
                Metier metier = metiers.get(m);
                double value = ownerMarginOverVariableCostsPerStrategyMetPerVessel(
                        str, metier, step);
                result.setValue(str, metier, value);
            }
        }

        return result;
    }

    /**
     * implante suivant document ModifTable3PourBP25-07-2006.doc
     * OwnerMarginOverVariableCostsPerStrategyMetPerVessel[str,met,month] =
     * NetRevenueToSharePerStrategyMetPerVessel[str,met,month] -
     * CrewSharePerStrategyMetPerVessel[str,met,month] -
     * RepairAndMaintenanceGearCostsPerVes sel[str,met,month] *
     * PropStr(str,met,month)
     * 
     * @param str
     * @param metier
     * @param step
     * @return
     * @throws TopiaException
     */
    private double ownerMarginOverVariableCostsPerStrategyMetPerVessel(
            Strategy str, Metier metier, TimeStep step) throws TopiaException {
        double netRenevueToShare = netRenevueToSharePerStrategyMetPerVessel(
                str, metier, step);
        double crewShare = crewSharePerStrategyMetPerVessel(str, metier, step);
        double repair = repairAndMaintenanceGearCostsPerVessel(str, metier,
                step);
        double propStr = str.getStrategyMonthInfo(step.getMonth())
                .getProportionMetier(metier);

        return netRenevueToShare - crewShare - repair * propStr;
    }

    //////////////////////////////////////////////////////////////////////
    // matrixVesselMarginOverVariableCostsPerStrategyMetPerVessel
    //////////////////////////////////////////////////////////////////////

    public MatrixND matrixVesselMarginOverVariableCostsPerStrategyMetPerVessel(
            TimeStep step) throws TopiaException {
        List<Strategy> strategies = siMatrix.getStrategies(step);
        List<Metier> metiers = siMatrix.getMetiers(step);

        MatrixND result = MatrixFactory
                .getInstance()
                .create(
                        ResultName.MATRIX_VESSEL_MARGIN_OVER_VARIABLE_COSTS_PER_STRATEGY_MET_PER_VESSEL,
                        new List[] { strategies, metiers },
                        new String[] { n_("Strategies"), n_("Metiers") });

        for (int s = 0; s < strategies.size(); s++) {
            Strategy str = strategies.get(s);
            metiers = siMatrix.getMetiers(str, step);
            for (int m = 0; m < metiers.size(); m++) {
                Metier metier = metiers.get(m);
                double value = vesselMarginOverVariableCostsPerStrategyMetPerVessel(
                        str, metier, step);
                result.setValue(str, metier, value);
            }
        }

        return result;
    }

    /**
     * implante suivant document ModifTable3PourBP25-07-2006.doc
     * VesselMarginOverVariableCostsPerStrategyMetPerVessel [str,met,month]
     * =NetRevenueToSharePerStrategyMetPerVessel[str,met,month]-
     * RepairAndMaintenanceGearCostsPerVessel [str,met,month] *
     * PropStr(str,met,month)
     * 
     * @param str
     * @param metier
     * @param step
     * @return
     * @throws TopiaException
     */
    private double vesselMarginOverVariableCostsPerStrategyMetPerVessel(
            Strategy str, Metier metier, TimeStep step) throws TopiaException {
        double netRenevueToShare = netRenevueToSharePerStrategyMetPerVessel(
                str, metier, step);
        double repair = repairAndMaintenanceGearCostsPerVessel(str, metier,
                step);
        double propStr = str.getStrategyMonthInfo(step.getMonth())
                .getProportionMetier(metier);

        return netRenevueToShare - repair * propStr;
    }

    //////////////////////////////////////////////////////////////////////
    // matrixOwnerMarginOverVariableCostsPerStrategyPerVessel
    //////////////////////////////////////////////////////////////////////

    public MatrixND matrixOwnerMarginOverVariableCostsPerStrategyPerVessel(
            TimeStep step) throws TopiaException {
        List<Strategy> strategies = siMatrix.getStrategies(step);

        MatrixND result = MatrixFactory
                .getInstance()
                .create(
                        ResultName.MATRIX_OWNER_MARGIN_OVER_VARIABLE_COSTS_PER_STRATEGY_PER_VESSEL,
                        new List[] { strategies },
                        new String[] { n_("Strategies") });

        for (int s = 0; s < strategies.size(); s++) {
            Strategy str = strategies.get(s);
            double value = ownerMarginOverVariableCostsPerStrategyPerVessel(
                    str, step);
            result.setValue(s, value);
        }

        return result;
    }

    /**
     * implante suivant document ModifTable3PourBP25-07-2006.doc
     * OwnerMarginOverVariableCostsPerStrategyPerVessel[str,month] = somme sur
     * tous les metiers de OwnerMarginOverVariableCostsPerStrategyMetPerVessel
     * [str,met,month]
     * 
     * @param str
     * @param step
     * @return
     * @throws TopiaException
     */
    private double ownerMarginOverVariableCostsPerStrategyPerVessel(
            Strategy str, TimeStep step) throws TopiaException {
        SetOfVessels sov = str.getSetOfVessels();
        Collection<EffortDescription> efforts = sov.getPossibleMetiers();

        float result = 0;

        for (EffortDescription effort : efforts) {
            Metier metier = effort.getPossibleMetiers();
            result += ownerMarginOverVariableCostsPerStrategyMetPerVessel(str,
                    metier, step);
        }
        return result;
    }

    //////////////////////////////////////////////////////////////////////
    // matrixOwnerMarginOverVariableCostsPerStrategy
    //////////////////////////////////////////////////////////////////////

    public MatrixND matrixOwnerMarginOverVariableCostsPerStrategy(TimeStep step)
            throws TopiaException {
        List<Strategy> strategies = siMatrix.getStrategies(step);

        MatrixND result = MatrixFactory
                .getInstance()
                .create(
                        ResultName.MATRIX_OWNER_MARGIN_OVER_VARIABLE_COSTS_PER_STRATEGY,
                        new List[] { strategies },
                        new String[] { n_("Strategies") });

        for (int s = 0; s < strategies.size(); s++) {
            Strategy str = strategies.get(s);
            double value = ownerMarginOverVariableCostsPerStrategy(str, step);
            result.setValue(s, value);
        }

        return result;
    }

    /**
     * implante suivant document ModifTable3PourBP25-07-2006.doc
     * OwnerMarginOverVariableCostsPerStrategy[str,month] =
     * OwnerMarginOverVariableCostsPerStrategyPerVessel[str,month]
     * *[PropNbVessels(str,sov)*NbVesselsSetOfVessels(sov)]
     * 
     * @param str
     * @param step
     * @return
     * @throws TopiaException
     */
    private double ownerMarginOverVariableCostsPerStrategy(Strategy str,
            TimeStep step) throws TopiaException {
        double ownerMarginOverVariableCostsPerStrategyPerVessel = ownerMarginOverVariableCostsPerStrategyPerVessel(
                str, step);
        double proportionSetOfVessels = str.getProportionSetOfVessels();
        double numberOfVessels = str.getSetOfVessels().getNumberOfVessels();

        return ownerMarginOverVariableCostsPerStrategyPerVessel
                * (proportionSetOfVessels * numberOfVessels);
    }

    //////////////////////////////////////////////////////////////////////
    // matrixVesselMarginOverVariableCostsPerStrategyPerVessel
    //////////////////////////////////////////////////////////////////////

    public MatrixND matrixVesselMarginOverVariableCostsPerStrategyPerVessel(
            TimeStep step) throws TopiaException {
        List<Strategy> strategies = siMatrix.getStrategies(step);

        MatrixND result = MatrixFactory
                .getInstance()
                .create(
                        ResultName.MATRIX_VESSEL_MARGIN_OVER_VARIABLE_COSTS_PER_STRATEGY_PER_VESSEL,
                        new List[] { strategies },
                        new String[] { n_("Strategies") });

        for (int s = 0; s < strategies.size(); s++) {
            Strategy str = strategies.get(s);
            double value = vesselMarginOverVariableCostsPerStrategyPerVessel(
                    str, step);
            result.setValue(s, value);
        }

        return result;
    }

    /**
     * implante suivant document ModifTable3PourBP25-07-2006.doc
     * VesselMarginOverVariableCostsPerStrategyPerVessel[str,month] = somme sur
     * tous les metiers de VesselMarginOverVariableCostsPerStrategyMetPerVessel
     * [str,met,month]
     * 
     * @param str
     * @param step
     * @return
     * @throws TopiaException
     */
    private double vesselMarginOverVariableCostsPerStrategyPerVessel(
            Strategy str, TimeStep step) throws TopiaException {
        SetOfVessels sov = str.getSetOfVessels();
        Collection<EffortDescription> efforts = sov.getPossibleMetiers();

        float result = 0;

        for (EffortDescription effort : efforts) {
            Metier metier = effort.getPossibleMetiers();
            result += vesselMarginOverVariableCostsPerStrategyMetPerVessel(str,
                    metier, step);
        }
        return result;
    }

    //////////////////////////////////////////////////////////////////////
    // matrixVesselMarginOverVariableCostsPerStrategy
    //////////////////////////////////////////////////////////////////////

    public MatrixND matrixVesselMarginOverVariableCostsPerStrategy(TimeStep step)
            throws TopiaException {
        List<Strategy> strategies = siMatrix.getStrategies(step);

        MatrixND result = MatrixFactory
                .getInstance()
                .create(
                        ResultName.MATRIX_VESSEL_MARGIN_OVER_VARIABLE_COSTS_PER_STRATEGY,
                        new List[] { strategies },
                        new String[] { n_("Strategies") });

        for (int s = 0; s < strategies.size(); s++) {
            Strategy str = strategies.get(s);
            double value = vesselMarginOverVariableCostsPerStrategy(str, step);
            result.setValue(s, value);
        }

        return result;
    }

    /**
     * implante suivant document ModifTable3PourBP25-07-2006.doc
     * VesselMarginOverVariableCostsPerStrategy[str,month]
     * =VesselMarginOverVariableCostsPerStrategyPerVessel [str,month]
     * *[PropNbVessels(str,sov)*NbVesselsSetOfVessels(sov)]
     * 
     * @param str
     * @param step
     * @return
     * @throws TopiaException
     */
    private double vesselMarginOverVariableCostsPerStrategy(Strategy str,
            TimeStep step) throws TopiaException {
        double vesselMarginOverVariableCostsPerStrategyPerVessel = vesselMarginOverVariableCostsPerStrategyPerVessel(
                str, step);
        double proportionSetOfVessels = str.getProportionSetOfVessels();
        double numberOfVessels = str.getSetOfVessels().getNumberOfVessels();

        return vesselMarginOverVariableCostsPerStrategyPerVessel
                * (proportionSetOfVessels * numberOfVessels);
    }

    ///////////////////////////////////////////////////////////////////////////
    //
    // Methode non utilisee directement dans GravityModel, mais dans les rules
    //
    ///////////////////////////////////////////////////////////////////////////

    public double valuePerUnitFishingEffort(Strategy str, Metier metier,
            TimeStep step) throws TopiaException {
        List<Population> pops = siMatrix.getPopulations(step);

        double result = 0;

        for (Population pop : pops) {
            List<PopulationGroup> groups = pop.getPopulationGroup();
            Collection<Zone> zones = pop.getPopulationZone();
            for (PopulationGroup group : groups) {
                double price = group.getPrice();
                for (Zone zone : zones) {
                    double catchValue = getCatchWeightPerStrMetPerZonePop(str,
                            metier, group, zone, step);
                    double discards = getDiscardsWeightPerStrMet(str, metier,
                            group, zone, step);
                    result += price * (catchValue - discards);
                }
            }
        }

        double effort = getEffortPerStrategyMet(str, metier, step);

        if (effort == 0) {
            result = 0;
        } else {
            result = result / effort;
        }
        // FIXME verifier qu'il faut bien retourner 0, si pas d'effort
        return result;
    }

    /**
     * @param str
     * @param metier
     * @param step
     * @return
     */
    public double getEffortPerStrategyMet(Strategy str, Metier metier, TimeStep step) {
        MatrixND mat = resultManager.getMatrix(step,
                ResultName.MATRIX_EFFORT_PER_STRATEGY_MET);
        double result = 0;
        if (mat != null) {
            result = mat.getValue(str, metier);
        }
        return result;
    }

    public double landingPerUnitFishingEffort(Strategy str, Metier metier,
            TimeStep step) throws TopiaException {
        List<Population> pops = siMatrix.getPopulations(step);

        double effort = getEffortPerStrategyMet(str, metier, step);
        double result = 0;

        if (effort != 0) { // s'il n'y a pas d'effort on retournera 0
            for (Population pop : pops) {
                List<PopulationGroup> groups = pop.getPopulationGroup();
                Collection<Zone> zones = pop.getPopulationZone();
                for (PopulationGroup group : groups) {
                    for (Zone zone : zones) {
                        double catchValue = getCatchWeightPerStrMetPerZonePop(
                                str, metier, group, zone, step);
                        double discards = getDiscardsWeightPerStrMet(str,
                                metier, group, zone, step);
                        result += catchValue - discards;
                    }
                }
            }
            result = result / effort;
        }

        return result;
    }
}
