/**
 * Copyright (C) 2019 Patrik Dufresne Service Logiciel <info@patrikdufresne.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package com.planimod.core.planif;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This runnable is used to assign the employee according to it's preferences.
 * 
 * @author Patrik Dufresne
 */
public class MaxEmployeePreferences extends SolveRunnable {

    private static final transient Logger LOGGER = LoggerFactory.getLogger(MaxEmployeePreferences.class);

    /**
     * Public constructor.
     * 
     * @param problem
     *            reference to ProblemBuilder
     */
    public MaxEmployeePreferences(ProblemBuilder problem) {
        super(problem);
    }

    /**
     * This function is called to apply the non-availabilities for the given employee.
     * 
     * @throws GeneratePlanifException
     */
    private void handleNonAvailabilities(int i) throws GeneratePlanifException {
        // Check if the employee is available
        if (!(pb.fixNonAvailabilitiesVariables(i))) {
            return;
        }
        // Clear the employee from current to less senior. We do not need to
        // recompute the current employee.
        pb.maxSeniorEmployee.release(i + 1);
        pb.minSwappable.release();
        pb.minEmployee.release();
        pb.maxTaskAssigned.release();
        pb.maxTaskAssigned.run();
        pb.minEmployee.run();
        pb.minSwappable.run();
        pb.maxSeniorEmployee.run(i + 1);
    }

    @Override
    protected boolean runSolver() throws GeneratePlanifException {
        // Loop on employee - the list is sorted by seniority
        for (int i = 0; i < pb.xi.length; i++) {
            // Update monitor
            pb.worked(ProblemBuilder.UNIT);
            // Maximize the preferred position
            MaxPreferredPosition solver = new MaxPreferredPosition(pb, i);
            solver.run();
            if (solver.isFeasible()) {
                // Apply the non-availability.
                handleNonAvailabilities(i);
                // A solution as been found with the employee working on
                // it's preferred position. So there is not need to optimize
                // the preferred team.
                continue;
            }
            // Maximize the preferred team
            MaxPreferredTeams maxPreferredTeam = new MaxPreferredTeams(pb, i);
            maxPreferredTeam.run();
            // Apply the non-availability.
            handleNonAvailabilities(i);
        }
        // Loop on employee
        for (int i = 0; i < pb.xi.length; i++) {
            pb.worked(ProblemBuilder.UNIT);
            // Maximize the employee preferred section.
            MaxPreferredSection maxPreferredSection = new MaxPreferredSection(pb, i);
            maxPreferredSection.run();
        }
        return true;
    }

    @Override
    protected boolean setUp() throws GeneratePlanifException {
        LOGGER.debug("Maximize employee preferences.");
        return true;
    }
}