//==============================================================================
//	
//	Copyright (c) 2013-
//	Authors:
//	* Dave Parker <d.a.parker@cs.bham.ac.uk> (University of Birmingham/Oxford)
//	
//------------------------------------------------------------------------------
//	
//	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 explicit;

import java.util.*;
import java.util.Map.Entry;

import explicit.rewards.MCRewards;
import parser.State;
import parser.Values;
import prism.ModelType;
import prism.PrismException;
import strat.MDStrategy;

/**
* Explicit-state representation of a DTMC, constructed (implicitly)
* from an MDP and a memoryless deterministic (MD) adversary.
* This class is read-only: most of the data is pointers to other model info.
*/
public class DTMCFromMDPAndMDStrategy extends DTMCExplicit
{
	// Parent MDP
	protected MDP mdp;
	// MD strategy
	protected MDStrategy strat;

	protected List<Distribution> trans;
	
	/**
	 * Constructor: create from MDP and memoryless adversary.
	 */
	public DTMCFromMDPAndMDStrategy(MDP mdp, MDStrategy strat)
	{
		this.mdp = mdp;
		this.numStates = mdp.getNumStates();
		this.strat = strat;
		initialise(numStates);
	}

	@Override
	public void initialise(int numStates)
	{
		super.initialise(numStates);
		trans = new ArrayList<Distribution>(numStates);
		for (int i = 0; i < numStates; i++) {
			int numChoices = mdp.getNumChoices(i);
			Distribution distr = new Distribution();
			for (int j = 0; j < numChoices; j++) {
				Iterator<Entry<Integer,Double>> k = mdp.getTransitionsIterator(i, j);
				while (k.hasNext()) {
					Entry<Integer,Double> n = k.next();
					distr.add(n.getKey(), n.getValue() / numChoices);
				}
			}
			trans.add(distr);
		}
	}
	
	@Override
	public void buildFromPrismExplicit(String filename) throws PrismException
	{
		throw new PrismException("Not supported");
	}

	// Accessors (for Model)

	public ModelType getModelType()
	{
		return ModelType.DTMC;
	}

	public int getNumStates()
	{
		return mdp.getNumStates();
	}

	public int getNumInitialStates()
	{
		return mdp.getNumInitialStates();
	}

	public Iterable<Integer> getInitialStates()
	{
		return mdp.getInitialStates();
	}

	public int getFirstInitialState()
	{
		return mdp.getFirstInitialState();
	}

	public boolean isInitialState(int i)
	{
		return mdp.isInitialState(i);
	}

	public boolean isDeadlockState(int i)
	{
		return mdp.isDeadlockState(i);
	}

	public List<State> getStatesList()
	{
		return mdp.getStatesList();
	}

	public Values getConstantValues()
	{
		return mdp.getConstantValues();
	}

	public int getNumTransitions()
	{
		int numTransitions = 0;
		for (int s = 0; s < numStates; s++)
			if (strat.isChoiceDefined(s))
				numTransitions += mdp.getNumTransitions(s, strat.getChoiceIndex(s));
		return numTransitions;
	}

	public Iterator<Integer> getSuccessorsIterator(final int s)
	{
		throw new RuntimeException("Not implemented yet");
	}

	public boolean isSuccessor(int s1, int s2)
	{
		throw new RuntimeException("Not implemented yet");
	}

	public boolean allSuccessorsInSet(int s, BitSet set)
	{
		throw new RuntimeException("Not implemented yet");
	}

	public boolean someSuccessorsInSet(int s, BitSet set)
	{
		throw new RuntimeException("Not implemented yet");
	}

	public int getNumChoices(int s)
	{
		// Always 1 for a DTMC
		return 1;
	}

	public void findDeadlocks(boolean fix) throws PrismException
	{
		// No deadlocks by definition
	}

	public void checkForDeadlocks() throws PrismException
	{
		// No deadlocks by definition
	}

	public void checkForDeadlocks(BitSet except) throws PrismException
	{
		// No deadlocks by definition
	}

	@Override
	public String infoString()
	{
		return mdp.infoString() + " + " + "???"; // TODO
	}

	@Override
	public String infoStringTable()
	{
		return mdp.infoString() + " + " + "???\n"; // TODO
	}

	// Accessors (for DTMC)

	public int getNumTransitions(int s)
	{
		return strat.isChoiceDefined(s) ? mdp.getNumTransitions(s, strat.getChoiceIndex(s)) : 0;
	}

	public Iterator<Entry<Integer, Double>> getTransitionsIterator(int s)
	{
		if (strat.isChoiceDefined(s)) {
//			System.out.println("ending up here1!");
			return mdp.getTransitionsIterator(s, strat.getChoiceIndex(s));
		} else {
//			System.out.println("ending up here2! " + strat.getChoiceIndex(s) + " choices: " + mdp.getNumChoices(s));
			if (strat.getChoiceIndex(s) == -2) {
				return trans.get(s).iterator();
			}
			else {
				// Empty iterator
				return new Iterator<Entry<Integer, Double>>()
				{
					@Override
					public boolean hasNext()
					{
						return false;
					}
	
					@Override
					public Entry<Integer, Double> next()
					{
						return null;
					}
	
					@Override
					public void remove()
					{
						throw new UnsupportedOperationException();
					}
				};
			}
		}
	}
	
	private class RandomIterator implements Iterator<Entry<Integer,Double>> {

		private int pointer;
		private int numChoices;
		private int s;
		private Iterator<Entry<Integer,Double>> iterator;
		
		public RandomIterator(int s) {
			this.s = s;
			pointer = 0;
			numChoices = mdp.getNumChoices(s);
			iterator = trans.get(s).iterator();
		}
		
		@Override
		public boolean hasNext()
		{
//			return pointer < mdp.getNumStates(); // && numChoices > 1;
			return iterator.hasNext();
		}

		@Override
		public Entry<Integer, Double> next()
		{
////			System.out.println("this choices: " + numChoices + " vs global choices: " + mdp.getNumChoices());
////			Iterator<Entry<Integer,Double>> i = mdp.getTransitionsIterator(s, pointer);
////			int[] trans = new Integer[];
//			Entry<Integer,Double> e = new AbstractMap.SimpleEntry<Integer,Double>(pointer,trans[pointer]);
////			System.out.println("action: " + mdp.getAction(s, pointer));
////			Entry<Integer,Double> e = new AbstractMap.SimpleEntry<Integer,Double>(pointer,0.00000001);
////			System.out.println("entry created: " + e + " state: " + s + " " + mdp.getStatesList().get(pointer));
//			pointer++;
//			return e;
			return iterator.next();
		}
		
	}
	
	@Override
	public void prob0step(BitSet subset, BitSet u, BitSet result)
	{
		int i;
		Distribution distr;
		for (i = 0; i < numStates; i++) {
			if (subset.get(i)) {
				distr = trans.get(i);
				result.set(i, distr.containsOneOf(u));
			}
		}
	}

	@Override
	public void prob1step(BitSet subset, BitSet u, BitSet v, BitSet result)
	{
		int i;
		Distribution distr;
		for (i = 0; i < numStates; i++) {
			if (subset.get(i)) {
				distr = trans.get(i);
				result.set(i, distr.containsOneOf(v) && distr.isSubsetOf(u));
			}
		}
	}

	@Override
	public double mvMultSingle(int s, double vect[])
	{
		return strat.isChoiceDefined(s) ? mdp.mvMultSingle(s, strat.getChoiceIndex(s), vect) : 0;
	}

	@Override
	public double mvMultJacSingle(int s, double vect[])
	{
		return strat.isChoiceDefined(s) ? mdp.mvMultJacSingle(s, strat.getChoiceIndex(s), vect) : 0;
	}

	@Override
	public double mvMultRewSingle(int s, double vect[], MCRewards mcRewards)
	{
		throw new RuntimeException("Not implemented yet");
		//return mdp.mvMultRewSingle(s, adv[s], vect);
	}

	@Override
	public void vmMult(double vect[], double result[])
	{
		throw new RuntimeException("Not implemented yet");
	}

	@Override
	public String toString()
	{
		throw new RuntimeException("Not implemented yet");
	}

	@Override
	public boolean equals(Object o)
	{
		throw new RuntimeException("Not implemented yet");
	}
}
