/**
 * THE PeanutCode.java WRITEME...
 */
package com.tootsville.promo;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.Locale;

import org.starhope.appius.except.AlreadyUsedException;
import org.starhope.appius.except.NotFoundException;
import org.starhope.appius.game.AppiusClaudiusCaecus;
import org.starhope.appius.sql.SQLPeerDatum;
import org.starhope.appius.util.AppiusConfig;

import com.tootsville.user.Toot;

/**
 * WRITEME: Document this type. theys Jan 7, 2010
 * 
 * @author <a href="mailto:theys@resinteractive.com">Tim Heys</a>
 * 
 */
public class PeanutCode extends SQLPeerDatum {

	/**
	 * WRITEME: Document this field. theys Jan 7, 2010
	 */
	private static final long serialVersionUID = -9157201241413515864L;

	/**
	 * <pre>
	 * theys Jan 7, 2010
	 * </pre>
	 * 
	 * TO getByPeanutCode WRITEME...
	 * 
	 * @param serialNumber WRITEME
	 * @return WRITEME
	 * @throws NotFoundException WRITEME
	 */
	public static PeanutCode getByPeanutCode (final String serialNumber)
			throws NotFoundException {
		Connection con = null;
		PreparedStatement st = null;
		ResultSet rs = null;
		try {
			con = AppiusConfig.getDatabaseConnection ();
			st = con
					.prepareStatement ("SELECT * FROM serialNumbers WHERE serial=?");
			st.setString (1, serialNumber);
			if (!st.execute ()) {
				AppiusClaudiusCaecus
						.reportBug ("Tried to fetch peanut code by serial number "
								+ serialNumber + " and failed");
				return null;
			}
			rs = st.getResultSet ();
			if (!rs.next ())
				return null;
			return new PeanutCode (rs);
		} catch (final SQLException e) {
			AppiusClaudiusCaecus.fatalBug (e);
		} finally {
			if (null != st) {
				try {
					st.close ();
				} catch (final SQLException e) { /* No Op */
				}
			}
			if (null != con) {
				try {
					con.close ();
				} catch (final SQLException e) { /* No Op */
				}
			}
		}
		throw new NotFoundException (serialNumber);
	}

	/**
	 * <pre>
	 * theys Jan 7, 2010
	 * </pre>
	 * 
	 * TO redeemPeanutCode WRITEME...
	 * 
	 * @param redeemer WRITEME
	 * @param peanutCode WRITEME
	 * @return WRITEME
	 * @throws NotFoundException WRITEME
	 * @throws AlreadyUsedException WRITEME
	 */
	public static BigDecimal redeemPeanutCode (final Toot redeemer,
			final String peanutCode) throws NotFoundException,
			AlreadyUsedException {
		final PeanutCode pnutCode = PeanutCode
				.getByPeanutCode (peanutCode);
		if (pnutCode.isRedeemed ())
			throw new AlreadyUsedException (peanutCode, pnutCode
					.getRedeemedOn ());
		pnutCode.redeem (redeemer.getUserID ());
		return pnutCode.getRedeemValue ();
	}

	/**
	 * 
	 */
	private Timestamp issued;

	/**
	 * 
	 */
	private BigDecimal maxPeanuts;

	/**
	 * 
	 */
	private BigDecimal minPeanuts;

	/**
	 * 
	 */
	private String peanutCode = "";

	/**
	 * 
	 */
	private int redeemedByID;

	/**
	 * 
	 */
	private Timestamp redeemedOn;

	/**
	 * 
	 */
	private int serialID = -1;

	/**
	 * <pre>
	 * theys Jan 7, 2010
	 * </pre>
	 * 
	 * A PeanutCode WRITEME...
	 * 
	 * @param rs WRITEME
	 */
	public PeanutCode (final ResultSet rs) {
		try {
			set (rs);
		} catch (final SQLException e) {
			throw AppiusClaudiusCaecus.fatalBug (e);
		}
	}

	/**
	 * <pre>
	 * theys Jan 7, 2010
	 * </pre>
	 * 
	 * A PeanutCode WRITEME...
	 * 
	 * @param pnutCodeSerial WRITEME
	 */
	public PeanutCode (final String pnutCodeSerial) {
		this (pnutCodeSerial, 1000, 5000);
	}

	/**
	 * <pre>
	 * theys Jan 7, 2010
	 * </pre>
	 * 
	 * A PeanutCode WRITEME...
	 * 
	 * @param pnutCodeSerial WRITEME
	 * @param minValue WRITEME
	 * @param maxValue WRITEME
	 */
	public PeanutCode (final String pnutCodeSerial, final int minValue,
			final int maxValue) {
		peanutCode = pnutCodeSerial.toLowerCase (Locale.ENGLISH);
		minPeanuts = new BigDecimal (minValue);
		maxPeanuts = new BigDecimal (maxValue);
		insert ();
	}

	/**
	 * @see org.starhope.appius.sql.SQLPeerDatum#flush()
	 */
	@Override
	public void flush () {
		Connection con = null;
		PreparedStatement st = null;
		try {
			con = AppiusConfig.getDatabaseConnection ();
			st = con
					.prepareStatement ("UPDATE serialNumbers SET redeemed=?, redeemedBy=?, "
							+ "minPeanuts=?, maxPeanuts=? WHERE serial=?");
			if (null == redeemedOn) {
				st.setNull (1, Types.TIMESTAMP);
			} else {
				st.setTimestamp (1, redeemedOn);
			}
			if (-1 == redeemedByID) {
				st.setNull (2, Types.INTEGER);
			} else {
				st.setInt (2, redeemedByID);
			}
			st.setBigDecimal (3, minPeanuts);
			st.setBigDecimal (4, maxPeanuts);
			st.setString (5, peanutCode);
			st.execute ();
		} catch (final SQLException e) {
			AppiusClaudiusCaecus.fatalBug (e);
		} finally {
			if (null != st) {
				try {
					st.close ();
				} catch (final SQLException e) { /* No Op */
				}
			}
			if (null != con) {
				try {
					con.close ();
				} catch (final SQLException e) { /* No Op */
				}
			}

		}
	}

	/**
	 * @see org.starhope.appius.sql.SQLPeerDatum#getCacheUniqueID()
	 */
	@Override
	protected String getCacheUniqueID () {
		return peanutCode;
	}

	/**
	 * @return the id
	 */
	public int getId () {
		return serialID;
	}

	/**
	 * @return the issued
	 */
	public Timestamp getIssued () {
		return issued;
	}

	/**
	 * @return the maxPeanuts
	 */
	public BigDecimal getMaxPeanuts () {
		return maxPeanuts;
	}

	/**
	 * @return the minPeanuts
	 */
	public BigDecimal getMinPeanuts () {
		return minPeanuts;
	}

	/**
	 * @return the peanutCode
	 */
	public String getPeanutCode () {
		return peanutCode;
	}

	/**
	 * @return the redeemedByID
	 */
	public int getRedeemedByID () {
		return redeemedByID;
	}

	/**
	 * @return the redeemedOn
	 */
	public Timestamp getRedeemedOn () {
		return redeemedOn;
	}

	/**
	 * <pre>
	 * theys Jan 7, 2010
	 * </pre>
	 * 
	 * TO getRedeemValue WRITEME...
	 * 
	 * @return WRITEME
	 */
	private BigDecimal getRedeemValue () {
		final int maxPeanutsInt = maxPeanuts.intValue ();
		final int minPeanutsInt = minPeanuts.intValue ();
		final BigDecimal value = BigDecimal.valueOf (Math.random ()
				* (maxPeanutsInt - minPeanutsInt) + minPeanutsInt);
		return value;

	}

	/**
	 * WRITEME
	 */
	private void insert () {
		Connection con = null;
		PreparedStatement insert = null;
		ResultSet keys = null;
		try {
			con = AppiusConfig.getDatabaseConnection ();
			insert = con
					.prepareStatement (
							"INSERT INTO serialNumbers (serial, minPeanuts, maxPeanuts, issued, avatarID, tootName) VALUES (?,?,?,NOW(),16,'toot')",
							Statement.RETURN_GENERATED_KEYS);
			insert.setString (1, peanutCode);
			insert.setBigDecimal (2, minPeanuts);
			insert.setBigDecimal (3, maxPeanuts);
			if (insert.executeUpdate () != 1)
				throw new SQLException ("adding new peanut code"
						+ " failed with " + insert.getUpdateCount ()
						+ " updates");
			keys = insert.getGeneratedKeys ();
			if (keys.next ()) {
				serialID = keys.getInt (1);
			} else
				throw new SQLException ("Can't get serial ID");
		} catch (final SQLException e) {
			AppiusClaudiusCaecus.fatalBug (e);
		} finally {
			if (null != insert) {
				try {
					insert.close ();
				} catch (final SQLException e) { /* No Op */
				}
			}
			if (null != con) {
				try {
					con.close ();
				} catch (final SQLException e) { /* No Op */
				}
			}
		}
	}

	/**
	 * <pre>
	 * theys Jan 7, 2010
	 * </pre>
	 * 
	 * TO isRedeemed WRITEME...
	 * 
	 * @return WRITEME
	 */
	public boolean isRedeemed () {
		return null != redeemedOn && -1 != redeemedByID;
	}

	/**
	 * <pre>
	 * theys Jan 7, 2010
	 * </pre>
	 * 
	 * TO redeem WRITEME...
	 * 
	 * @param redeemerID WRITEME
	 */
	private void redeem (final int redeemerID) {
		redeemedByID = redeemerID;
		redeemedOn = new Timestamp (System.currentTimeMillis ());
		changed ();
	}

	/**
	 * @see org.starhope.appius.sql.SQLPeerDatum#set(java.sql.ResultSet)
	 */
	@Override
	protected void set (final ResultSet rs) throws SQLException {
		serialID = rs.getInt ("id");
		peanutCode = rs.getString ("serial");
		issued = rs.getTimestamp ("issued");
		minPeanuts = rs.getBigDecimal ("minPeanuts");
		maxPeanuts = rs.getBigDecimal ("maxPeanuts");
		redeemedByID = rs.getInt ("redeemedBy");
		if (rs.wasNull ()) {
			redeemedByID = -1;
		}
		redeemedOn = rs.getTimestamp ("redeemed");
		if (rs.wasNull ()) {
			redeemedOn = null;
		}
		System.out.println ("Set peanut by serial " + peanutCode);
	}

	/**
	 * @param newMaxPeanuts the maxPeanuts to set
	 */
	public void setMaxPeanuts (final BigDecimal newMaxPeanuts) {
		maxPeanuts = newMaxPeanuts;
		changed ();
	}

	/**
	 * @param newMinPeanuts the minPeanuts to set
	 */
	public void setMinPeanuts (final BigDecimal newMinPeanuts) {
		minPeanuts = newMinPeanuts;
		changed ();
	}
}
