package sso;

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

public class Arms extends Equipment implements Weapon, Throwable
{
	protected int criticalHit;		// on attack roll, n > criticalHit
	protected int fumbleRoll;		// on attack roll, n < fumbleRoll
	protected int attackType;		// as per Resistance
	protected int dmgDice;			// number of dice to roll to get damage
	protected int dmgFaces;			// sides on each die to roll for damage
	protected int minRange;
	protected int shortRange;
	protected int medRange;
	protected int longRange;
	protected int extremeRange;
	protected int shortRangePenalty;
	protected int medRangePenalty;
	protected int longRangePenalty;
	protected int extremeRangePenalty;
	protected String throwSkill;	// skill required to throw this
	protected String weaponSkill;	// skill required to attack with this
	protected String oppSkill;		// skill required to counter attacks
	protected int dmgBonus;			// on successful hit, add to dmg roll
	protected Vector fumbleListeners;

	
	/**
	 *  Constructor. Not intended to be called publicly.
	 */
	protected Arms()
	{
		super();
	
		fumbleListeners = new Vector();
	}

	/**
	 *  Factory method. Used to create a new instance of Arms.
	 */
	public static Arms createArms()
	{
		Arms a = new Arms();
		a.init();
		return a;
	}

	/**
	 *  Protected initializer. Used for creating news instances of Arms.
	 */
	protected void init()
	{
		super.init();

		criticalHit = 98;
		fumbleRoll = 3;
		attackType |= Resistance.PHYSICAL;
		dmgDice = 1;
		dmgFaces = 8;
		minRange = 1;
		shortRange = 2;
		shortRangePenalty = 10;
		medRange = 3;
		medRangePenalty = 0;
		longRange = 4;
		longRangePenalty = -10;
		extremeRange = 5;
		extremeRangePenalty = -20;
		throwSkill = "sso.skill.Throwing";
		weaponSkill = "sso.skill.Melee";
		oppSkill = "sso.skill.Defense";
		dmgBonus = 0;
	}
		
		

	/**
	 *  Set the critical hit threshold
	 */
	public void setCriticalHit(int threshold)
	{
		criticalHit = threshold;

		propChanged("criticalHit");
	}

	/**
	 *  Set the fumble roll threshold.
	 */
	public void setFumbleRoll(int threshold)
	{
		fumbleRoll = threshold;

		propChanged("fumbleRoll");
	}

	/**
	 *  Set the attack type
	 */
	public void setAttackType(int type)
	{
		attackType = type;

		propChanged("attackType");
	}

	/**
	 *  Set the damage dice (number to roll when computing damage).
	 */
	public void setDamageDice(int dice)
	{
		dmgDice = dice;

		propChanged("dmgDice");
	}

	/**
	 *  Set the damage die faces (number of faces on each damage die).
	 */
	public void setDamageFaces(int faces)
	{
		dmgFaces = faces;

		propChanged("dmgFaces");
	}

	/**
	 *  Set the minimum throw range for this weapon.
	 */
	public void setMinRange(int min)
	{
		minRange = min;

		propChanged("minRange");
	}

	/**
	 *  Set short range for throwing this object.
	 */
	public void setShortRange(int shortR)
	{
		shortRange = shortR;

		propChanged("shortRange");
	}

	/**
	 *  Set medium range for throwing this weapon.
	 */
	public void setMediumRange(int med)
	{
		medRange = med;

		propChanged("medRange");
	}

	/**
	 *  Set long range for throwing this weapon.
	 */
	public void setLongRange(int longR)
	{
		longRange = longR;

		propChanged("longRange");
	}

	/**
	 *  Set extreme range for throwing this weapon.
	 */
	public void setExtremeRange(int extreme)
	{
		extremeRange = extreme;

		propChanged("extremeRange");
	}

	/**
	 *  Set the short range penalty.
	 */
	public void setShortRangePenalty(int pen)
	{
		shortRangePenalty = pen;

		propChanged("shortRangePenalty");
	}

	/**
	 *  Set the medium range penalty.
	 */
	public void setMedRangePenalty(int pen)
	{
		medRangePenalty = pen;

		propChanged("medRangePenalty");
	}

	/**
	 *  Set the long range penalty.
	 */
	public void setLongRangePenalty(int pen)
	{
		longRangePenalty = pen;

		propChanged("longRangePenalty");
	}

	/**
	 *  Set the extreme range penalty.
	 */
	public void setExtremeRangePenalty(int pen)
	{
		extremeRangePenalty = pen;

		propChanged("extremeRangePenalty");
	}

	/**
	 *  Set the skill needed to throw this object.
	 */
	public void setThrowSkill(String skill)
	{
		throwSkill = skill;

		propChanged("throwSkill");
	}
		
	/**
	 *  Set the skill needed to attack with this weapon.
	 */
	public void setSkill(String skill)
	{
		weaponSkill = skill;

		propChanged("weaponSkill");
	}

	/**
	 *  Set the opposing skill to avoid attacks.
	 */
	public void setOpposingSkill(String skill)
	{
		oppSkill = skill;

		propChanged("oppSkill");
	}

	/**
	 *  Get the damage bonus for successful attacks.
	 */
	public int getDamageBonus()
	{
		return dmgBonus;
	}

	/**
	 *  Set the damage bonus for successful attacks.
	 */
	public void setDamageBonus(int bonus)
	{
		dmgBonus = bonus;

		propChanged("dmgBonus");
	}

	
	// Weapon
	/**
	 *  Get the skill required to attack with this weapon
	 */
	public Skill getSkill()
	{
		Skill skill = null;

		try
		{
			skill = ((Skill)(Class.forName(weaponSkill).newInstance()));
		}
		catch (Exception e)
		{
			System.err.println("Broken skill heirarchy or wrong skill name (" +
			weaponSkill + ": " + e.getMessage());
		}
		
		return skill;
	}

	/**
	 *  Get the opposing skill that is required to block attacks
	 */
	public Skill getOpposingSkill()
	{
		Skill skill = null;

		try
		{
			skill = ((Skill)(Class.forName(oppSkill).newInstance()));
		}
		catch (Exception e)
		{
			System.err.println("Broken skill heirarchy or wrong skill name (" +
			weaponSkill + ": " + e.getMessage());
		}
		
		return skill;
	}

	/**
	 *  Make a damage roll.
	 */
	public int getDamage()
	{
		return Random.dice(dmgDice, dmgFaces) + dmgBonus;
	}
	
	/**
	 *  Return the critical hit threshold.
	 */
	public int getCriticalHit()
	{
		return criticalHit;
	}

	/**
	 *  Return the fumble threshold.
	 */
	public int getFumble()
	{
		return fumbleRoll;
	}

	/**
	 *  Do a fumble on this.getLocation() if location is a PC
	 */
	public void doFumble()
	{
		// the basic fumble will raise a fumble event and roll damage against
		// the PC
		fumbleEvent();

		if (getLocation() instanceof PC)
		{
			((PC)getLocation()).hurt(getDamage());
		}
	}

	/**
	 *  Get the attack type of this weapon.
	 */
	public int getAttackType()
	{
		return attackType;
	}
	
	// throwable
	// some objects can be thrown, but give no damage (the ammo does that
	// instead)...such as a bow
	/**
	 *  Get the minimum range that this weapon can be thrown in.
	 *  And closer and it can't be thrown.
	 */
	public int getMinRange()
	{
		return minRange;
	}

	/**
	 *  Get the band (min..short) for this weapon.
	 */
	public int getShortRange()
	{
		return shortRange;
	}

	/**
	 *  Get the band (short..med) for this weapon.
	 */
	public int getMedRange()
	{
		return medRange;
	}

	/**
	 *  Get the band (med..long) for this weapon.
	 */
	public int getLongRange()
	{
		return longRange;
	}


	/**
	 *  Get the band (long..extreme) for this weapon.
	 */
	public int getExtremeRange()
	{
		return extremeRange;
	}
	
	/**
	 *  Get the short range penalty
	 */
	public int getShortRangePenalty()
	{
		return shortRangePenalty;
	}

	/**
	 *  Get the medium range penalty
	 */
	public int getMedRangePenalty()
	{
		return medRangePenalty;
	}

	/**
	 *  Get the long range penalty
	 */
	public int getLongRangePenalty()
	{
		return longRangePenalty;
	}

	/**
	 *  Get the extreme range penalty
	 */
	public int getExtremeRangePenalty()
	{
		return extremeRangePenalty;
	}

	/**
	 *  Get the throw skill for this weapon
	 */
	public Skill getThrowSkill()
	{
		try
		{
			return (Skill)Class.forName(throwSkill).newInstance();
		}
		catch (Exception e)
		{
		}

		return null;
		
	}

	/**
	 *  Do a throw type attack. Different from a normal attack which uses the
	 *  normal attack mode of an object (defined as PC.doAttack(target). This
	 *  one uses the Attack.throwAttack method.
	 */
	public Attack doThrow(Attackable target)
	{
		Attack attack = new Attack();
		attack.setAttacker((PC)this.getLocation());
		attack.setDefender(target);
		attack.setWeapon(this);
		attack.throwAttack();
		return attack;
	}
	

	// EVENTS
	/**
	 *  Add a fumble event listener.
	 */
	public void addFumbleListener(FumbleListener fl)
	{
		fumbleListeners.addElement(fl);
	}

	/**
	 *  Remove a fumble event listener.
	 */
	public void removeFumbleListener(FumbleListener fl)
	{
		fumbleListeners.removeElement(fl);
	}

	/**
	 *  Raise a fumble event.
	 */
	protected void fumbleEvent()
	{
		FumbleEvent fe = new FumbleEvent(this);

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

		for (int i = 0; i < vl.size(); i++)
		{
			((FumbleListener)vl.elementAt(i)).notifyFumble(fe);
		
		}
	}

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

		Statement stmt = null;

		try
		{
			stmt = Persistant.getStatement();

			stmt.executeUpdate("INSERT INTO Arms (arms_id, critical_hit, fumble_roll, attack_type, damage_dice, damage_faces, min_range, short_range, med_range, long_range, extreme_range, short_r_pen, med_r_pen, long_r_pen, extreme_r_pen, throw_skill, weapon_skill, opp_skill, damage_bonus) VALUES (" + id + ", " + criticalHit + ", " + fumbleRoll + ", " + attackType + ", " + dmgDice + ", " + dmgFaces + ", " + minRange + ", " + shortRange + ", " + medRange + ", " + longRange + ", " + extremeRange + ", " + shortRangePenalty + ", " + medRangePenalty + ", " + longRangePenalty + ", " + extremeRangePenalty + ", '" + throwSkill + "', '" + weaponSkill + "', '" + oppSkill + "', " + dmgBonus + ")");
			
			stmt.close();
			stmt = null;
		} 
		catch (SQLException e)
		{
			System.err.println("Trying to create Arms (" + id + "): " +
			e.getMessage());
		}

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

			stmt = null;
		}
	}

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

		Statement stmt = null;

		try
		{
			stmt = Persistant.getStatement();

			stmt.executeUpdate("UPDATE Arms SET critical_hit=" + criticalHit + ", fumble_roll=" + fumbleRoll + ", attack_type=" + attackType + ", damage_dice=" + dmgDice + ", damage_faces=" + dmgFaces + ", min_range=" + minRange + ", short_range=" + shortRange + ", med_range=" + medRange + ", long_range=" + longRange + " , extreme_range=" + extremeRange + ", short_r_pen=" + shortRangePenalty + ", med_r_pen=" + medRangePenalty + ", long_r_pen=" + longRangePenalty + ", extreme_r_pen=" + extremeRangePenalty + ", throw_skill='" + throwSkill + "', weapon_skill='" + weaponSkill + "', opp_skill='" + oppSkill + "', damage_bonus=" + dmgBonus + " WHERE arms_id=" + id);
			
			stmt.close();
			stmt = null;
		}
		catch (SQLException e)
		{
			System.err.println("Trying to store Arms (" + id + "): " +
			e.getMessage());
		}

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

	/**
	 *  Load the Arms object.
	 */
	public static Arms loadArms(int id)
	{
		Arms a = null;

		if (Registry.isLoaded(id))
		{
			a = (Arms)Registry.get(id);
		}
		else
		{
			a = new Arms();
			a.id = id;
			a.load();
		}

		return a;
	}

	/**
	 *  Load the Arms object from the db.
	 */
	protected void load()
	{
		super.load();

		Statement stmt = null;
		ResultSet rs = null;

		try
		{
			stmt = Persistant.getStatement();

			rs = stmt.executeQuery("SELECT * FROM Arms WHERE arms_id=" + id);

			while (rs.next())
			{
				criticalHit = rs.getInt("critical_hit");
				fumbleRoll = rs.getInt("fumble_roll");
				attackType = rs.getInt("attack_type");
				dmgDice = rs.getInt("damage_dice");
				dmgFaces = rs.getInt("damage_faces");
				minRange = rs.getInt("min_range");
				shortRange = rs.getInt("short_range");
				medRange = rs.getInt("med_range");
				longRange = rs.getInt("long_range");
				extremeRange = rs.getInt("extreme_range");
				shortRangePenalty = rs.getInt("short_r_pen");
				medRangePenalty = rs.getInt("med_r_pen");
				longRangePenalty = rs.getInt("long_r_pen");
				extremeRangePenalty = rs.getInt("extreme_r_pen");
				throwSkill = rs.getString("throw_skill");
				weaponSkill = rs.getString("weapon_skill");
				oppSkill = rs.getString("opp_skill");
				dmgBonus = rs.getInt("damage_bonus");
			}

			rs.close();
			rs = null;
			
			stmt.close();
			stmt = null;
		}
		catch (SQLException e)
		{
			System.err.println("Trying to load Arms (" + 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 Arms object from the database
	 */
	public void unregister()
	{
		Statement stmt = null;

		try
		{
			stmt = Persistant.getStatement();

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

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

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

			stmt = null;
		}

		super.unregister();
	}


	/**
	 *  Tester
	 */
	public static void main(String [] args)
	{
		// Create a location
		Tile t1 = Tile.createTile();
		Tile t2 = Tile.createTile();

		// Create something to hold the weapon
		PC pc = PC.createPC();
		pc.setCapacity(1000);

		// Create the weapon
		Arms a = Arms.createArms();
		a.setEquipLocation(PC.LOC_HANDS);
		a.setWeight(10);
		a.store();

		// put the PC on a tile
		System.out.println("PC moved? " + pc.move(t1));
		System.out.println("PC location " + pc.getLocation());

		// put the arms on a tile
		System.out.println("Arms moved? " + a.move(t2));
		System.out.println("Arms location " + a.getLocation());

		t1.store();
		t2.store();

		System.out.println("stored");

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

		// move the arms to the PC
		System.out.println("Arms moved? " + a.move(pc));
		System.out.println("Arms location " + a.getLocation());

		t1.store();
		t2.store();

		System.out.println("stored again");

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

		a.unregister();
		a = null;

		pc.unregister();
		t2.unregister();
		t1.unregister();

	}
}

