/*
 * Copyright (c) 2012 Ryan Vogt <vogt@cs.ualberta.ca>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

import java.util.*;

/**
 * The <code>DuelState</code> class represents all of the different states that
 * Zell's Duel command in Final Fantasy VIII can be in after a sequence of
 * actions.
 */
public class DuelState
{
    /**
     * Each state is named after the action that resulted in the transition
     * into that state.
     */
    private static final String[] NAMES =
    {
        "Punch Rush",
        "Booya",
        "Heel Drop",
        "Dolphin Blow",
	"Meteor Strike",
	"Mach Kick",
	"Mach Kick",
	"Meteor Strike",
	"Punch Rush",
	"Dolphin Blow",
	"Heel Drop",
	"Dolphin Blow",
	"Booya",
	"Heel Drop",
	"Booya",
	"Punch Rush",
	"Mach Kick",
	"Punch Rush",
	"Meteor Strike",
	"Heel Drop",
	"Dolphin Blow",
        "Burning Rave",
        "Meteor Barret",
        "My Final Heaven",
        "Different Beat"
    };

    /**
     * The indices of the states into which each state can transition.
     */
    private static final int[][] TRANSITIONS =
    {
	{1, 5, 3},
	{0, 2, 4},
	{1, 6, 7},
	{10, 5, 22},
	{12, 2, 11},
	{8, 9, 1},
	{15, 13, 4},
	{14, 9, 23},
	{19, 3, 18},
	{1, 2, 4},
	{0, 16, 22},
	{0, 1, 16},
	{17, 11, 24},
	{12, 20, 4},
	{19, 9, 21},
	{2, 7, 23},
	{0, 3, 22},
	{16, 11, 24},
	{19, 5, 21},
	{5, 9, 21},
	{12, 4, 24},
        {},
        {},
        {},
        {}
    };

    /**
     * The number of states in which Zell's Duel command may begin.
     */
    private static final int NUM_INITIAL_STATES = 2;

    /**
     * The single instance of all possible states.
     */
    private static final DuelState[] STATES = DuelState.initializeStates();

    /**
     * The index in the table of all possible states held by this state.
     */
    private int index;

    /**
     * Creates a new <code>DuelState</code> that will occupy the state table at
     * the given index.
     *
     * @param index the index at which the state will live in the table of
     * states.
     * @throws IllegalArgumentException if the given index is negative, or
     * greater than or equal than the total number of states.
     */
    private DuelState(int index)
    {
	if (this.index < 0 || this.index >= DuelState.NAMES.length)
	    throw new IllegalArgumentException("Invalid duel state index");
	this.index = index;
    }

    /**
     * Returns the index of this state in the state table.
     *
     * @return the index of this state in the state table.
     */
    public int getIndex()
    {
	return this.index;
    }

    /**
     * Returns the name of the state, which is Zell's action that caused the
     * transition to this state followed by the index of this state in the
     * state table.
     *
     * @return a standard name for this state.
     */
    public String getName()
    {
	return DuelState.NAMES[this.index] + " [" + this.index + "]";
    }

    /**
     * Returns the standard name for this state.
     *
     * @return the standard name for this state.
     */
    public String toString()
    {
	return this.getName();
    }

    /**
     * Returns an iterator over all possible states to which this state could
     * transition.
     *
     * @return an iterator over all possible next states.
     */
    public Iterator<DuelState> nextStates()
    {
	return new DuelStateTxIterator();
    }

    /**
     * Returns an iterator over all the possible states.
     *
     * @return an iterator over all possible states.
     */
    public static Iterator<DuelState> getStates()
    {
	return new DuelStateIterator(DuelState.STATES.length);
    }

    /**
     * Returns an iterator over all the possible initial states in which a
     * Duel may begin.
     *
     * @return an iterator over all the possible initial states.
     */
    public static Iterator<DuelState> getInitialStates()
    {
	return new DuelStateIterator(DuelState.NUM_INITIAL_STATES);
    }

    /**
     * Returns the total number of possible states in which a Duel may be.
     *
     * @return the total number of possible Duel states.
     */
    public static int numStates()
    {
	return DuelState.STATES.length;
    }

    /**
     * Returns the state at the given index in the table of possible states.
     *
     * @param index the index of the state to return.
     * @throws ArrayIndexOutOfBoundsException if the given index is negative,
     * or greater than or equal to the number of states in the state table.
     */
    public static DuelState getState(int index)
    {
	return DuelState.STATES[index];
    }

    /**
     * Creates the single instances of each possible state and returns them as
     * an array.
     *
     * @return an array of all possible states.
     */
    private static DuelState[] initializeStates()
    {
	DuelState[] arr = new DuelState[DuelState.NAMES.length];
	for (int i = 0; i < arr.length; i++)
	    arr[i] = new DuelState(i);
	return arr;
    }

    /**
     * The <code>DuelStateIterator</code> class iterates over the first states
     * in the state table, up to a certain limit.
     */
    private static class DuelStateIterator implements Iterator<DuelState>
    {
	/**
	 * The index of the next state to be returned by this iterator.
	 */
	int onState;

	/**
	 * The total number of states to return by this iterator.
	 */
	int numToShow;

	/**
	 * Creates a new <code>DuelStateIterator</code> that will return the
	 * first states from the state table.
	 *
	 * @param numToShow the number of elements to show from the state
	 * table.
	 * @throws IllegalArgumentException if the number of elements to return
	 * is negative, or greater than or equal to the number of elements in
	 * the state table.
	 */
	public DuelStateIterator(int numToShow)
	{
	    if (numToShow < 0 || numToShow >= DuelState.STATES.length)
		throw new IllegalArgumentException("Invalid number of " +
						   "states to return");
	    this.onState = 0;
	    this.numToShow = numToShow;
	}

	/**
	 * Returns whether or not this iterator has more elements to display.
	 *
	 * @return <code>true</code> if this iterator has more elements to
	 * return, otherwise false.
	 */
	public boolean hasNext()
	{
	    return (this.onState < this.numToShow);
	}

	/**
	 * Returns the next state in the iteration.
	 *
	 * @return the next state in the iteration.
	 * @throws NoSuchElementException if there are no more states for the
	 * iteration to return.
	 */
	public DuelState next()
	{
	    if (this.onState < this.numToShow)
		return DuelState.STATES[this.onState++];
	    else
		throw new NoSuchElementException("No more states");
	}

	/**
	 * An unsupported operation.
	 *
	 * @throws UnsupportedOperationException.
	 */
	public void remove()
	{
	    throw new UnsupportedOperationException("Remove unsupported");
	}
    }

    /**
     * The <code>DuelStateTxIterator</code> class iterates over the states into
     * which any other state can transform.
     */
    private class DuelStateTxIterator implements Iterator<DuelState>
    {
	/**
	 * The index of the next potential-transition state to be returned by
	 * this iterator.
	 */
	int onTx;

	/**
	 * All of the possible states into which this state can transform.
	 */
	int[] txs;

	/**
	 * Creates a new <code>DuelStateTxIterator</code> for this state.
	 */
	public DuelStateTxIterator()
	{
	    this.onTx = 0;
	    this.txs = DuelState.TRANSITIONS[DuelState.this.index];
	}

	/**
	 * Returns whether or not this iterator has more elements to display.
	 *
	 * @return <code>true</code> if this iterator has more elements to
	 * return, otherwise false.
	 */
	public boolean hasNext()
	{
	    return (this.onTx < txs.length);
	}

	/**
	 * Returns the next state in the iteration.
	 *
	 * @return the next state in the iteration.
	 * @throws NoSuchElementException if there are no more states for the
	 * iteration to return.
	 */
	public DuelState next()
	{
	    if (this.onTx < txs.length)
		return DuelState.STATES[this.txs[this.onTx++]];
	    else
		throw new NoSuchElementException("No more transitions");
	}

	/**
	 * An unsupported operation.
	 *
	 * @throws UnsupportedOperationException.
	 */
	public void remove()
	{
	    throw new UnsupportedOperationException("Remove unsupported");
	}
    }
}