/*
 * #%L
 * IsisFish data
 * %%
 * Copyright (C) 2006 - 2014 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 3 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public 
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 * #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;
import org.nuiton.math.matrix.MatrixIterator;

/**
 * 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("..."); */
    final 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);
            SetOfVessels sov = str.getSetOfVessels();
            metiers = siMatrix.getMetiers(str, step);
            for (int m = 0; m < metiers.size(); m++) {
                Metier metier = metiers.get(m);

                double fishingTime = 0;
                double repair = 0;

                EffortDescription effort = sov.getPossibleMetiers(metier);
                if (effort != null) {
                    repair = effort.getRepairAndMaintenanceGearCost();
                }
                if (repair != 0 ) {
                    fishingTimePerMonthPerVessel(str, metier, step);
                }
                
                // FIXME verifier qu'il faut bien retourner 0, si pas d'effort
                double value = fishingTime * repair / TimeUnit.HOUR_PER_DAY;

                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") });

        // indice dans les matrices catch et discards
        int strDim = 0;
        int metierDim = 1;
        int groupDim = 2;
        int zoneDim = 3;

        for (int p = 0; p < pops.size(); p++) {
            Population pop = pops.get(p);
            List<PopulationGroup> groups = pop.getPopulationGroup();
            double[] prices = new double[groups.size()];
            int cpt=0;
            for (PopulationGroup group : groups) {
                prices[cpt++] = group.getPrice();
            }

            MatrixND matCatch = getResult_MATRIX_CATCH_WEIGHT_PER_STRATEGY_MET_PER_ZONE_POP(step, pop);
            MatrixND matDiscards = getResult_MATRIX_DISCARDS_WEIGHT_PER_STR_MET_PER_ZONE_POP(step, pop);

            if (matCatch != null) {
                for (MatrixIterator i=matCatch.iteratorNotZero(); i.next();){
                    int[] pos = i.getCoordinates();
                    double price = prices[pos[groupDim]];
                    if (price != 0) {
                        double value = result.getValue(pos[strDim], pos[metierDim], p) + price * i.getValue();
                        result.setValue(pos[strDim], pos[metierDim], p, value);
                    }
                }
            }
            if (matDiscards != null) {
                for (MatrixIterator i=matDiscards.iteratorNotZero(); i.next();){
                    int[] pos = i.getCoordinates();
                    double price = prices[pos[groupDim]];
                    if (price != 0) {
                        double value = result.getValue(pos[strDim], pos[metierDim], p) - price * i.getValue();
                        result.setValue(pos[strDim], pos[metierDim], p, value);
                    }
                }
            }
        }

        return result;

//        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();
//            if (price != 0) {
//                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 = getResult_MATRIX_CATCH_WEIGHT_PER_STRATEGY_MET_PER_ZONE_POP(step, group.getPopulation());
        double result = 0;
        if (mat != null) {
            result = mat.getValue(str, metier, group, zone);
        }
        return result;
    }

    private MatrixND getResult_MATRIX_CATCH_WEIGHT_PER_STRATEGY_MET_PER_ZONE_POP(TimeStep step, Population pop) {
        MatrixND mat = resultManager.getMatrix(step, pop,
                ResultName.MATRIX_CATCH_WEIGHT_PER_STRATEGY_MET_PER_ZONE_POP);
        return mat;
    }

    /**
     * @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 = getResult_MATRIX_DISCARDS_WEIGHT_PER_STR_MET_PER_ZONE_POP(step, group.getPopulation());
        double result = 0;
        if (mat != null) {
            result = mat.getValue(str, metier, group, zone);
        }
        return result;
    }

    private MatrixND getResult_MATRIX_DISCARDS_WEIGHT_PER_STR_MET_PER_ZONE_POP(TimeStep step, Population pop) {
        MatrixND mat = resultManager.getMatrix(step, pop,
                ResultName.MATRIX_DISCARDS_WEIGHT_PER_STR_MET_PER_ZONE_POP);
        return mat;
    }

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

    public MatrixND matrixGrossValueOfLandingsPerStrategyMet(TimeStep step)
            throws TopiaException {

        MatrixND matPerSpecies = matrixGrossValueOfLandingsPerSpeciesPerStrategyMet(step);
        // sum over pop
        MatrixND result = matPerSpecies.sumOverDim(2).reduceDims(2);
        result.setName(ResultName.MATRIX_GROSS_VALUE_OF_LANDINGS_PER_STRATEGY_MET);
        return result;

//        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 {
        MatrixND matGrossValue = matrixGrossValueOfLandingsPerStrategyMet(step);
        MatrixND result = matGrossValue.copy();
        result.setName(ResultName.MATRIX_GROSS_VALUE_OF_LANDINGS_PER_STRATEGY_MET_PER_VESSEL);

        for (MatrixIterator i=result.iterator(); i.next();) {
            Object[] sems = i.getSemanticsCoordinates();
            Strategy str = (Strategy)sems[0];
            Metier metier = (Metier)sems[1];

            // 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);

            double value = (i.getValue() + grossValueOfLandingsOtherSpeciesPerStrategyMet)
                / (proportionSetOfVessels * numberOfVessels);

            i.setValue(value);
        }
        return result;

//        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 {

        MatrixND result = matrixGrossValueOfLandingsPerStrategyMet(step).copy();
        result.setName(ResultName.MATRIX_NET_VALUE_OF_LANDINGS_PER_STRATEGY_MET);
        
        for (MatrixIterator i=result.iterator(); i.next();) {
            Object[] sems = i.getSemanticsCoordinates();
            Strategy str = (Strategy)sems[0];
            Metier metier = (Metier)sems[1];

            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 value = (i.getValue() + grossValueOfLandingsOtherSpeciesPerStrategyMet)
                    * landingCost;
            i.setValue(value);
        }
        return result;


//        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 {
        MatrixND result = matrixNetValueOfLandingsPerStrategyMet(step).copy();
        result.setName(ResultName.MATRIX_NET_VALUE_OF_LANDINGS_PER_STRATEGY_MET_PER_VESSEL);

        for(MatrixIterator i=result.iteratorNotZero(); i.next();) {
            Object[] sems = i.getSemanticsCoordinates();
            Strategy str = (Strategy)sems[0];
            Metier metier = (Metier)sems[1];

            // 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 div = proportionSetOfVessels * numberOfVessels;

            if (div != 0) {
                double value = i.getValue() / div;

                i.setValue(value);
            }
        }

        return result;

//        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 {

        MatrixND result = matrixNetValueOfLandingsPerStrategyMetPerVessel(step).copy();
        result.setName(ResultName.MATRIX_NET_RENEVUE_TO_SHARE_PER_STRATEGY_MET_PER_VESSEL);

        for(MatrixIterator i=result.iteratorNotZero(); i.next();) {
            Object[] sems = i.getSemanticsCoordinates();
            Strategy str = (Strategy)sems[0];
            Metier metier = (Metier)sems[1];
            
            double sharedNotFixedCostsPerVessel = sharedNotFixedCostsPerVessel(str,
                    metier, step);
            double propStr = str.getStrategyMonthInfo(step.getMonth())
                    .getProportionMetier(metier);

            double value = i.getValue() - sharedNotFixedCostsPerVessel * propStr;
            i.setValue(value);
        }
        return result;

//        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 {

        MatrixND result = matrixNetValueOfLandingsPerStrategyMetPerVessel(step).copy();
        result.setName(ResultName.MATRIX_CREW_SHARE_PER_STRATEGY_MET_PER_VESSEL);

        for(MatrixIterator i=result.iteratorNotZero(); i.next();) {
            Object[] sems = i.getSemanticsCoordinates();
            Strategy str = (Strategy)sems[0];
            Metier metier = (Metier)sems[1];

            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
            double value = i.getValue() * crewShareRate;
            i.setValue(value);
        }
        return result;

//        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 {

        MatrixND matRepair = matrixRepairAndMaintenanceGearCostsPerVessel(step);
        MatrixND matCrew = matrixCrewSharePerStrategyMetPerVessel(step);
        MatrixND matRevenue = matrixNetRenevueToSharePerStrategyMetPerVessel(step);

        MatrixND result = matRevenue.copy();
        result.setName(ResultName.MATRIX_OWNER_MARGIN_OVER_VARIABLE_COSTS_PER_STRATEGY_MET_PER_VESSEL);

        for(MatrixIterator i=result.iterator(); i.next();) {
            Object[] sems = i.getSemanticsCoordinates();
            Strategy str = (Strategy)sems[0];
            Metier metier = (Metier)sems[1];

            double propStr = str.getStrategyMonthInfo(step.getMonth())
                    .getProportionMetier(metier);

            double netRenevueToShare = i.getValue();
            double crewShare = matCrew.getValue(str, metier);
            double repair = 0;
            if (propStr != 0) {
                repair = matRepair.getValue(str, metier);
            }
            
            double value = netRenevueToShare - crewShare - repair * propStr;
            i.setValue(value);
        }

        return result;

//        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 {

        MatrixND matRepair = matrixRepairAndMaintenanceGearCostsPerVessel(step);

        MatrixND result = matrixNetValueOfLandingsPerStrategyMetPerVessel(step).copy();
        result.setName(ResultName.MATRIX_VESSEL_MARGIN_OVER_VARIABLE_COSTS_PER_STRATEGY_MET_PER_VESSEL);

        for(MatrixIterator i=result.iterator(); i.next();) {
            Object[] sems = i.getSemanticsCoordinates();
            Strategy str = (Strategy)sems[0];
            Metier metier = (Metier)sems[1];

            double repair = 0;
            double propStr = str.getStrategyMonthInfo(step.getMonth())
                    .getProportionMetier(metier);
            if (propStr != 0) {
                // optimisation repair ne sert pas si propStr == 0, donc on va pas le chercher
                repair = matRepair.getValue(str, metier);
            }

            double value = i.getValue() - repair * propStr;
            i.setValue(value);
        }
        return result;

//        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 {

        MatrixND matOwner = matrixOwnerMarginOverVariableCostsPerStrategyMetPerVessel(step);
        MatrixND result = MatrixFactory
                .getInstance()
                .create(
                        ResultName.MATRIX_OWNER_MARGIN_OVER_VARIABLE_COSTS_PER_STRATEGY_PER_VESSEL,
                        new List[] { matOwner.getSemantic(0) },
                        new String[] { n("Strategies") });

        for(MatrixIterator i=result.iterator(); i.next();) {
            Object[] sems = i.getSemanticsCoordinates();
            Strategy str = (Strategy)sems[0];

            SetOfVessels sov = str.getSetOfVessels();
            Collection<EffortDescription> efforts = sov.getPossibleMetiers();

            double value = 0;

            for (EffortDescription effort : efforts) {
                Metier metier = effort.getPossibleMetiers();
                value += matOwner.getValue(str, metier);
            }
            i.setValue(value);
        }
        return result;

//        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 {
        MatrixND result = matrixOwnerMarginOverVariableCostsPerStrategyPerVessel(step).copy();
        result.setName(ResultName.MATRIX_OWNER_MARGIN_OVER_VARIABLE_COSTS_PER_STRATEGY);

        for(MatrixIterator i=result.iteratorNotZero(); i.next();) {
            Object[] sems = i.getSemanticsCoordinates();
            Strategy str = (Strategy)sems[0];

            double proportionSetOfVessels = str.getProportionSetOfVessels();
            double numberOfVessels = str.getSetOfVessels().getNumberOfVessels();

            double value =  i.getValue() * (proportionSetOfVessels * numberOfVessels);
            i.setValue(value);
        }
        return result;

//        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 {

        MatrixND matPerMet =
                matrixVesselMarginOverVariableCostsPerStrategyMetPerVessel(step);
        MatrixND result = MatrixFactory
                .getInstance()
                .create(
                        ResultName.MATRIX_VESSEL_MARGIN_OVER_VARIABLE_COSTS_PER_STRATEGY_PER_VESSEL,
                        new List[] { matPerMet.getSemantic(0) },
                        new String[] { n("Strategies") });

        for(MatrixIterator i=result.iterator(); i.next();) {
            Object[] sems = i.getSemanticsCoordinates();
            Strategy str = (Strategy)sems[0];

            SetOfVessels sov = str.getSetOfVessels();
            Collection<EffortDescription> efforts = sov.getPossibleMetiers();

            double value = 0;

            for (EffortDescription effort : efforts) {
                Metier metier = effort.getPossibleMetiers();
                value += matPerMet.getValue(str, metier);
            }

            i.setValue(value);
        }
        return result;


//        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 {

        MatrixND result = matrixVesselMarginOverVariableCostsPerStrategyPerVessel(step).copy();
        result.setName(ResultName.MATRIX_VESSEL_MARGIN_OVER_VARIABLE_COSTS_PER_STRATEGY);

        for(MatrixIterator i=result.iteratorNotZero(); i.next();) {
            Object[] sems = i.getSemanticsCoordinates();
            Strategy str = (Strategy)sems[0];

            double proportionSetOfVessels = str.getProportionSetOfVessels();
            double numberOfVessels = str.getSetOfVessels().getNumberOfVessels();

            double value = i.getValue() * (proportionSetOfVessels * numberOfVessels);
            i.setValue(value);
        }
        return result;

//        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;

        double effort = getEffortPerStrategyMet(str, metier, step);
        if (effort != 0) {
            for (Population pop : pops) {
                List<PopulationGroup> groups = pop.getPopulationGroup();
                for (PopulationGroup group : groups) {
                    double price = group.getPrice();
                    if (price != 0) {
                        Collection<Zone> zones = pop.getPopulationZone();
                        for (Zone zone : zones) {
                            double catchValue = getCatchWeightPerStrMetPerZonePop(str,
                                    metier, group, zone, step);
                            double discards = getDiscardsWeightPerStrMet(str, metier,
                                    group, zone, step);
                            result += price * (catchValue - discards);
                        }
                    }
                }
            }
            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;
    }
}
