package bdi4jade.examples.undo;

import java.util.ArrayList;

import bdi4jade.annotation.Belief;
import bdi4jade.belief.PredicateBelief;
import bdi4jade.belief.TransientPredicate;
import bdi4jade.core.BDIAgent;
import bdi4jade.examples.undo.domain.CO;
import bdi4jade.examples.undo.domain.House;
import bdi4jade.examples.undo.domain.predicate.Abnormal;
import bdi4jade.examples.undo.domain.predicate.Leak;
import bdi4jade.examples.undo.domain.predicate.Locked;
import bdi4jade.examples.undo.domain.predicate.On;
import bdi4jade.examples.undo.domain.predicate.Open;
import bdi4jade.examples.undo.domain.predicate.TakeOff;
import bdi4jade.examples.undo.plan.EvacuateAndVentilatePlanBody;
import bdi4jade.examples.undo.plan.NotifyAbnormalCOPlanBody;
import bdi4jade.examples.undo.plan.StopLeakingPlanBody;
import bdi4jade.examples.undo.plan.request.RequestDeviceClosePlanBody;
import bdi4jade.examples.undo.plan.request.RequestDeviceLockPlanBody;
import bdi4jade.examples.undo.plan.request.RequestDeviceOffPlanBody;
import bdi4jade.examples.undo.plan.request.RequestDeviceOnPlanBody;
import bdi4jade.examples.undo.plan.request.RequestDeviceOpenPlanBody;
import bdi4jade.examples.undo.plan.request.RequestDeviceShutdownPlanBody;
import bdi4jade.examples.undo.plan.request.RequestDeviceTakeOffPlanBody;
import bdi4jade.examples.undo.plan.request.RequestDeviceUnlockPlanBody;
import bdi4jade.extension.remediation.RemediationCapability;
import bdi4jade.extension.remediation.graph.CauseEffectRelationship;
import bdi4jade.extension.remediation.logics.Fact;
import bdi4jade.goal.GoalTemplateFactory;
import bdi4jade.goal.PredicateGoal;
import bdi4jade.plan.DefaultPlan;
import bdi4jade.plan.Plan;
import bdi4jade.reasoning.DefaultBeliefRevisionStrategy;
import jade.core.AID;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;

/**
 * @author jgfaccin
 *
 */
public class ManagementCapability extends RemediationCapability {

	private static final long serialVersionUID = -1971498055603790248L;

	// Neighbours
	@Belief
	private String alarmAgent;
	@Belief
	private String detectorAgent;
	@Belief
	private String doorsAgent;
	@Belief
	private String fansAgent;
	@Belief
	private String lightsAgent;
	@Belief
	private String valveAgent;
	@Belief
	private String windowsAgent;

	// Device Predicates
	private TransientPredicate<TakeOff> alarmTakeOff;
	private TransientPredicate<Locked> doorsUnlock;
	private TransientPredicate<On> fansOn;
	private TransientPredicate<Leak> leakingWaterHeater;
	private TransientPredicate<On> lightsOn;
	private TransientPredicate<Open> valveOpen;
	private TransientPredicate<Open> windowsOpen;
	private TransientPredicate<Abnormal> abnormalCO;

	// Plans
	@bdi4jade.annotation.Plan
	private Plan evacuateAndVentilatePlan;
	@bdi4jade.annotation.Plan
	private Plan requestDeviceTakeOffPlan;
	@bdi4jade.annotation.Plan
	private Plan requestDeviceShutdownPlan;
	@bdi4jade.annotation.Plan
	private Plan requestDeviceOnPlan;
	@bdi4jade.annotation.Plan
	private Plan requestDeviceOffPlan;
	@bdi4jade.annotation.Plan
	private Plan requestDeviceUnlockPlan;
	@bdi4jade.annotation.Plan
	private Plan requestDeviceLockPlan;
	@bdi4jade.annotation.Plan
	private Plan requestDeviceOpenPlan;
	@bdi4jade.annotation.Plan
	private Plan requestDeviceClosePlan;
	@bdi4jade.annotation.Plan
	private Plan stopLeakingPlan;

	public ManagementCapability(BDIAgent agent, String alarmAgent, String detectorAgent, String doorsAgent,
			String fansAgent, String lightsAgent, String valveAgent, String windowsAgent) {
		super(agent);
		this.alarmAgent = alarmAgent;
		this.detectorAgent = detectorAgent;
		this.doorsAgent = doorsAgent;
		this.fansAgent = fansAgent;
		this.lightsAgent = lightsAgent;
		this.valveAgent = valveAgent;
		this.windowsAgent = windowsAgent;

		setBeliefRevisionStrategy(new MyBeliefRevisionStrategy());

		initializeBeliefs();
		initializeCauseEffectKnowledgeModel();
		initializePlans();
	}

	private void initializeBeliefs() {
		ArrayList<PredicateBelief<?>> beliefs = new ArrayList<>();
		this.alarmTakeOff = new TransientPredicate<>(new TakeOff(House.ALARM), false);
		beliefs.add(alarmTakeOff);
		this.doorsUnlock = new TransientPredicate<>(new Locked(House.DOORS), true);
		beliefs.add(doorsUnlock);
		this.fansOn = new TransientPredicate<On>(new On(House.FANS), false);
		beliefs.add(fansOn);
		this.leakingWaterHeater = new TransientPredicate<Leak>(new Leak(House.WATER_HEATER), false);
		beliefs.add(leakingWaterHeater);
		this.lightsOn = new TransientPredicate<>(new On(House.LIGHTS), false);
		beliefs.add(lightsOn);
		this.valveOpen = new TransientPredicate<Open>(new Open(House.VALVE), true);
		beliefs.add(valveOpen);
		this.windowsOpen = new TransientPredicate<Open>(new Open(House.WINDOWS), false);
		beliefs.add(windowsOpen);
		this.abnormalCO = new TransientPredicate<Abnormal>(new Abnormal(CO.getInstance()), false);
		beliefs.add(abnormalCO);

		for (PredicateBelief<?> predicateBelief : beliefs) {
			beliefBase.addBelief(predicateBelief);
		}
	}

	private void initializeCauseEffectKnowledgeModel() {
		CauseEffectRelationship cer = new CauseEffectRelationship(new Fact(new Abnormal(CO.getInstance()), true));
		cer.addOptionalCause(new Fact(new Leak(House.WATER_HEATER), true));
		causeEffectKnowledgeModel.addCauseEffectRelationship(cer);
	}

	private void initializePlans() {
		this.requestDeviceTakeOffPlan = new DefaultPlan(
				GoalTemplateFactory.hasBeliefOfTypeWithValue(TakeOff.class, true), RequestDeviceTakeOffPlanBody.class);
		this.requestDeviceShutdownPlan = new DefaultPlan(
				GoalTemplateFactory.hasBeliefOfTypeWithValue(TakeOff.class, false),
				RequestDeviceShutdownPlanBody.class);

		this.requestDeviceOnPlan = new DefaultPlan(GoalTemplateFactory.hasBeliefOfTypeWithValue(On.class, true),
				RequestDeviceOnPlanBody.class);
		this.requestDeviceOffPlan = new DefaultPlan(GoalTemplateFactory.hasBeliefOfTypeWithValue(On.class, false),
				RequestDeviceOffPlanBody.class);

		this.requestDeviceUnlockPlan = new DefaultPlan(
				GoalTemplateFactory.hasBeliefOfTypeWithValue(Locked.class, false), RequestDeviceUnlockPlanBody.class);
		this.requestDeviceLockPlan = new DefaultPlan(GoalTemplateFactory.hasBeliefOfTypeWithValue(Locked.class, true),
				RequestDeviceLockPlanBody.class);

		this.requestDeviceOpenPlan = new DefaultPlan(GoalTemplateFactory.hasBeliefOfTypeWithValue(Open.class, true),
				RequestDeviceOpenPlanBody.class);
		this.requestDeviceClosePlan = new DefaultPlan(GoalTemplateFactory.hasBeliefOfTypeWithValue(Open.class, false),
				RequestDeviceClosePlanBody.class);

		this.stopLeakingPlan = new DefaultPlan(GoalTemplateFactory.hasBeliefOfTypeWithValue(Leak.class, false),
				StopLeakingPlanBody.class);

		this.evacuateAndVentilatePlan = new DefaultPlan(
				GoalTemplateFactory.hasBeliefOfTypeWithValue(Abnormal.class, false),
				EvacuateAndVentilatePlanBody.class);
	}

	class MyBeliefRevisionStrategy extends DefaultBeliefRevisionStrategy {

		private MessageTemplate mt;

		@SuppressWarnings("unchecked")
		@Override
		public void reviewBeliefs() {
			super.reviewBeliefs();
			this.mt = MessageTemplate.MatchSender(new AID(detectorAgent, false));
			ACLMessage notificationMsg = getMyAgent().receive(mt);
			TransientPredicate<Abnormal> abnormalCO = (TransientPredicate<Abnormal>) this.capability.getBeliefBase()
					.getBelief(new Abnormal(CO.getInstance()));
			TransientPredicate<Leak> leakingWaterHeater = (TransientPredicate<Leak>) this.capability.getBeliefBase()
					.getBelief(new Leak(House.WATER_HEATER));
			if (notificationMsg != null) {
				if (notificationMsg.getContent().equals(NotifyAbnormalCOPlanBody.ABNORMAL_CO)) {
					abnormalCO.setValue(true);
					leakingWaterHeater.setValue(true);
					this.capability.getMyAgent().addGoal(this.capability,
							new PredicateGoal<Abnormal>(new Abnormal(CO.getInstance()), false));
				} else {
					abnormalCO.setValue(false);
				}
			}
			
			TransientPredicate<Open> openValve = (TransientPredicate<Open>) this.capability.getBeliefBase()
					.getBelief(new Open(House.VALVE));
			if (!openValve.getValue()) {
				leakingWaterHeater.setValue(false);;
			}
		}

	}
}
