/* License
 * 
 * Copyright 1994-2004 Sun Microsystems, Inc. All Rights Reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *  
 *  * Redistribution of source code must retain the above copyright notice,
 *	this list of conditions and the following disclaimer.
 * 
 *  * Redistribution in binary form must reproduce the above copyright notice,
 *	this list of conditions and the following disclaimer in the
 *	documentation and/or other materials provided with the distribution.
 * 
 * Neither the name of Sun Microsystems, Inc. or the names of contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 *  
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
 * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN")
 * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
 * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
 * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
 * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 *  
 * You acknowledge that this software is not designed, licensed or intended
 * for use in the design, construction, operation or maintenance of any
 * nuclear facility. 
 */

package j2medeveloper;

import java.io.*;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.rms.RecordStoreException;
import javax.microedition.rms.RecordStore;

public class FSM implements CommandListener {

	MIDlet midlet;
	Display display;
	private String FSM_RSTORE = "FSM_RSTORE";

	// Finite states

	public final static int FSM_STATE_UNDEFINED = -1;
	public final static int FSM_STATE_STARTED = 1;
	public final static int FSM_STATE_PAUSED = 2;
	public final static int FSM_STATE_DESTROYED = 3;
	public final static int FSM_STATE_RESTARTED = 4;
	public final static int FSM_STATE_READY = 5;
	public final static int FSM_STATE1 = 6;
	public final static int FSM_STATE2 = 7;
	protected static int mPreviousState = FSM_STATE_UNDEFINED;
	protected static int mCurrentState = FSM_STATE_UNDEFINED;

	// Application Screens
	private javax.microedition.lcdui.List readyScreen;
	private javax.microedition.lcdui.List state1Screen;
	private javax.microedition.lcdui.List state2Screen;

	//  Screen commands

	public static Command CMD_READY_SCREEN_EXIT = null;
	public static Command CMD_READY_SCREEN_GOSTATE1 = null;
	public static Command CMD_READY_SCREEN_GOSTATE2 = null;
	public static Command CMD_STATE1_SCREEN_OK = null;
	public static Command CMD_STATE2_SCREEN_OK = null;

	public FSM(MIDlet midlet, Display display) {
		this.midlet = midlet;
		this.display = display;
	}


	/*
	 *  Start of FSM transition function
	 */
	/**
	 *  This is the finite state machine (FSM) for the MIDlet. This machine
	 *  supports returning to a previous state from the paused and destroyed
	 *  states.
	 *
	 *@param  currentState     current FSM state.
	 *@param  transitionState  transition FSM state.
	 */
	public void transition(int currentState, int transitionState) {
		System.out.println("FSM.transition");
		System.out.println(" currentState is    " + currentState);
		System.out.println(" transitionState is " + transitionState);

		try {
			// The next switch statement implements the acceptor,
			// recognizer, and transducer, for decoding an input state
			// and transitioning to a new state.
			switch (transitionState) {

			case FSM_STATE_STARTED:
				;
				System.out.println("FSM_STATE_START");
				// The started state is a fresh state, and requires
				// proper application initialization such as
				// allocating any required objects.

				// Here initialize any required objects
				// ...

				// Initialize screens.
				initializeScreens();

				//  Load current state from record store.
				loadState();

				//  Transition to previous state.
				if (mCurrentState == FSM_STATE_UNDEFINED) {
					transition(transitionState, FSM_STATE_READY);
				} else {
					transition(mPreviousState, mCurrentState);
				}

				return;
			// need not remember this state.

			case FSM_STATE_READY:
				System.out.println("FSM_STATE_READY");
				// Here do application-specific ready processing.
				// In our example we just display the initial
				// application screen.
				display.setCurrent(readyScreen);
				break;
			case FSM_STATE_RESTARTED:
				System.out.println("FSM_STATE_RESTART");
				// This state is invoked when from paused state.
				// All prev. objects should still be around, so
				// there is no need to reinitialize. Just load the
				// state, and return to previous state.

				//  Load current state from record store.
				loadState();

				//  Transition to previous state.
				transition(mPreviousState, mCurrentState);

				return;
			// no need to remember; so to return
			// go to real previous state.

			case FSM_STATE_PAUSED:
				System.out.println("FSM_STATE_PAUSED");
				// Typically a transition to the paused state
				// indicates the application is suspended, and
				// resources remain in memory as is, so returning
				// from paused only requires going back to previous
				// state.
				return;
			case FSM_STATE_DESTROYED:
				System.out.println("FSM_STATE_DESTROYED");
				// Here do application cleanup such as closing
				// connections, exiting threads, and so on.
				// ...

				return;
			// No need to remember destroyed state.

			case FSM_STATE1:
				System.out.println("FSM_STATE1");
				// Here do application-specific processing.
				// In this example we just display a screen.
				display.setCurrent(state1Screen);
				break;
			case FSM_STATE2:
				System.out.println("FSM_STATE2");
				// Here do application-specific processing.
				// In this example we just display a screen.
				display.setCurrent(state2Screen);
				break;
			default:
				return;
			// Unrecognized symbols; reject.

			}
			// switch (transitionState)

			// Complete state transition...
			mPreviousState = currentState;
			mCurrentState = transitionState;

			// Preserve application state
			persistState();

		} catch (Exception e) {
			System.out.println("FSM Exception: " + e);
		}
	}


	/*
	 *  End of FSM Transition Function
	 */
	/**
	 *  Command Listener.
	 *
	 *@param  c  is the LCDUI Command.
	 *@param  d  is the source Displayable.
	 */
	public void commandAction(final Command c, Displayable d) {

		try {
			///////////////////////
			// Main Screen Commands
			///////////////////////
			if (c == CMD_READY_SCREEN_EXIT) {
				transition(mCurrentState, FSM_STATE_DESTROYED);
			} else if (c == CMD_READY_SCREEN_GOSTATE1) {
				transition(mCurrentState, FSM_STATE1);
			} else if (c == CMD_READY_SCREEN_GOSTATE2) {
				transition(mCurrentState, FSM_STATE2);
			} else if (c == CMD_STATE1_SCREEN_OK) {
				transition(mCurrentState, FSM_STATE_READY);
			} else if (c == CMD_STATE2_SCREEN_OK) {
				transition(mCurrentState, FSM_STATE_READY);
			}
		} catch (Exception e) {
		}
	}

	private void initializeScreens() {

		CMD_READY_SCREEN_EXIT = new Command("Exit", Command.SCREEN, 1);
		CMD_READY_SCREEN_GOSTATE1 = new Command("Go to State 1",
				Command.SCREEN, 1);
		CMD_READY_SCREEN_GOSTATE2 = new Command("Go to State 2",
				Command.SCREEN, 1);
		CMD_STATE1_SCREEN_OK = new Command("Back", Command.SCREEN, 1);
		CMD_STATE2_SCREEN_OK = new Command("Back", Command.SCREEN, 1);

		readyScreen = new javax.microedition.lcdui.List("Ready Screen",
				List.IMPLICIT);
		readyScreen.append("Ready Screen Item 1", null);
		readyScreen.append("Ready Screen Item 2", null);
		readyScreen.append("Ready Screen Item n", null);
		readyScreen.addCommand(CMD_READY_SCREEN_EXIT);
		readyScreen.addCommand(CMD_READY_SCREEN_GOSTATE1);
		readyScreen.addCommand(CMD_READY_SCREEN_GOSTATE2);
		readyScreen.setCommandListener(this);

		state1Screen = new javax.microedition.lcdui.List(
				"State1 Screen", List.IMPLICIT);
		state1Screen.append("State 1 Screen Item 1", null);
		state1Screen.append("State 1 Screen Item 2", null);
		state1Screen.append("State 1 Screen Item n", null);
		state1Screen.addCommand(CMD_STATE1_SCREEN_OK);
		state1Screen.setCommandListener(this);

		state2Screen = new javax.microedition.lcdui.List(
				"State2 Screen", List.IMPLICIT);
		state2Screen.append("State 2 Screen Item 1", null);
		state2Screen.append("State 2 Screen Item 2", null);
		state2Screen.append("State 2 Screen Item n", null);
		state2Screen.addCommand(CMD_STATE2_SCREEN_OK);
		state2Screen.setCommandListener(this);

	}

	private void persistState() {
		RecordStore pStateStore = null;
		DataOutputStream dos;
		ByteArrayOutputStream baos;
		try {
			RecordStore.deleteRecordStore(FSM_RSTORE);
		} catch (RecordStoreException e) {
		}
		try {
			pStateStore = RecordStore.openRecordStore(FSM_RSTORE, true);
			baos = new ByteArrayOutputStream();
			dos = new DataOutputStream(baos);
			dos.writeInt(mPreviousState);
			dos.writeInt(mCurrentState);
			pStateStore.addRecord(baos.toByteArray(), 0, baos.size());
			System.out.println("App. State Saved...");
		} catch (IOException ioe) {
			//... Handle the exception
			System.out.println("IOException " + ioe);
		} catch (RecordStoreException rse) {
			//... Handle the exception
			System.out.println("RecordStoreException " + rse);
		} catch (Exception e) {
			//... Handle the exception
			System.out.println("Exception " + e);
		} finally {
			try {
				if (pStateStore != null) {
					pStateStore.closeRecordStore();
				}
			} catch (Exception ignore) {
				//  Ignore
				System.out.println("Exception is " + ignore);
			}
		}
	}

	private void loadState() {
		RecordStore pStateStore = null;
		DataInputStream dis;
		ByteArrayInputStream bais;
		try {
			pStateStore = RecordStore.openRecordStore(FSM_RSTORE, false);
			/*
			 *  Deserialize data
			 */
			bais = new ByteArrayInputStream(pStateStore.getRecord(1));
			dis = new DataInputStream(bais);
			mPreviousState = dis.readInt();
			mCurrentState = dis.readInt();
			System.out.println("App. State Loaded...");
		} catch (IOException ioe) {
			//... Handle the exception
			System.out.println("IOException " + ioe);
		} catch (RecordStoreException rse) {
			//... Handle the exception
			System.out.println("RecordStoreException " + rse);
		} catch (Exception e) {
			//... Handle the exception
			System.out.println("Exception " + e);
		} finally {
			try {
				if (pStateStore != null) {
					pStateStore.closeRecordStore();
				}
			} catch (Exception ignore) {
				//  Ignore
				System.out.println("Exception is " + ignore);
			}
		}
	}
}
/*
 *  End of FSM.java
 */