package net.morilib.nina;

import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;

import net.morilib.automata.AlphabetsNotDiscreteException;
import net.morilib.automata.DFA;
import net.morilib.automata.DFAState;
import net.morilib.automata.NFA;
import net.morilib.automata.NFAEdges;
import net.morilib.automata.NFAState;
import net.morilib.automata.TextBound;
import net.morilib.automata.dfa.DFAs;
import net.morilib.nina.DFABuilder.DBS;
import net.morilib.range.Interval;
import net.morilib.range.Range;
import net.morilib.util.Tuple2;

class BuildMachine
implements DFA<Object, Object, Void>, NFA<Object, Object, Void> {

	String name;
	private DFAState<Object, Object, Void> initial;

	private transient Set<Object> allStates;

	BuildMachine(String name, DFAState<Object, Object, Void> init) {
		this.name = name;
		initial   = init;
		allStates = new HashSet<Object>(DFAs.allStateRanges(init));
	}

	@Override
	public boolean isState(NFAState o) {
		return allStates.contains(o);
	}

	@Override
	public Set<NFAState> getStates(NFAState s, Object a) {
		DFAState<Object, Object, Void> d;

		if(!isState(s)) {
			return Collections.emptySet();
		} else if((d = ((DBS)s).go(a)).isDead()) {
			return Collections.emptySet();
		} else {
			return Collections.singleton((NFAState)d);
		}
	}

	@Override
	public Set<NFAState> getStates(NFAState s, Range r) {
		Set<NFAState> x;
		DBS d;

		if(!isState(s)) {
			return Collections.emptySet();
		} else {
			d = (DBS)s;
			x = new HashSet<NFAState>();
			for(Tuple2<Interval, DBS> t : d.edges.entries()) {
				for(Interval v : r.intervals()) {
					if(t.getA().equals(v)) {
						x.add(t.getB());
					}
				}
			}
			return x;
		}
	}

	/* (non-Javadoc)
	 * @see net.morilib.automata.NFA#getStates(net.morilib.automata.NFAState, java.util.EnumSet)
	 */
	@Override
	public Set<NFAState> getStates(NFAState s,
			EnumSet<TextBound> b) {
		return Collections.emptySet();
	}

	/* (non-Javadoc)
	 * @see net.morilib.automata.NFA#getStatesEpsilon(net.morilib.automata.NFAState)
	 */
	@Override
	public Set<NFAState> getStatesEpsilon(NFAState s) {
		return Collections.emptySet();
	}

	/* (non-Javadoc)
	 * @see net.morilib.automata.NFA#getStatesBound(net.morilib.automata.NFAState, java.util.EnumSet)
	 */
	@Override
	public Set<NFAState> getStatesBound(NFAState s,
			EnumSet<TextBound> b) {
		return Collections.emptySet();
	}

	/* (non-Javadoc)
	 * @see net.morilib.automata.NFA#getInitialStates()
	 */
	@Override
	public Set<NFAState> getInitialStates() {
		return Collections.singleton((NFAState)initial);
	}

	/* (non-Javadoc)
	 * @see net.morilib.automata.NFA#isInitialState(net.morilib.automata.NFAState)
	 */
	@Override
	public boolean isInitialState(NFAState o) {
		return initial.equals(o);
	}

	/* (non-Javadoc)
	 * @see net.morilib.automata.NFA#isFinal(net.morilib.automata.NFAState)
	 */
	@Override
	public boolean isFinal(NFAState s) {
		return isState(s) && ((DBS)s).isAccepted();
	}

	/* (non-Javadoc)
	 * @see net.morilib.automata.NFA#isFinalAny(java.util.Set)
	 */
	@Override
	public boolean isFinalAny(Set<NFAState> ss) {
		for(NFAState s : ss) {
			if(isFinal(s))  return true;
		}
		return false;
	}

	/* (non-Javadoc)
	 * @see net.morilib.automata.NFA#getEdges(net.morilib.automata.NFAState)
	 */
	@Override
	public NFAEdges<Object> getEdges(NFAState s) {
		return isState(s) ? (DBS)s : null;
	}

	/* (non-Javadoc)
	 * @see net.morilib.automata.NFA#nextAlphabets(net.morilib.automata.NFAState)
	 */
	@Override
	public Set<Interval> nextAlphabets(NFAState s) {
		Set<Interval> x = new HashSet<Interval>();
		DBS d;

		if(isState(s)) {
			d = (DBS)s;
			for(Tuple2<Interval, DBS> t : d.edges.entries()) {
				x.add(t.getA());
			}
			return x;
		} else {
			return Collections.emptySet();
		}
	}

	/* (non-Javadoc)
	 * @see net.morilib.automata.NFA#nextAlphabets(java.util.Set)
	 */
	@Override
	public Iterable<Interval> nextAlphabets(Set<NFAState> ss) {
		Set<Interval> x = new HashSet<Interval>();

		for(NFAState s : ss) {
			x.addAll(nextAlphabets(s));
		}
		return x;
	}

	/* (non-Javadoc)
	 * @see net.morilib.automata.NFA#nextDiscreteAlphabets(net.morilib.automata.NFAState)
	 */
	@Override
	public Set<Object> nextDiscreteAlphabets(NFAState s) {
		throw new AlphabetsNotDiscreteException();
	}

	/* (non-Javadoc)
	 * @see net.morilib.automata.NFA#nextDiscreteAlphabets(java.util.Set)
	 */
	@Override
	public Iterable<Object> nextDiscreteAlphabets(
			Set<NFAState> s) {
		throw new AlphabetsNotDiscreteException();
	}

	/* (non-Javadoc)
	 * @see net.morilib.automata.NFA#getAcceptedStates()
	 */
	@Override
	public Set<NFAState> getAcceptedStates() {
		Set<NFAState> x = new HashSet<NFAState>();

		for(Object s : allStates) {
			if(((DBS)s).isAccepted())  x.add((DBS)s);
		}
		return x;
	}

	/* (non-Javadoc)
	 * @see net.morilib.automata.NFA#getMatchTag(net.morilib.automata.NFAState)
	 */
	@Override
	public Set<Void> getMatchTag(NFAState s) {
		return Collections.emptySet();
	}

	/* (non-Javadoc)
	 * @see net.morilib.automata.NFA#getMatchTagEnd(net.morilib.automata.NFAState)
	 */
	@Override
	public Set<Void> getMatchTagEnd(NFAState s) {
		return Collections.emptySet();
	}

	/* (non-Javadoc)
	 * @see net.morilib.automata.NFA#getAccept(net.morilib.automata.NFAState)
	 */
	@Override
	public Set<Object> getAccept(NFAState s) {
		if(isAccepted(s)) {
			return Collections.singleton((Object)s);
		} else {
			return Collections.emptySet();
		}
	}

	/* (non-Javadoc)
	 * @see net.morilib.automata.NFA#isAccepted(net.morilib.automata.NFAState)
	 */
	@Override
	public boolean isAccepted(NFAState s) {
		return isState(s) && ((DBS)s).isAccepted();
	}

	/* (non-Javadoc)
	 * @see net.morilib.automata.DFA#getInitialState()
	 */
	@Override
	public DFAState<Object, Object, Void> getInitialState() {
//		initial.sortirMoore();
		return initial;
	}

}