package sso;

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

public class Effect extends Persistant implements Runnable
{
	protected int		time_to_live;				// in SECONDS
	protected int		effect_type;
	protected Vector	victims;
	private Thread		tEffect;
	protected Vector	effectTriggerListeners;
	protected Vector	effectVictimChangeListeners;
	protected Vector	effectEndListeners;

	// EFFECT TYPES 
	public static final int	NOTHING			= 0x0;
	public static final int	INVULNERABLE	= 0x01;
	public static final int	INVISIBLE		= 0x02;
	public static final int	ARCHMAGE		= 0x04;
	public static final int	CATEYES			= 0x08;
	public static final int FLYING			= 0x10;
	public static final int	BLIND			= 0x20;
	public static final int	LYCANTHROPE		= 0x40;
	public static final int	ASLEEP			= 0x80;
	public static final int	DEAD			= 0x100;
	public static final int	CURSED			= 0x200;
	public static final int	BLESSED			= 0x400;
	public static final int	POISONED		= 0x800;
	public static final int	DISEASED		= 0x1000;
	public static final int	RESTING			= 0x2000;
	public static final int	GLOWING			= 0x4000;
	public static final int	DEAF			= 0x8000;
	public static final int	GAGGED			= 0x10000;
	public static final int	FROZEN			= 0x20000;
	public static final int	CRIMINAL		= 0x40000;
	public static final int	MURDERER		= 0x80000;
	public static final int	POSSESSED		= 0x100000;
	public static final int	GM				= 0x200000;
	public static final int	INSANE			= 0x400000;
	public static final int	WEAK			= 0x800000;
	public static final int	BUFFED			= 0x1000000;
	public static final int	SILENT			= 0x2000000;
	public static final int	BURNING			= 0x4000000;

	/**
     * Constructor. Should only be called by Factory methods.
	 */
	public Effect()
	{
		super();
		tEffect = new Thread(this);
		effectTriggerListeners = new Vector();
		effectVictimChangeListeners = new Vector();
		effectEndListeners = new Vector();
System.out.println("Effect constructor entered.");
	}

	/**
	 *  Factory method for making a new instance of Effect.
	 */
	public static Effect createEffect()
	{
		Effect ef = new Effect();

		ef.init();

		return ef;
	}

	/**
	 *  Initializer for factory method.
	 */
	public void init()
	{
		super.init();

		time_to_live = 0;
		victims = new Vector();
	}

	/**
     *  Gets the effect ttl
	 */
	public int getTimeToLive()
	{
		return time_to_live;
	}

	/**
	 *  Sets the effect ttl.
	 *  Can be used to short circuit or extend an effect's duration.
	 */
	public void setTimeToLive(int ttl)
	{
		time_to_live = ttl;
	}

	/**
	 *  Gets the effect type.
	 */
	public int getType()
	{
		return effect_type;
	}

	/**
	 *  Sets the effect type.
	 */
	public void setType(int type)
	{
		effect_type = type;
	}

	/**
	 *  Gets the list of effect victims.
	 */
	public Vector getVictims()
	{
		return victims;
	}

	/**
	 *  Adds an effect victim.
	 */
	public void addVictim(GameObject victim)
	{
		victims.addElement(victim);
	}

	/**
	 *  Removes an effect victim.
	 */
	public void removeVictim(GameObject victim)
	{
		victims.removeElement(victim);

		if (victims.size() < 1)
		{
			// no victims yet...destroy this effect
			stop();
			unregister();
		}
	}

	/**
	 *  Starts the effect.
	 */
	public void start()
	{
		tEffect.start();
	}

	/**
	 *  Stops the effect.
	 *  SIDE EFFECT:  sets time_to_live to 0
	 */
	public void stop()
	{
		time_to_live = 0;
	}

	/**
	 *  Creates the rows needed to store the effect.
	 */
	protected void createRows()
	{
		super.createRows();

		Statement stmt = null;

		try
		{
			stmt = Persistant.getStatement();

			stmt.executeUpdate("INSERT INTO Effect (effect_id, effect_ttl, effect_type) VALUES (" + id + ", 0, 0)");

			stmt.close();
			stmt = null;
		}
		catch (SQLException e)
		{
			System.err.println("Trying to add an effect row (" + id + "): " +
			e.getMessage());
		}

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

			stmt = null;
		}
	}

	/**
	 *  Save the effect object.
	 */
	public void store()
	{
		Statement stmt = null;

		try
		{
			stmt = Persistant.getStatement();

			// update the victims first
			stmt.executeUpdate("DELETE FROM Effect_Victims WHERE effect_id=" + id);

			// re-add the victims
			Vector vv = (Vector)victims.clone();

			for (int i = 0; i < vv.size(); i++)
			{
				stmt.executeUpdate("INSERT INTO Effect_Victims (effect_id, victim_id) VALUES (" + id + ", " + ((GameObject)vv.elementAt(i)).getID() + ")");
			}

			// update the effect table
			stmt.executeUpdate("UPDATE Effect SET effect_ttl=" + time_to_live + ", effect_type=" + effect_type + " WHERE effect_id=" + id);

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

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

			stmt = null;
		}

		super.store();
	}

	/**
	 *  Load the Effect object.
	 */
	public static Effect loadEffect(int id)
	{
		Effect ef = null;

		if (Registry.isLoaded(id))
		{
			ef = (Effect)Registry.get(id);
		}
		else
		{
			ef = new Effect();
			ef.id = id;
			ef.load();
		}

		return ef;
	}

	/**
	 *  Internals for loading effect.
	 */
	protected void load()
	{
		super.load();

		Statement stmt = null;
		ResultSet rs = null;

		try
		{
			stmt = Persistant.getStatement();

			rs = stmt.executeQuery("SELECT * FROM Effect WHERE effect_id=" + getID());

			while (rs.next())
			{
				time_to_live = rs.getInt("effect_ttl");
				effect_type = rs.getInt("effect_type");
			}

			rs.close();
			rs = null;

			// get the effect victims
			rs = stmt.executeQuery("SELECT * FROM Effect_Victims WHERE effect_id=" + getID());

			while (rs.next())
			{
				addVictim(GameObject.loadGameObject(rs.getInt("victim_id")));
			}

			rs.close();
			rs = null;

			stmt.close();
			stmt = null;

			// restart the effect
			start();
		}
		catch (SQLException e)
		{
			System.err.println("Trying to load effect (" + 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;
		}
	}

	/**
	 *  Unregister the effect.
	 */
	public void unregister()
	{
		// stop the effect
		time_to_live = 0;
	
		// remove rows from database
		Statement stmt = null;

		try
		{
			stmt = Persistant.getStatement();

			stmt.executeUpdate("DELETE FROM Effect_Victims WHERE effect_id="+id);
			stmt.executeUpdate("DELETE FROM Effect WHERE effect_id="+id);
		}
		catch (SQLException e)
		{
			System.err.println("Trying to delete Effect (" + id + "): " + e.getMessage());
		}

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

			stmt = null;
		}

		super.unregister();
	}
	
	/**
	 *  Event loop.
	 */
	public void run()
	{
		Vector vv = null;
		int i;

		long start_time = System.currentTimeMillis();
		long end_time = start_time + time_to_live * 1000;

		do
		{
			vv = (Vector)victims.clone();

			for (i = 0; i < vv.size(); i++)
			{
				((GameObject)vv.elementAt(i)).setFlag(effect_type);
			}

			// notify listeners
			effectTrigger();

			// reset time to live
			time_to_live = (int)(end_time - System.currentTimeMillis()) / 1000;

			// sleep for 5 seconds
			try
			{
				tEffect.sleep(5000);
			}
			catch (InterruptedException e)
			{
				// hmmm..
			}
		} while (end_time < System.currentTimeMillis());

		// effects get unset when they're done
		vv = (Vector)victims.clone();
	
		for (i = 0; i < vv.size(); i++)
		{
			((GameObject)vv.elementAt(i)).unsetFlag(effect_type);
		}

		time_to_live = 0;

		// notify listeners
		effectEnd();

		// unregister event
		Registry.unregister(this);
	}

	// EVENTS
	/**
	 *  Add an effect triggered listener.
	 */
	public void addEffectTriggerListener(EffectTriggerListener el)
	{
		effectTriggerListeners.addElement(el);
	}

	/**
	 *  Remove an effect triggered listener.
	 */
	public void removeEffectTriggerListener(EffectTriggerListener el)
	{
		effectTriggerListeners.removeElement(el);
	}

	/**
	 *  Fire an effect triggered event.
	 */
	protected void effectTrigger()
	{
		EffectTriggerEvent et = new EffectTriggerEvent(this);

		Vector vel = (Vector)effectTriggerListeners.clone();

		for (int i = 0; i < vel.size(); i++)
		{
			((EffectTriggerListener)vel.elementAt(i)).notifyEffectTrigger(et);
		}
	}

	/**
     *  Add a victim changed listener.
	 */
	public void addVictimChangeListener(VictimChangeListener vl)
	{
		effectVictimChangeListeners.addElement(vl);
	}
	
	/**
	 *  Remove a victim changed listener.
	 */
	public void removeVictimChangeListener(VictimChangeListener vl)
	{
		effectVictimChangeListeners.removeElement(vl);
	}
	
	/**
     *  Fire a victim changed event.
	 */
	protected void victimChange()
	{
		VictimChangeEvent ve = new VictimChangeEvent(this);

		Vector vl = (Vector)effectVictimChangeListeners.clone();

		for (int i = 0; i < vl.size(); i++)
		{
			((VictimChangeListener)vl.elementAt(i)).notifyVictimChange(ve);
		}
	}

	/**
     *  Add an event ended listener.
	 */
	public void addEffectEndListener(EffectEndListener el)
	{
		effectEndListeners.addElement(el);
	}

	/**
     *  Remove an event ended listener.
     */
	public void removeEffectEndListener(EffectEndListener el)
	{
		effectEndListeners.removeElement(el);
	}

	/**
     *  Fire an ended event.
     */
	protected void effectEnd()
	{
		EffectEndEvent ee = new EffectEndEvent(this);

		Vector el = (Vector)effectEndListeners.clone();

		for (int i = 0; i < el.size(); i++)
		{
			((EffectEndListener)el.elementAt(i)).notifyEffectEnd(ee);
		}
	}
}
			

