//----------------------------------------------------------------------------
// Copyright (C) 2018  João Faccin
// 
// This library 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 2.1 of the License, or (at your option) any later version.
// 
// This library 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 library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
// 
// To contact the authors:
// http://inf.ufrgs.br/prosoft/bdi4jade/
//
//----------------------------------------------------------------------------

package bdi4jade.extension.undo.reasoning;

import java.util.ArrayList;
import java.util.Map;

import bdi4jade.belief.BeliefBase;
import bdi4jade.core.Capability;
import bdi4jade.core.GoalUpdateSet;
import bdi4jade.extension.remediation.logics.Predicate;
import bdi4jade.extension.undo.GoalAchievementMetadata;
import bdi4jade.extension.undo.RevertingCapability;
import bdi4jade.goal.Goal;
import bdi4jade.goal.PredicateGoal;
import bdi4jade.reasoning.OptionGenerationFunction;

/**
 * This class implements the {@link OptionGenerationFunction} interface. It is
 * responsible for performing the reversion of actions recorded by existing goal
 * achievement metadata.
 * 
 * @author João Faccin
 */
public class RevertingOptionGenerationFunction implements OptionGenerationFunction {

	private Capability capability;

	/**
	 * Initializes a new option generation function and sets its related capability.
	 * 
	 * @param capability
	 *            the capability to be set.
	 */
	public RevertingOptionGenerationFunction(Capability capability) {
		setCapability(capability);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see bdi4jade.reasoning.OptionGenerationFunction#generateGoals(bdi4jade.core.
	 * GoalUpdateSet)
	 */
	@Override
	public void generateGoals(GoalUpdateSet goalUpdateSet) {
		if (this.capability instanceof RevertingCapability) {
			Map<Goal, GoalAchievementMetadata> gams = ((RevertingCapability) this.capability)
					.getGoalAchievementMetadata();
			for (GoalAchievementMetadata gam : gams.values()) {
				if (gam.isReversionDeactivated()) {
					gams.remove(gam.getGoal());
				} else {
					if (gam.isReversionActivated(this.capability.getBeliefBase())) {
						ArrayList<Predicate> filteredChanges = gam.filterBeliefChanges(this.capability.getBeliefBase());

						ArrayList<PredicateGoal<?>> reversionGoals = generateReversionGoals(filteredChanges,
								this.capability.getBeliefBase());

						for (PredicateGoal<?> predicateGoal : reversionGoals) {
							goalUpdateSet.generateGoal(predicateGoal, capability);
						}

						for (Predicate predicate : filteredChanges) {
							gam.getBeliefChangeTrace().remove(predicate);
						}

						if (!gam.isRollbackTriggered()) {
							gams.remove(gam.getGoal());
						}
					}
				}
			}
		}
	}

	/**
	 * Generates achievement goals with the aim of reverting the evaluation value of
	 * each predicate in predicates according to the beliefBase.
	 * 
	 * @param predicates
	 *            the list of predicates to be reverted.
	 * @param beliefBase
	 *            the belief base that must contain the updated evaluation values of
	 *            the given predicates.
	 * @return
	 */
	public ArrayList<PredicateGoal<?>> generateReversionGoals(ArrayList<Predicate> predicates, BeliefBase beliefBase) {
		ArrayList<PredicateGoal<?>> reversionGoals = new ArrayList<>();

		for (Predicate predicate : predicates) {
			PredicateGoal<?> goal = new PredicateGoal<Predicate>(predicate, !predicate.getValue(beliefBase));
			reversionGoals.add(goal);
		}

		return reversionGoals;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * bdi4jade.reasoning.ReasoningStrategy#setCapability(bdi4jade.core.Capability)
	 */
	@Override
	public void setCapability(Capability capability) {
		if (this.capability != null) {
			if (!this.capability.equals(capability)) {
				throw new IllegalArgumentException(
						"This reasoning strategy is already associated with another capability.");
			}
		}
		this.capability = capability;
	}

}
