/**
 * Copyright © 2010, Res Interactive, LLC. All Rights Reserved.
 */
package com.tootsville.promo;

import java.math.BigDecimal;
import java.math.BigInteger;
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.user.GeneralUser;
import org.starhope.appius.util.AppiusConfig;
import org.starhope.util.LibMisc;

/**
 * Peanut Codes are hang-tags associated with various products, printed
 * with unique serial numbers.
 * 
 * @author <a href="mailto:twheys@gmail.com@resinteractive.com">Tim
 *         Heys</a>
 * 
 */
public class PeanutCode extends SQLPeerDatum {

	/**
	 * Java serialisation unique ID
	 */
	private static final long serialVersionUID = -9157201241413515864L;

	/**
	 * <pre>
	 * twheys@gmail.com 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 {
			LibMisc.closeAll (rs, st, con);
		}
		throw new NotFoundException (serialNumber);
	}

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

	/**
	 *
	 */
	private Timestamp issued;

	/**
	 *
	 */
	private BigDecimal maxPeanuts;

	/**
	 *
	 */
	private BigDecimal minPeanuts;

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

	/**
	 *
	 */
	private int redeemedByID;

	/**
	 *
	 */
	private Timestamp redeemedOn;

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

	/**
	 * <pre>
	 * twheys@gmail.com 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>
	 * twheys@gmail.com Jan 7, 2010
	 * </pre>
	 *
	 * A PeanutCode WRITEME...
	 *
	 * @param pnutCodeSerial WRITEME
	 */
	public PeanutCode (final String pnutCodeSerial) {
		this (pnutCodeSerial, 1000, 5000);
	}

	/**
	 * <pre>
	 * twheys@gmail.com 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 {
			LibMisc.closeAll (st, con);
		}
	}

	/**
	 * @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>
	 * twheys@gmail.com 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>
	 * twheys@gmail.com Jan 7, 2010
	 * </pre>
	 *
	 * TO isRedeemed WRITEME...
	 *
	 * @return WRITEME
	 */
	public boolean isRedeemed () {
		return null != redeemedOn && -1 != redeemedByID;
	}

	/**
	 * <pre>
	 * twheys@gmail.com 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 ();
	}
}
