package sso;

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

/**
 *  Thing represents an object in the game that can be carried, used, or moves.
 */
public class Thing extends GameObject
{
	// It's important to understand the concept of keys. A key allows an object
	// to be taken or dropped if the object is locked to that key. Each object
	// can have one and only one key. One key may be used for multiple objects.
	// Any GameObject can act as a key--the primary requirement is simply an
	// id, though it helps if it's a Thing because Things can be taken.

	protected int	weight;			// in coins
	protected int	base_value;		// for exchanging, selling
	protected int	capacity;		// weight in coins max. carriable
	protected int	base_rating;	// bonus, strength, quality
	protected int	cur_rating;
	protected GameObject	key;	// key object (required to take)

	// inherits property changed listeners

	/**
	 *  Constructor. Do not call directly.
	 */
	protected Thing()
	{
		super();
	}
	/**
	 *  Factory method for creating a new Thing.
	 */
	public static Thing createThing()
	{
		Thing t = new Thing();
		t.init();
		return t;
	}

	/**
	 *  New Thing initializer method.
	 */
	protected void init()
	{
		super.init();
		weight = 1;
		base_value = 0;
		capacity = 0;
		base_rating = 10;
		cur_rating = base_rating;
		key = null;
	}

	/**
	 *  Get weight.
	 */
	public int getWeight()
	{
		return getBaseWeight() + getCarriedWeight();
	}

	/**
	 *  Get base weight.
	 */
	public final int getBaseWeight()
	{
		return weight;
	}

	/**
	 *  Set weight.
	 */
	public void setWeight(int weight)
	{
		this.weight = weight;
		propChanged("weight");
	}

	/**
	 *  Get value.
	 *  This may be overriden by child objects to incorporate value increases
	 *  or decreases based on rating.
	 */
	public int getValue()
	{
		return base_value;
	}

	/**
	 *  Gets the base value.
	 */
	public final int getBaseValue()
	{
		return base_value;
	}

	/**
	 *  Set value.
	 *  Sets the base value.
	 */
	public void setBaseValue(int base_value)
	{
		this.base_value = base_value;
		propChanged("base_value");
	}

	/**
	 *  Get total capacity.
	 *  This should be overriden by children (like PC).
	 */
	public int getCapacity()
	{
		return capacity;
	}

	/**
	 *  Get base capacity.
	 */
	public final int getBaseCapacity()
	{
		return capacity;
	}

	/**
	 *  Set base capacity.
	 */
	public void setCapacity(int capacity)
	{
		this.capacity = capacity;
		propChanged("capacity");
	}

	/**
	 *  Get rating.
	 *  This can be overriden in child objects.
	 */
	public int getRating()
	{
		return cur_rating;
	}

	/**
	 *  Get base rating.
	 */
	public final int getBaseRating()
	{
		return base_rating;
	}

	/**
	 *  Set base rating.
	 */
	public void setBaseRating(int rating)
	{
		base_rating = rating;
		propChanged("base_rating");
	}

	/**
	 *  Set current rating.
	 */
	public void setRating(int rating)
	{
		cur_rating = rating;
		propChanged("cur_rating");
	}

	/**
	 *  Get carried weight.
	 */
	public int getCarriedWeight()
	{
		int weight = 0;

		Vector vc = (Vector)contents.clone();

		for (int i = 0; i < vc.size(); i++)
		{
			if (vc.elementAt(i) instanceof Thing)
			{
				weight += ((Thing)vc.elementAt(i)).getWeight();
			}
		}

		return weight;
	}

	/**
	 *  Get key.
	 */
	public GameObject getKey()
	{
		return key;
	}

	/**
	 *  Set key.
	 */
	public void setKey(GameObject key)
	{
		this.key = key;
		propChanged("key");
	}

	/**
	 *  Can this object be dropped by holder?
	 */
	public boolean isDroppable()
	{
// dropped the PC specific code...otherwise, I can't move things
// programatically
//		if (location instanceof PC)
//		{
		if (location != null)
		{
			if (key == null || location.has(key))
			{
				if (location.getLocation() != null)
				{
					return location.getLocation().accept(this);
				}
			}
		}

		return false;
	}

	/**
	 *  Can this object be taken by the spec?
	 */
	public boolean isTakeableBy(PC pc)
	{
		if (key == null || pc.has(key))
		{
			return true;
		}

		return false;
	}

	/**
	 *  OVERRIDE METHOD.
	 *  Check to see if the Thing is takable or droppable.
	 */
	public boolean move(GameObject destination)
	{
		boolean moved = false;
	
		// if being picked up by a pc
		// pcs may be able to pick something up even if it would otherwise by
		// locked...someday, you may want to reconsider this behavior
		if (destination instanceof PC)
		{

System.out.println("Destination is PC");

			if (isTakeableBy((PC)destination))
			{

System.out.println("Thing:move() is takeable by dest");

				moved = super.move(destination);
			}
		}
		else
		{

System.out.println("Destination is not a PC");

			// any other move
			if (isDroppable() || location == null)
			{

System.out.println("Thing:move() is droppable");

				moved = super.move(destination);
			}
		}

System.out.println("Thing:move() moveable == " + moved);

		return moved;
	}

	/**
 	 *  OVERRIDE METHOD.
	 *  Only accept objects when sufficient capacity exists.
	 */
	public boolean accept(GameObject spec)
	{
		boolean acceptable = true;
	
		if (spec instanceof Thing)
		{
			Thing t = (Thing)spec;

			if (t.getWeight() + getCarriedWeight() > capacity)
			{

System.out.println("Weight > capacity...Thing:accept == false");

				acceptable = false;
			}
		}

		return super.accept(spec) && acceptable;
	}
	
	
	/**
	 *  Create the row needed to store this object.
	 */
	protected void createRows()
	{
		super.createRows();

		Statement stmt = null;
		
		try
		{
			stmt = Persistant.getStatement();

			stmt.executeUpdate("INSERT INTO Thing (thing_id, weight, base_value, capacity, base_rating, cur_rating, key_id) VALUES (" + id + ", " + weight + ", " + base_value + ", " + capacity + ", " + base_rating + ", " + cur_rating + ", " + Registry.ID_INVALID + ")");

			stmt.close();
			stmt = null;
		}
		catch (SQLException e)
		{
			System.err.println("Trying to create rows for Thing (" + id + "): "
			+ e.getMessage());
		}
		
		if (stmt != null)
		{
			try
			{
				stmt.close();
			}
			catch (SQLException e)
			{
				//
			}

			stmt = null;
		}
	}

	/**
	 *  Store the Thing.
	 */
	public void store()
	{
		super.store();

		Statement stmt = null;

		try
		{
			stmt = Persistant.getStatement();

			stmt.executeUpdate("UPDATE Thing SET weight=" + weight +", base_value=" + base_value + ", capacity=" + capacity + ", base_rating=" + base_rating + ", cur_rating=" + cur_rating + ", key_id=" + (key == null ? Registry.ID_INVALID : key.getID()) + " WHERE thing_id=" + id);

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

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

			stmt = null;
		}
	}

	/**
	 *  Load a thing by ID. Factory method.
	 */
	public static Thing loadThing(int id)
	{
		Thing t = null;
		if (Registry.isLoaded(id))
		{
			return (Thing)Registry.get(id);
		}
		else
		{
			t = new Thing();
			t.id = id;
			t.load();
		}

		return t;
	}

	/**
	 *  Load the data into this instance.
	 */
	protected void load()
	{
		super.load();

		Statement stmt = null;
		ResultSet rs = null;

		try
		{
			stmt = Persistant.getStatement();
		
			rs = stmt.executeQuery("SELECT * FROM Thing WHERE thing_id=" + id);

			while (rs.next())
			{
				weight = rs.getInt("weight");
				base_value = rs.getInt("base_value");
				capacity = rs.getInt("capacity");
				base_rating = rs.getInt("base_rating");
				cur_rating = rs.getInt("cur_rating");
				key = GameObject.loadGameObject(rs.getInt("key_id"));
			}

			rs.close();
			rs = null;

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

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

			rs = null;
		}

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

			stmt = null;
		}
	}

	/**
	 *  Remove the object from the database and registry.
	 */
	public void unregister()
	{
		Statement stmt = null;

		try
		{
			stmt = Persistant.getStatement();

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

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

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

			stmt = null;
		}

		super.unregister();
	}

	/**
	 *  Tester method.
	 */
	public static void main(String [] args)
	{
		Thing t = Thing.createThing();
		System.out.println(t.getID());
		t.store();
		try
		{
			Thread.sleep(15000);
		}
		catch (InterruptedException e)
		{
			//
		}
		t.unregister();
		t = null;
	}
}

