/**
 * Copyright © 2009-2010, Res Interactive, LLC. All Rights Reserved.
 */

package com.tootsville;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Locale;
import java.util.Random;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspWriter;

import org.json.JSONException;
import org.json.JSONObject;
import org.starhope.appius.except.DataException;
import org.starhope.appius.except.NotFoundException;
import org.starhope.appius.game.AppiusClaudiusCaecus;
import org.starhope.appius.mb.Enrolment;
import org.starhope.appius.mb.UserEnrolment;
import org.starhope.appius.sql.SQLPeerDatum;
import org.starhope.appius.user.AbstractUser;
import org.starhope.appius.user.User;
import org.starhope.appius.util.AppiusConfig;
import org.starhope.util.LibMisc;

/**
 * 
 * 
 * TODO: The documentation for this type (IBCUtil) is incomplete.
 * (theys, Sep 23, 2009)
 * 
 * @author theys
 * 
 */
public class IBCUtil extends SQLPeerDatum {

	/**
	 * TODO: document this field (theys, Nov 3, 2009)
	 * 
	 * AVAILABLE (IBCUtil)
	 */
	private static final String AVAILABLE = "avl";
	/**
	 * TODO: document this field (theys, Nov 3, 2009)
	 * 
	 * CANCELED (IBCUtil)
	 */
	private static final String CANCELED = "cncl";
	/**
	 * TODO: document this field (theys, Nov 3, 2009)
	 * 
	 * FAIL (IBCUtil)
	 */
	private static final String FAIL = "fail";
	/**
	 * TODO: document this field (theys, Nov 3, 2009)
	 * 
	 * ORDER_SOURCE_IBC (IBCUtil)
	 */
	private static final String ORDER_SOURCE_IBC = "ibcc";
	/**
	 * TODO: document this field (theys, Nov 3, 2009)
	 * 
	 * orderCodeChars (IBCUtil)
	 */
	private static final char orderCodeChars[] = { 'A', 'B', 'C', 'D',
			'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'R', 'S',
			'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4',
			'5', '6', '7', '8', '9' };
	/**
	 * TODO: document this field (theys, Nov 3, 2009)
	 * 
	 * REDEEMED (IBCUtil)
	 */
	private static final String REDEEMED = "rdmd";

	/**
	 * TODO: document this field (theys, Oct 7, 2009)
	 * 
	 * serialVersionUID (long)
	 */
	private static final long serialVersionUID = 221377110802359630L;

	/**
	 * TODO: document this field (theys, Nov 3, 2009)
	 * 
	 * signatureKey (IBCUtil)
	 */
	private static final String signatureKey = "z`nghBDXxxq|FOcbjMNB^NZVO_t`@NgHD[nKCuyUkhSt^k]mgcfVasuWBREtmMYYVETV`Dm|Bg@MwmKalfus]qZlYQfTz|NT{fZMVVbkFXZylNP{xAopt]|Bw[ZGAUAtspw]GQT~z]_R^WXFxVzG]";

	/**
	 * TODO: document this field (theys, Nov 3, 2009)
	 * 
	 * SUCCESS (IBCUtil)
	 */
	private final static String SUCCESS = "ok";

	/**
	 * 
	 * TODO: document this method (theys, Sep 25, 2009)
	 * 
	 * @param request request allows method to retrieve data
	 * @param out out allows method to display results
	 * 
	 */
	public static void cancel (final HttpServletRequest request,
			final JspWriter out) {
		final JSONObject json = new JSONObject ();
		try {
			json.put ("result", "");
			json.put ("orderSource", "");
			json.put ("orderCode", "");
			json.put ("productCode", "");
			json.put ("redeemed", "");
		} catch (final JSONException e) {
			// Default catch action, report bug (theys, Sep 25, 2009)
			AppiusClaudiusCaecus.reportBug (e);
		}

		try {
			if (null == request.getParameter ("orderSource")
					|| null == request.getParameter ("orderCode")
					|| null == request.getParameter ("signature")
					|| "".equals (request.getParameter ("orderSource"))
					|| "".equals (request.getParameter ("orderCode"))
					|| "".equals (request.getParameter ("signature")))
				throw new DataException (
						"Invalid Parameters for command 'cancel'");
			final String orderSource = request
					.getParameter ("orderSource");
			final String orderCode = request.getParameter ("orderCode");
			final String signature = request.getParameter ("signature");

			IBCUtil.checkSignature (signature);

			IBCUtil order = null;
			try {
				order = new IBCUtil (orderSource, orderCode);
			} catch (final NotFoundException e) {
				throw new DataException ("invalidOrder");
			}

			if ( !IBCUtil.REDEEMED.equals (order.orderStatus)) {
				order.setOrderStatus (IBCUtil.CANCELED);
			} else throw new DataException ("alreadyRedeemed");

			try {
				json.put ("result", IBCUtil.SUCCESS);
				json.put ("orderSource", order.getOrderSource ());
				json.put ("orderCode", order.getOrderCode ());
				json.put ("productCode", order.getProductCode ());
			} catch (final JSONException e) {
				// Default catch action, report bug (theys, Sep 25,
				// 2009)
				AppiusClaudiusCaecus.reportBug (e);
			}

		} catch (final DataException e) {
			try {
				json.put ("result", IBCUtil.FAIL);
				json.put ("error", e.getComplaint ());
				json.put ("orderSource", "");
				json.put ("orderCode", "");
				json.put ("productCode", "");
				json.put ("redeemed", "");
			} catch (final JSONException e1) {
				// Default catch action, report bug (theys, Sep 25,
				// 2009)
				AppiusClaudiusCaecus.reportBug (e1);
			}
		}

		IBCUtil.displayJSON (json, out);
	}

	/**
	 * <p>
	 * Verify the signature parameter as a sha1 and hex code. Throw
	 * DataException if the code is invalid.
	 * </p>
	 * 
	 * @param signature is a literal string of a sha1 and hex code used
	 *            for security.
	 * @throws DataException WRITEME
	 */
	private static void checkSignature (final String signature)
		throws DataException {
		if ( !IBCUtil.signatureKey.equals (signature))
			throw new DataException ("Signature is invalid");
	}

	/**
	 * <p>
	 * Display the JSONObject created as output.
	 * </p>
	 * <p>
	 * BRP: Say what? TODO: explain why anyone would call this?
	 * </p>
	 * 
	 * @param json json is the JSON to be displayed
	 * @param out is the JSPWriter to display json
	 * 
	 */
	private static void displayJSON (final JSONObject json,
			final JspWriter out) {
		try {
			out.print (json.toString ());
		} catch (final IOException e) {
			// Default catch action, report bug (theys, Sep 25, 2009)
			AppiusClaudiusCaecus.reportBug (e);
		}
	}

	/**
	 * 
	 * <p>
	 * This method should be called when the 'command' parameter is
	 * "generate". This will create a JSON object with the keys result,
	 * error, orderSource, orderCode, and productCode and display the
	 * JSONObject as a string thru the JSPWriter object.
	 * </p>
	 * 
	 * <p>
	 * The parameters in the request object should be productCode and
	 * signature. productCode is a literal String that must equal
	 * "monthly", "trimonthly", "semiannual", or "annual". Signature is
	 * a literal string of a sha1 and hex code used for security.
	 * </p>
	 * 
	 * @param request request allows method to retrieve data
	 * @param out allows method to display results
	 * 
	 * @throws DataException if the parameters in the request object are
	 *             invalid
	 */
	public static void generate (final HttpServletRequest request,
			final JspWriter out) throws DataException {
		final JSONObject json = new JSONObject ();
		try {
			json.put ("result", "");
			json.put ("orderSource", "");
			json.put ("orderCode", "");
			json.put ("productCode", "");
		} catch (final JSONException e) {
			// Default catch action, report bug (theys, Sep 25, 2009)
			AppiusClaudiusCaecus.reportBug (e);
		}

		try {
			if (null == request.getParameter ("productCode")
					|| null == request.getParameter ("signature")
					|| "".equals (request.getParameter ("productCode"))
					|| "".equals (request.getParameter ("signature")))
				throw new DataException (
						"Invalid Parameters for command 'generate'");
			final String productCode = request
					.getParameter ("productCode");
			final String signature = request.getParameter ("signature");

			IBCUtil.checkSignature (signature);

			final IBCUtil order = new IBCUtil (productCode);

			try {
				json.put ("result", IBCUtil.SUCCESS);
				json.put ("orderSource", order.getOrderSource ());
				json.put ("orderCode", order.getOrderCode ());
				json.put ("productCode", order.getProductCode ());
			} catch (final JSONException e) {
				// Default catch action, report bug (theys, Sep 25,
				// 2009)
				AppiusClaudiusCaecus.reportBug (e);
			}

		} catch (final DataException e) {
			try {
				json.put ("result", IBCUtil.FAIL);
				json.put ("error", e.getComplaint ());
				json.put ("orderSource", "");
				json.put ("orderCode", "");
				json.put ("productCode", "");
			} catch (final JSONException e1) {
				// Default catch action, report bug (theys, Sep 25,
				// 2009)
				AppiusClaudiusCaecus.reportBug (e1);
			}
		}

		IBCUtil.displayJSON (json, out);
	}

	/**
	 * 
	 * TODO: document this method (theys, Sep 29, 2009)
	 * 
	 * @return WRITEME
	 */
	private static String generateRandomCode () {
		String eCode = "";
		boolean found = false;
		final Random rnd = new Random ();

		PreparedStatement checkExists = null;
		try {
			checkExists = AppiusConfig
					.getDatabaseConnection ()
					.prepareStatement (
							"SELECT COUNT(*) FROM ibc WHERE order_code=?");
			do {
				// generate 10 random characters from the given
				for (int i = 0; i < 12; ++i) {
					eCode += String
							.valueOf (IBCUtil.orderCodeChars [(int) (rnd
									.nextDouble () * IBCUtil.orderCodeChars.length)]);
				}
				checkExists.setString (1, eCode);
				if (checkExists.execute ()) {
					final ResultSet exists = checkExists
							.getResultSet ();
					exists.next ();
					if (exists.getInt (1) == 0) {
						found = true;
					}
				}
			} while ( !found);
			checkExists.close ();
		} catch (final SQLException e) {
			throw AppiusClaudiusCaecus.fatalBug (e);
		} finally {
			if (null != checkExists) {
				try {
					checkExists.close ();
				} catch (final SQLException e) {
					AppiusClaudiusCaecus.reportBug ("finally", e);
				}
			}
		}

		return eCode;
	}

	/**
	 * TODO: document this method (theys, Oct 7, 2009)
	 * 
	 * @param order_code WRITEME
	 * @return WRITEME
	 */
	public static String getEnrolmentTitle (final String order_code) {
		IBCUtil order;
		try {
			order = new IBCUtil (IBCUtil.ORDER_SOURCE_IBC, order_code);
			return Enrolment.getByID (order.getProductCode ())
					.getTitle ();
		} catch (final NotFoundException e) {
			return "void";
		}
	}

	/**
	 * 
	 * TODO: document this method (theys, Sep 29, 2009)
	 * 
	 * @return WRITEME
	 */
	public static String getSignatureKey () {
		// default getter (theys, Sep 29, 2009)
		return IBCUtil.signatureKey;
	}

	/**
	 * 
	 * TODO: document this method (theys, Sep 25, 2009)
	 * 
	 * @param request request allows method to retrieve data
	 * @param out out allows method to display results
	 * 
	 */
	public static void getStatus (final HttpServletRequest request,
			final JspWriter out) {
		final JSONObject json = new JSONObject ();
		try {
			json.put ("result", "");
			json.put ("orderSource", "");
			json.put ("orderCode", "");
			json.put ("productCode", "");
			json.put ("redeemed", "");
			json.put ("redeemeByUserID", "");
			json.put ("redeemedByUserNick", "");
		} catch (final JSONException e) {
			// Default catch action, report bug (theys, Sep 25, 2009)
			AppiusClaudiusCaecus.reportBug (e);
		}

		try {
			if (null == request.getParameter ("orderSource")
					|| null == request.getParameter ("orderCode")
					|| null == request.getParameter ("signature")
					|| "".equals (request.getParameter ("orderSource"))
					|| "".equals (request.getParameter ("orderCode"))
					|| "".equals (request.getParameter ("signature")))
				throw new DataException (
						"Invalid Parameters for command 'getStatus'");
			final String orderSource = request
					.getParameter ("orderSource");
			final String orderCode = request.getParameter ("orderCode");
			final String signature = request.getParameter ("signature");

			IBCUtil.checkSignature (signature);

			final IBCUtil order;

			try {
				order = new IBCUtil (orderSource, orderCode);
			} catch (final NotFoundException e) {
				throw new DataException (
						"invalidOrder: no orders exist");
			}

			AbstractUser redeemer = null;
			String id = "";
			String username = "";

			redeemer = User.getByID (order.getRedeemedByUserID ());
			if (null == redeemer) {
				// Order has not been redeemed
				id = "";
				username = "";
			} else {
				id = String.valueOf (redeemer.getUserID ());
				username = redeemer.getAvatarLabel ();
			}

			try {
				json.put ("result", IBCUtil.SUCCESS);
				json.put ("orderSource", order.getOrderSource ());
				json.put ("orderCode", order.getOrderCode ());
				json.put ("productCode", order.getProductCode ());
				json.put ("redeemed", order.getOrderStatus ());
				json.put ("redeemedByUserID", id);
				json.put ("redeemedByUserNick", username);
			} catch (final JSONException e) {
				// Default catch action, report bug (theys, Sep 25,
				// 2009)
				AppiusClaudiusCaecus.reportBug (e);
			}

		} catch (final DataException e) {
			try {
				json.put ("result", IBCUtil.FAIL);
				json.put ("error", e.getComplaint ());
				json.put ("orderSource", "");
				json.put ("orderCode", "");
				json.put ("productCode", "");
				json.put ("redeemed", "");
				json.put ("redeemeByUserID", "");
				json.put ("redeemedByUserNick", "");
			} catch (final JSONException e1) {
				// Default catch action, report bug (theys, Sep 25,
				// 2009)
				AppiusClaudiusCaecus.reportBug (e1);
			}
		}

		IBCUtil.displayJSON (json, out);
	}

	/**
	 * 
	 * TODO: document this method (theys, Sep 25, 2009)
	 * 
	 * @param request WRITEME
	 * @param out WRITEME
	 * 
	 */
	public static void handleRequest (final HttpServletRequest request,
			final JspWriter out) {
		try {
			if (null != request.getParameter ("command")
					&& !"".equals (request.getParameter ("command"))) {
				final String command = request.getParameter ("command");

				if ("generate".equals (command)) {
					IBCUtil.generate (request, out);
				} else if ("getStatus".equals (command)) {
					IBCUtil.getStatus (request, out);
				} else if ("cancel".equals (command)) {
					IBCUtil.cancel (request, out);
				} else throw new DataException ("invalidParameter: "
						+ command);

			} else throw new DataException (
					"Invalid parameters:  The command parameter is required and was not issued.");
		} catch (final DataException e) {
			final JSONObject json = new JSONObject ();
			try {
				json.put ("result", IBCUtil.FAIL);
				json.put ("error", e.getComplaint ());
			} catch (final JSONException e1) {
				// Default catch action, report bug (theys, Sep 25,
				// 2009)
				AppiusClaudiusCaecus.reportBug (e1);
			}

			IBCUtil.displayJSON (json, out);
		}
	}

	/**
	 * 
	 * TODO: document this method (theys, Sep 23, 2009)
	 * 
	 * @param codeUntrimmed WRITEME
	 * @param user WRITEME
	 * @throws DataException WRITEME
	 */
	public static void redeemCode (final String codeUntrimmed,
			final User user) throws DataException {
		final String code = codeUntrimmed.trim ();
		if (null == code || null == user || "".equals (code))
			throw new DataException (LibMisc.getText ("invalid_ecode"));
		final IBCUtil order;

		try {
			order = new IBCUtil (IBCUtil.ORDER_SOURCE_IBC, code);
		} catch (final NotFoundException e) {
			throw new DataException (LibMisc.getText ("invalid_ecode"));
		}

		IBCUtil.verifyCode (code);

		try {
			final UserEnrolment enrol = new UserEnrolment (order
					.getOrderSource (), order.getProductCode (), user
					.getUserID ());
			enrol.activate (true);
			enrol.setOrderCode (order.getOrderCode ());
			enrol.changed ();
			order.setOrderStatus (IBCUtil.REDEEMED);
		} catch (final NotFoundException e) {
			AppiusClaudiusCaecus
					.reportBug (
							"Chase-It code not being redeemed, product code is incorrectly set.",
							e);
			throw new DataException (LibMisc.getText ("unexpected"));
		}
	}

	/**
	 * 
	 * TODO: document this method (theys, Sep 29, 2009)
	 * 
	 * @param code WRITEME
	 * @return WRITEME
	 * @throws DataException WRITEME
	 */
	public static boolean verifyCode (final String code)
		throws DataException {
		final IBCUtil order;

		try {
			order = new IBCUtil (IBCUtil.ORDER_SOURCE_IBC, code);
		} catch (final NotFoundException e) {
			throw new DataException (LibMisc.getText ("invalid_ecode"));
		}

		if (IBCUtil.REDEEMED.equals (order.getOrderStatus ()))
			throw new DataException (LibMisc.getText ("ecode_redeemed"));

		// If we make it this far in the code, then the code is valid
		return true;
	}

	/**
	 * TODO: document this field (theys, Nov 3, 2009)
	 * 
	 * orderCode (IBCUtil)
	 */
	private String orderCode = "";
	/**
	 * Order Source must be one of the following values
	 * "avl, cncl, or rdmd"
	 */
	private String orderSource = "";

	/**
	 * WRITEME
	 */
	private String orderStatus = null;
	/**
	 * WRITEME
	 */
	private int productCode = 0;
	/**
	 * WRITEME
	 */
	private int redeemedByUserID = 0;

	/**
	 * @param rs WRITEME
	 */
	public IBCUtil (final ResultSet rs) {
		try {
			set (rs);
		} catch (final SQLException e) {
			// Default catch action, report bug (theys, Sep 28, 2009)
			AppiusClaudiusCaecus.reportBug (e);
		}
	}

	/**
	 * 
	 * @param productCode2 WRITEME
	 * @throws DataException WRITEME
	 */
	public IBCUtil (final String productCode2) throws DataException {
		try {
			productCode = Enrolment.getByProductCode (productCode2)
					.getProductID ();
		} catch (final NotFoundException e) {
			// Default catch action, report bug (theys, Sep 28, 2009)
			throw new DataException ("invalidProduct: " + productCode2);
		}
		orderCode = IBCUtil.generateRandomCode ();
		orderStatus = IBCUtil.AVAILABLE;
		orderSource = IBCUtil.ORDER_SOURCE_IBC;

		try {
			insert ();
		} catch (final SQLException e1) {
			AppiusClaudiusCaecus.reportBug (e1);
		}
	}

	/**
	 * 
	 * @param order_source WRITEME
	 * @param order_code WRITEME
	 * @throws NotFoundException WRITEME
	 */
	private IBCUtil (final String order_source, final String order_code)
		throws NotFoundException {
		Connection con = null;
		PreparedStatement st = null;
		ResultSet rs = null;
		try {
			con = AppiusConfig.getDatabaseConnection ();
			st = con
					.prepareStatement ("SELECT * FROM ibc WHERE order_source=? AND order_code=?");
			st.setString (1, order_source);
			st.setString (2, order_code);
			rs = st.executeQuery ();
			rs.next ();
			set (rs);
		} catch (final SQLException e) {
			throw new NotFoundException (orderSource + "-" + orderCode);
		} finally {
			if (null != rs) {
				try {
					rs.close ();
				} catch (final SQLException e) { /* No Op */
				}
			}
			if (null != st) {
				try {
					st.close ();
				} catch (final SQLException e) { /* No Op */
				}
			}
			if (null != con) {
				try {
					con.close ();
				} catch (final SQLException e) { /* No Op */
				}
			}
		}
	}

	/**
	 * 
	 * This is an overriding method.
	 * 
	 * @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 ibc SET "
							+ " product_code=?, order_status=?, redeemedByUserID=? "
							+ " WHERE order_source=? AND order_code=?");
			st.setInt (1, productCode);
			st.setString (2, orderStatus.toLowerCase (Locale.ENGLISH));
			if (0 == redeemedByUserID) {
				st.setNull (3, java.sql.Types.INTEGER);
			} else {
				st.setInt (3, redeemedByUserID);
			}
			st.setString (4, orderSource);
			st.setString (5, orderCode);
			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 */
				}
			}
		}

	}

	/**
	 * 
	 * This is an overriding method.
	 * 
	 * @see org.starhope.appius.sql.SQLPeerDatum#getCacheUniqueID()
	 */
	@Override
	protected String getCacheUniqueID () {
		// TODO Auto-generated method stub (theys, Sep 28, 2009)
		return null;
	}

	/**
	 * 
	 * TODO: document this method (theys, Oct 13, 2009)
	 * 
	 * @return WRITEME
	 */
	public String getOrderCode () {
		// default getter (theys, Sep 28, 2009)
		return orderCode;
	}

	/**
	 * 
	 * TODO: document this method (theys, Oct 13, 2009)
	 * 
	 * @return WRITEME
	 */
	public String getOrderSource () {
		// default getter (theys, Sep 28, 2009)
		return orderSource;
	}

	/**
	 * 
	 * TODO: document this method (theys, Oct 13, 2009)
	 * 
	 * @return WRITEME
	 */
	public String getOrderStatus () {
		// default getter (theys, Sep 28, 2009)
		return orderStatus;
	}

	/**
	 * 
	 * TODO: document this method (theys, Oct 13, 2009)
	 * 
	 * @return WRITEME
	 */
	public int getProductCode () {
		// default getter (theys, Sep 28, 2009)
		return productCode;
	}

	/**
	 * 
	 * TODO: document this method (theys, Oct 13, 2009)
	 * 
	 * @return WRITEME
	 */
	public int getRedeemedByUserID () {
		// default getter (theys, Sep 28, 2009)
		return redeemedByUserID;
	}

	/**
	 * @throws SQLException WRITEME
	 */
	public void insert () throws SQLException {
		Connection con = null;
		PreparedStatement st = null;
		try {
			con = AppiusConfig.getDatabaseConnection ();
			st = con
					.prepareStatement ("INSERT INTO ibc  (order_source, order_code, product_code, order_status)  VALUES (?,?,?,?)");
			st.setString (1, orderSource);
			st.setString (2, orderCode);
			st.setInt (3, productCode);
			st.setString (4, orderStatus.toLowerCase (Locale.ENGLISH));
			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 */
				}
			}
		}
	}

	/**
	 * 
	 * This is an overriding method.
	 * 
	 * @see org.starhope.appius.sql.SQLPeerDatum#set(java.sql.ResultSet)
	 */
	@Override
	protected void set (final ResultSet rs) throws SQLException {
		orderSource = rs.getString ("order_source");
		orderCode = rs.getString ("order_code");
		productCode = rs.getInt ("product_code");
		orderStatus = rs.getString ("order_status");
		redeemedByUserID = rs.getInt ("redeemedByUserID");
	}

	/**
	 * 
	 * TODO: document this method (theys, Sep 29, 2009)
	 * 
	 * @param orderStatus1 WRITEME
	 */
	public void setOrderStatus (final String orderStatus1) {
		// default setter (theys, Sep 28, 2009)
		orderStatus = orderStatus1;
		changed ();
	}

	/**
	 * 
	 * TODO: document this method (theys, Sep 29, 2009)
	 * 
	 * @param productCode1 WRITEME
	 */
	public void setProductCode (final int productCode1) {
		// default setter (theys, Sep 28, 2009)
		productCode = productCode1;
		changed ();
	}

	/**
	 * 
	 * TODO: document this method (theys, Sep 29, 2009)
	 * 
	 * @param redeemedByUserID1 WRITEME
	 */
	public void setRedeemedByUserID (final int redeemedByUserID1) {
		// default setter (theys, Sep 28, 2009)
		redeemedByUserID = redeemedByUserID1;
		changed ();
	}
}
