package sso;

import java.sql.*;
import java.util.*;
import sso.event.*;

public class Persistant
{
	protected int		id;
	protected long		created_time;
	protected long		checkpoint_time;
	protected Vector 	storeListeners;

	/**
	 *  Constructor. Not intended to be publically called.
	 */
	protected Persistant()
	{
		storeListeners = new Vector();
System.out.println("Persistant constructor called.");
	}

	/**
     *  Factory method. Intended for new object only.
     *  For loading objects from the database, use the factory method
     *  load().
     */
	public static Persistant createPersistant()
	{
		Persistant p = new Persistant();

		p.init();

		return p;
	}

	/**
	 *  For creating new instances of this object only.
	 *  Called by create().
	 */
	protected void init()
	{
System.out.println("Persistant.init() called.");
		register();
		created_time = System.currentTimeMillis();
		checkpoint_time = 0;
	}

	/**
	 *  Get the object's unique id.
     */
	public int getID()
	{
		return id;
	}

	/**
	 *  Get a unique ID for the object.
	 */
	public void register()
	{
		// get the unique id
		id = Registry.register(this);

		// register this object as being of this type - only used for
		// reflection (dropped)
		// registerType();

		// create the necessary rows to allow this to store itself
		createRows();
	} 

	/**
	 *  Remove the object from the registry and database.
	 *  Set the unique id to invalid object id.
	 */ 
	public void unregister()
	{
		// remove type rows from registry
		Statement stmt = null;

		try
		{
			stmt = Persistant.getStatement();

			// DROPPED
			//stmt.executeUpdate("DELETE FROM ID_Type WHERE id=" + id);

			stmt.executeUpdate("DELETE FROM Persistant WHERE id=" + id);

			stmt.close();
			stmt = null;
		}
		catch (SQLException e)
		{
			System.err.println("Trying to delete from Persistant (" + id + "): " + e.getMessage());
		}

		if (stmt != null)
		{
			try
			{
				stmt.close();
			}
			catch (SQLException e)
			{
				//
			}

			stmt = null;
		}
		
		Registry.unregister(this);
		id = Registry.ID_INVALID;
	}

	/**
	 *  Register the object's type into the database to ensure that loads and
	 *  stores work properly.
	 */
	// TEST TO MAKE SURE THIS WORKS PROPERLY WITH super.registerType()
	// *** DROPPED *** This is applicable for reflection. While doable, I'm
	// using the factory static Obj.createObj(int id) instead. Not as dynamic,
	// but worlds simpler for a static MUDbase
/*
	protected void registerType()
	{
		Statement stmt = null;
	
		try
		{
			stmt = Persistant.getStatement();
			stmt.executeUpdate("INSERT INTO ID_Type SET id=" + id + ", type='" + getClass().getName() + "'");
			stmt.close();
			stmt = null;
		}
		catch (SQLException e)
		{
			System.err.println("Attempting to save ID_Type: " +
			e.getMessage());
		}
	
		// close db stmt and null for the sake of the gc
		if (stmt != null)
		{
			try
			{
				stmt.close();
			}
			catch (SQLException e)
			{
				//
			}

			stmt = null;
		}
	}
*/

	/**
	 *  Create the rows needed to store this object.
	 */
	protected void createRows()
	{
System.out.println("Persistant.createRows() called.");
		Statement stmt = null;

		try
		{
			stmt = Persistant.getStatement();

			stmt.executeUpdate("INSERT INTO Persistant (id, created_time, checkpoint_time) VALUES (" + id + ", -1, -1)");

			stmt.close();
			stmt = null;
		}
		catch (SQLException e)
		{
			System.err.println("Trying to insert a row to store this (" + id + "): " + e.getMessage());
		}

		if (stmt != null)
		{
			try
			{
				stmt.close();
			}
			catch (SQLException e)
			{
				//
			}

			stmt = null;
		}
	}
	
	/**
	 *  Store the object in the database.
	 */
	public void store()
	{

System.out.println("Persistant.store() called.");

		Statement stmt = null;
	
			// it's easier to store these by simply assigning them into the
			// database rather than using reflection. It's possible to use
			// reflection, but then you depend on the serializable interface to
			// pull and retrieve binary representations of the field members
			// (or the entire object) to/from the database. This should be
			// faster, as well as easier to understand.
		try
		{
			stmt = Persistant.getStatement();

			// use a StringBuffer because it's faster, less memory intensive
			// than String
			StringBuffer sbSql = new StringBuffer("UPDATE Persistant SET created_time=");
			sbSql.append(String.valueOf(created_time));
			sbSql.append(", checkpoint_time=");
			sbSql.append(String.valueOf(System.currentTimeMillis()));
			sbSql.append(" WHERE id=");
			sbSql.append(String.valueOf(id));

			stmt.executeUpdate(sbSql.toString());
		}
		catch (SQLException e)
		{
			System.err.println("Trying to write Persistant (" + id + ") to db: " +
			e.getMessage());
		}

		// gc
		if (stmt != null)
		{
			try
			{
				stmt.close();
			}
			catch (SQLException e)
			{
				//
			}

			stmt = null;
		}
	
		// fire a stored event
		stored();
	}

	/**
 	 *  Factory method.
	 *  Retrieve the object from the database.
	 */
	public static Persistant loadPersistant(int id)
	{
		Persistant p = null;

		if (Registry.isLoaded(id))
		{
			p = (Persistant)Registry.get(id);
		}
		else
		{
			p = new Persistant();
			p.id = id;
			p.load();
		}

		return p;
	}

	protected void load()
	{
		Statement stmt = null;
		ResultSet rs = null;
		
		try
		{
			stmt = Persistant.getStatement();
			rs = stmt.executeQuery("SELECT * FROM Persistant WHERE id=" +
getID());

			while (rs.next())
			{
				created_time = rs.getLong("created_time");
				checkpoint_time = rs.getLong("checkpoint_time");
			}

			rs.close();
			rs = null;

			stmt.close();
			stmt = null;
		}
		catch (SQLException e)
		{
			System.err.println("Trying to load Persistant (" + getID() + "): " +
			e.getMessage());
		}

		if (rs != null)
		{
			try
			{
				rs.close();
			}
			catch (SQLException e)
			{
				//
			}

			rs = null;
		}

		if (stmt != null)
		{
			try
			{
				stmt.close();
			}
			catch (SQLException e)
			{
				//
			}

			stmt = null;
		}
		

		// tell the registry that this object is loaded
		Registry.markLoaded(this);

	}
			

	/**
	 *  Get a database statement.
	 */
	protected static Statement getStatement() throws SQLException
	{
		return Registry.getConnection().createStatement();
	}

	/**
	 *  Fires a new stored event.
	 */
	protected void stored()
	{
		StoreEvent evt = new StoreEvent(this);

		Vector list = (Vector)storeListeners.clone();
		StoreListener sl;

		for (int i = 0; i < list.size(); i++)
		{
			sl = (StoreListener)list.elementAt(i);

			sl.notifyStore(evt);
		}

		sl = null;
		list = null;
		evt = null;
	}

	/**
	 *  Add listener for stored events.
	 */
	public void addStoreListener(StoreListener l)
	{
		storeListeners.addElement(l);
	}

	/**
	 *  Remove listener for stored events.
	 */
	public void removeStoreListener(StoreListener l)
	{
		storeListeners.removeElement(l);
	}

	/**
	 *  Tester.
	 */
	public static void main(String [] args)
	{
		System.out.println("...creating persistant...");
		Persistant po = Persistant.createPersistant();

		System.out.println("...storing persistant...");
		po.store();

		try
		{
			Thread.sleep(30000);
		}
		catch (InterruptedException e)
		{
			//
		}

		int id = po.getID();

		po = null;


		System.out.println("...reloading persistant...");
		po = Persistant.loadPersistant(id);

		System.out.println(po.getID());
		System.out.println(po.created_time);
		System.out.println(po.checkpoint_time);

		System.out.println("...reloading persistant (again, test)...");
		po = Persistant.loadPersistant(id);

		System.out.println("...unregistering persistant...");
		po.unregister();
	}
} 

