/*
 * #%L
 * IsisFish data
 * %%
 * Copyright (C) 2009 - 2014 Ifremer, Code Lutin, Benjamin Poussin
 * %%
 * 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 simulators;

import fr.ifremer.isisfish.IsisFishException;
import fr.ifremer.isisfish.entities.Population;
import fr.ifremer.isisfish.entities.SimulatorToUseForPopulation;
import fr.ifremer.isisfish.simulator.SimulationContext;
import fr.ifremer.isisfish.types.TimeStep;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuiton.math.matrix.MatrixND;
import org.nuiton.topia.TopiaException;
import scripts.SiMatrix;

/**
 * Simulateur qui utilise par cellule ou par zone selon la population.
 */
public class DefaultSimulator extends SimulatorEffortByZone {

    private static final Log log = LogFactory.getLog(DefaultSimulator.class);

    @Override
    protected SiMatrix newSiMatrix(SimulationContext context) throws TopiaException {
        SiMatrix siMatrixEffortByZone = new SimulatorEffortByZone().newSiMatrix(context);
        SiMatrix siMatrixEffortByCell = new SimulatorEffortByCell().newSiMatrix(context);
        return new InnerSiMatrix(context, siMatrixEffortByZone, siMatrixEffortByCell);
    }

    private static class InnerSiMatrix extends SiMatrix {

        private final SiMatrix siMatrixEffortByZone;

        private final SiMatrix siMatrixEffortByCell;

        private InnerSiMatrix(SimulationContext context, SiMatrix siMatrixEffortByZone, SiMatrix siMatrixEffortByCell) throws TopiaException {
            super(context);
            this.siMatrixEffortByZone = siMatrixEffortByZone;
            this.siMatrixEffortByCell = siMatrixEffortByCell;
        }

        @Override
        public void computeMonthExtra(TimeStep step, Population pop, MatrixND N) throws TopiaException, IsisFishException {
            getSiMatrixToCall(pop).computeMonthExtra(step, pop, N);
        }

        @Override
        public MatrixND matrixCatchPerStrategyMetPerZone(MatrixND N,
                                                         Population pop, TimeStep step) throws TopiaException, IsisFishException {
            return getSiMatrixToCall(pop).matrixCatchPerStrategyMetPerZone(N, pop, step);
        }

        @Override
        public MatrixND matrixAbundance(MatrixND N, Population pop, TimeStep step)
                throws TopiaException, IsisFishException {
            return getSiMatrixToCall(pop).matrixAbundance(N, pop, step);
        }

        @Override
        public MatrixND matrixCatchWeightPerStrategyMetPerZonePop(TimeStep step,
                                                                  Population pop, MatrixND matrixCatchPerStrategyMetPerZonePop)
                throws TopiaException, IsisFishException {
            return getSiMatrixToCall(pop).matrixCatchWeightPerStrategyMetPerZonePop(step, pop, matrixCatchPerStrategyMetPerZonePop);
        }

        private SiMatrix getSiMatrixToCall(Population population) {
            SimulatorToUseForPopulation simulatorToUseForPopulation = population.getSimulatorToUse();
            if (log.isDebugEnabled()) {
                log.debug("for population " + population.getName() + ", apply " + simulatorToUseForPopulation);
            }
            switch (simulatorToUseForPopulation) {
                case PER_ZONE:
                    return siMatrixEffortByZone;
                case PER_CELL:
                    return siMatrixEffortByCell;
                default:
                    throw new IllegalArgumentException("unknown value: " + simulatorToUseForPopulation);
            }
        }
    }
}