package sso;

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

/**
 *  Represents an instance of a skill on a PC.
 */
public class Skill extends Persistant
{
	protected int	cur_rating;
	protected int	base_rating;
	protected int	pips;			// six pips to increase the skill--improve by doing
	protected int	improveThreshold;	// 1-100, when skill roll below threshold, increase number of pips--improve by doing
	protected int	learnChance;	// 1-100%, each time skill tested that skill will improve; also, chance to pick up skill by observation or study
	protected Skill	parentSkill;	// skill default, null is no parent (instance variable...refers to actual skill object)
	protected int	defaultPenalty;	// default skill penalty for not knowing skill (penalty applied when calling default)
	protected Vector	skillChangeListeners;
	protected String	parentSkillName;	// skill default, empty is no parent (class variable...refers to skill class name)

	public final static int	DEFAULT_RATING	= 0;	// skills start at this
	public static String SKILL 	= "sso.Skill";		// static skill name, changes in each child class

	/**
	 *  Constructor. Don't call directly.
	 */
	protected Skill()
	{
		super();

		skillChangeListeners = new Vector();
		parentSkillName = "";
	}

	/**
	 *  Create a new skill object.
	 */
	public static Skill createSkill()
	{
		Skill s = new Skill();
		s.init();
		return s;
	}

	/**
	 *  Initialize the new skill object.
	 */
	protected void init()
	{
		super.init();

		cur_rating = DEFAULT_RATING;
		base_rating = DEFAULT_RATING;
		pips = 0;
		improveThreshold = 5;
		learnChance = 15;
		defaultPenalty = -20;
		parentSkill = null;
	}

	/**
	 *  Get the current rating.
	 */
	public int getRating()
	{
		return cur_rating;
	}

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

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

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

	/**
	 *  Improve the skill.
	 */
	public void improve(int points)
	{
		cur_rating += points;
		base_rating += points;
		skillChange();
	}

	/**
	 *  Get the pips.
	 */
	public int getPips()
	{
		return pips;
	}

	/**
	 *  Get the improvement threshold.
	 */
	public int getImproveThreshold()
	{
		return improveThreshold;
	}

	/**
	 *  Set the improvement threshold.
	 */
	public void setImproveThreshold(int thresh)
	{
		improveThreshold = thresh;
	}

	/**
	 *  Get the learning chance.
	 */
	public int getLearnChance()
	{
		return learnChance;
	}

	/**
	 *  Set the learning chance.
	 */
	public void setLearnChance(int chance)
	{
		learnChance = chance;
	}

	/**
	 *  Get the parent skill.
	 */
	public Skill getParentSkill()
	{
		return parentSkill;
	}

	/**
	 *  Set the parent skill.
	 *  This shoul never be called. Since most skills are direct instances of
	 *  a Skill class child, the parent shouldn't need to be changed in each 
	 *  instance.
	 *
	 *  This is only useful for testing. Real skill classes will have the
	 *  parent set in init.
	 */
	private void setParentSkill(Skill skill)
	{
		parentSkill = skill;
	}

	/**
	 *  Get the default penalty. This is the roll penalty modifier when the
	 *  skill roll needs to default to the parent skill.
	 */
	public int getDefaultPenalty()
	{
		return defaultPenalty;
	}

	/**
	 *  Set the default penalty.
	 */
	public void setDefaultPenalty(int penalty)
	{
		defaultPenalty = penalty;
	}

	/**
	 *  Perform a skill roll.
	 */
	public int roll()
	{
		int roll = Random.roll(100);

		if (roll <= improveThreshold)
		{
			pips++;

			if (pips >= 6)
			{
				improve(1);
				pips = 0;
			}
		}

		return roll + cur_rating;
	}

	/**
	 *  Get parent skill name.
	 */
	public String getParentSkillName()
	{
		return parentSkillName;
	}

	/**
	 *  Create the rows needed to store this object in the database.
	 */
	protected void createRows()
	{
		super.createRows();

		Statement stmt = null;

		try
		{
			stmt = Persistant.getStatement();
			
			stmt.executeUpdate("INSERT INTO Skill (skill_id, cur_rating, base_rating, improve_threshold, learn_chance, parent_skill_id, default_penalty, pips) VALUES (" + id + ", " + cur_rating + ", " + base_rating + ", " + improveThreshold + ", " + learnChance + ", " + (parentSkill == null ? Registry.ID_INVALID : parentSkill.getID()) + ", " + defaultPenalty + ", " + pips + ")");

			stmt.close();
			stmt = null;

		}
		catch (SQLException e)
		{
			System.err.println("Trying to create rows for Skill (" + id + "): "
			+ e.getMessage());
		}

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

	/**
	 *  Store the state of the skill object.
	 */
	public void store()
	{
		super.store();

		Statement stmt = null;

		try
		{
			stmt = Persistant.getStatement();

			stmt.executeUpdate("UPDATE Skill SET cur_rating=" + cur_rating + ", base_rating=" + base_rating + ", improve_threshold=" + improveThreshold + ", learn_chance=" + learnChance + ", parent_skill_id=" + (parentSkill == null ? Registry.ID_INVALID : parentSkill.getID()) + ", default_penalty=" + defaultPenalty + ", pips=" + pips + " WHERE skill_id=" + id);

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

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

			stmt = null;
		}
	}

	/**
	 *  Load a Skill object.
	 */
	public static Skill loadSkill(int id)
	{
		Skill s = null;

		if (Registry.isLoaded(id))
		{
			s = (Skill)Registry.get(id);
		}
		else
		{
			s = new Skill();
			s.id = id;
			s.load();
		}

		return s;
	}

	/**
	 *  Load a Skill object from the database.
	 */
	protected void load()
	{
		super.load();

		Statement stmt = null;
		ResultSet rs = null;

		try
		{
			stmt = Persistant.getStatement();

			rs = stmt.executeQuery("SELECT * FROM Skill WHERE skill_id=" + id);
			
			while (rs.next())
			{
				cur_rating = rs.getInt("cur_rating");
				base_rating = rs.getInt("base_rating");
				improveThreshold = rs.getInt("improve_threshold");
				learnChance = rs.getInt("learn_chance");
				parentSkill = Skill.loadSkill(rs.getInt("parent_skill_id"));
				defaultPenalty = rs.getInt("default_penalty");
				pips = rs.getInt("pips");
			}

			rs.close();
			rs = null;

			stmt.close();
			stmt = null;
		}
		catch (SQLException e)
		{
			System.err.println("Trying to load Skill (" + 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;
		}
	}

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

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

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

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

			stmt = null;
		}

		super.unregister();
	}
	

	// EVENTS
	public void addSkillChangeListener(SkillChangeListener sl)
	{
		skillChangeListeners.addElement(sl);
	}

	public void removeSkillChangeListener(SkillChangeListener sl)
	{
		skillChangeListeners.removeElement(sl);
	}

	protected void skillChange()
	{
		SkillChangeEvent se = new SkillChangeEvent(this);

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

		for (int i = 0; i < vl.size(); i++)
		{
			((SkillChangeListener)vl.elementAt(i)).notifySkillChange(se);
		}
	}

	public static void main(String [] args)
	{
		Skill s1 = Skill.createSkill();
		Skill s2 = Skill.createSkill();

		s2.setParentSkill(s1);

		s1.store();
		s2.store();

		int is1 = s1.getID();
		int is2 = s2.getID();

		s1 = null;
		s2 = null;

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

		s1 = Skill.loadSkill(is1);
		s2 = Skill.loadSkill(is2);

		s2.unregister();
		s1.unregister();
	}
}

