//==============================================================================
//	
//	Copyright (c) 2015-
//	Author:
//  * Andreas Fellner <andreas.fellner@ist.ac.at (Institute of Science and Technology Austria)
//	
//------------------------------------------------------------------------------
//
//  This file was created as part of the implementation for 
//  Counterexample Explanation by Learning
//  Small Strategies in Markov Decision Processes
//  Brázdil et al. 2015  
//
//	This file is part of PRISM.
//	
//	PRISM 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 2 of the License, or
//	(at your option) any later version.
//	
//	PRISM 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 PRISM; if not, write to the Free Software Foundation,
//	Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//	
//==============================================================================

package heuristics.decisiontree;

import heuristics.CollapseModelExplorer;
import heuristics.update.StateUpdate;

import java.util.*;

import explicit.CachedModelExplorer;
import parser.State;
import prism.PrismException;
import simulator.Choice;
import simulator.PrismModelExplorer;
import simulator.SimulatorEngine;
import simulator.TransitionList;

/**
 * PlayDT performs simulations on a decision tree to approximate its value.
 */
public class PlayDT
{

	private DecisionTree tree;
	private CollapseModelExplorer pme;
	private StateUpdate stateUpdate;
	private SimulatorEngine uncollapsed;
	private HashMap<State,Integer> successfullTrace;
	private ArrayList<State> successfullList;
	private Map<State,Integer> chosenAction;
	
	public PlayDT(SimulatorEngine uncollapsed, CollapseModelExplorer pme, StateUpdate stateUpdate, DecisionTree tree) {
		this.tree = tree;
		this.pme = pme;
		this.uncollapsed = uncollapsed;
		this.stateUpdate = stateUpdate;
		successfullTrace = new HashMap<State,Integer>();
		successfullList = new ArrayList<State>();
		this.chosenAction = new HashMap<State,Integer>();
	}
	
	public boolean doSimulation() throws PrismException {
		return doSimulation(false);
	}
	
	/**
	 * Performs the simulation.
	 * A simulation plays among the good actions uniformly until the target, a zero state, a self loop state is reached
	 * or a state is seen more than 100 times.
	 * The latter is necessary, because of undetected end components.
	 */
	public boolean doSimulation(boolean stickToAction) throws PrismException{
		stickToAction = false; //Disabled for the moment
		State s = pme.getDefaultInitialState();
		HashMap<State,Integer> seen = new HashMap<State,Integer>();
		Integer currentSeen = 0;
		Random r = new Random();
		while (!stateUpdate.isTarget(s) && !stateUpdate.isZero(s) && !stateUpdate.isOnlySelfLoop(s) && !(currentSeen > 100)) {
			currentSeen = seen.get(s);
			if (currentSeen == null) {
				currentSeen = 0;
			}
			currentSeen++;
			seen.put(s, currentSeen);
			pme.queryState(s);
			uncollapsed.initialisePath(s);
			int act = 0;
			if (stickToAction && chosenAction.containsKey(s)) {
				act = chosenAction.get(s);
			}
			else {
				ArrayList<Integer> goodActions = new ArrayList<Integer>();
				double bestOfTheRestValue = 0.0;
				int bestOfTheRest = -1;
				TransitionList trans = uncollapsed.getTransitionList();
				for (int i = 0; i < trans.getNumChoices(); i++) {
					Choice c = trans.getChoice(i);
					String module = "";
					if (c.getModuleOrActionIndex() > 0) module = "synchronised";
					else module = c.getModuleOrAction();
					String name = c.getCommandString();
					boolean classif = false;
					classif = tree.classify(s,module,name);
					if (classif) goodActions.add(i);
					else {
						double chanceGood = 0.0;
						chanceGood = tree.chanceGood(s,module,name);
						if (chanceGood > bestOfTheRestValue) {
							bestOfTheRest = i;
							bestOfTheRestValue = chanceGood;
						}
					}
				}
				
				
				if (goodActions.isEmpty()) {
					if (bestOfTheRest >= 0 && !pme.isInEC()) act = bestOfTheRest;
					else act = r.nextInt(trans.getNumChoices());
				}
				else act = goodActions.get(r.nextInt(goodActions.size()));
				if (stickToAction) chosenAction.put(s,act);
			}
			s = pme.computeChoiceTargetDistrNoEC(act);
		}
		pme.queryState(s);
		return stateUpdate.isTarget(s);
	}
	
	public HashMap<State,Integer> getSuccTrace() {
		return successfullTrace;
	}
	
	public ArrayList<State> getSuccList() {
		return successfullList;
	}
}
