//
// $Id$
package com.threerings.user;
import static com.threerings.user.Log.log;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.List;
import com.samskivert.io.PersistenceException;
import com.samskivert.jdbc.ConnectionProvider;
import com.samskivert.jdbc.DatabaseLiaison;
import com.samskivert.jdbc.JDBCUtil;
import com.samskivert.jdbc.JORARepository;
import com.samskivert.jdbc.jora.Cursor;
import com.samskivert.jdbc.jora.FieldMask;
import com.samskivert.jdbc.jora.Table;
/**
* Maintains persistent reward information.
*/
public class RewardRepository extends JORARepository
{
/**
* Creates the repository with the specified connection provider.
*
* @exception PersistenceException thrown if an error occurs communicating with the underlying
* persistence facilities.
*/
public RewardRepository (ConnectionProvider provider)
throws PersistenceException
{
super(provider, OOOUserRepository.USER_REPOSITORY_IDENT);
}
/**
* Creates a new RewardInfo record in the database. info should have the
* description, data and expiration filled in.
*/
public void createReward (RewardInfo info)
throws PersistenceException
{
info.maxEligibleId = getMaxUserId();
store(_infoTable, info);
}
/**
* Immediately expires a reward by setting the expiration to the current date then making a
* call to purgeExpiredRewards.
*/
public void expireReward (int rewardId)
throws PersistenceException
{
RewardInfo info = new RewardInfo();
info.rewardId = rewardId;
info.expiration = new Date(System.currentTimeMillis());
updateField(_infoTable, info, "expiration");
purgeExpiredRewards();
}
/**
* Attempt to activate the reward for an account. If this account has not already activated
* the reward, a new RewardRecord will be created. Returns true if a new RewardRecord is
* created, false otherwise.
*/
public boolean activateReward (int rewardId, String account)
throws PersistenceException
{
return activateReward(rewardId, account, null);
}
public boolean activateReward (int rewardId, String account, String param)
throws PersistenceException
{
final RewardRecord rr = new RewardRecord();
rr.rewardId = rewardId;
rr.account = account;
rr.param = param == null ? "" : param;
// try to insert it; catching duplicate row exceptions and reporting that the reward is
// already activated
return executeUpdate(new Operation() {
public Boolean invoke (Connection conn, DatabaseLiaison liaison)
throws SQLException, PersistenceException
{
try {
_rewardsTable.insert(conn, rr);
return true;
} catch (SQLException sqe) {
if (liaison.isDuplicateRowException(sqe)) {
return false;
} else {
throw sqe;
}
}
}
});
}
/**
* Activates monthly rewards of the specified ID for the account between start and end. If they
* already have monthly rewards for this ID, will add new ones only at appropriate monthly
* intervals from the existing ones.
*/
public boolean activateMonthlyRewards (final int rewardId, final String account,
final java.util.Date start, final java.util.Date end)
throws PersistenceException
{
// Setup an example to find relevant rewards.
final RewardRecord example = new RewardRecord();
example.rewardId = rewardId;
example.account = account;
final FieldMask mask = _rewardsTable.getFieldMask();
mask.setModified("rewardId");
mask.setModified("account");
final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return executeUpdate(new Operation() {
public Boolean invoke (Connection conn, DatabaseLiaison liaison)
throws SQLException, PersistenceException
{
// Find the most recent reward this account already has of this type.
java.util.Date latest = null;
Cursor matches =
_rewardsTable.queryByExample(conn, example, mask);
RewardRecord rec;
while ((rec = matches.next()) != null) {
if (rec.param == null) {
log.warning("Missing monthly reward param: ", "rewardId", rewardId,
"account", account);
continue;
}
try {
java.util.Date thisDate = dateFormat.parse(rec.param);
if (latest == null || thisDate.after(latest)) {
latest = thisDate;
}
} catch (ParseException pe) {
log.warning("Bogus reward param: ", "rewardId", rewardId,
"account", account, "param", rec.param);
continue;
}
}
// We use the maximum of our provided start time and a month-after-latest
// as the first time we'll input a reward. Note that this can result
// in no rewards being given.
Calendar startCal = Calendar.getInstance();
if (latest != null) {
startCal.setTime(latest);
startCal.add(Calendar.MONTH, 1);
if (startCal.getTime().before(start)) {
startCal.setTime(start);
}
} else {
startCal.setTime(start);
}
// Add in any appropriate rewards between the modified start and end.
while (startCal.getTime().before(end)) {
final RewardRecord rr = new RewardRecord();
rr.rewardId = rewardId;
rr.account = account;
rr.param = dateFormat.format(startCal.getTime());
_rewardsTable.insert(conn, rr);
startCal.add(Calendar.MONTH, 1);
}
return true;
}
});
}
/**
* Deactivates all rewards of the specified ID and account during the time window.
* Returns the number of rewards deleted.
*/
public int deactivateMonthlyRewards (final int rewardId, final String account,
final java.util.Date start, final java.util.Date end)
throws PersistenceException
{
return executeUpdate(new Operation() {
public Integer invoke (Connection conn, DatabaseLiaison liaison)
throws SQLException, PersistenceException
{
String delQuery = "delete from REWARD_RECORDS where REWARD_ID = ? and ACCOUNT = ?" +
" and PARAM >= ? and PARAM <= ? and REDEEMER_IDENT is null";
final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String startStr = dateFormat.format(start);
String endStr = dateFormat.format(end);
PreparedStatement stmt = null;
try {
stmt = conn.prepareStatement(delQuery);
stmt.setInt(1, rewardId);
stmt.setString(2, account);
stmt.setString(3, startStr);
stmt.setString(4, endStr);
return stmt.executeUpdate();
} finally {
JDBCUtil.close(stmt);
}
}
});
}
/**
* Returns all currently active rewards.
*/
public List loadActiveRewards ()
throws PersistenceException
{
return loadAll(_infoTable, "where EXPIRATION > NOW()");
}
/**
* Returns all rewards.
*/
public List loadRewards ()
throws PersistenceException
{
return loadAll(_infoTable, "order by REWARD_ID desc");
}
/**
* Returns all RewardRecords that match the given account name and whose eligible
* date is in the past. The returned list will be sorted by rewardId.
*/
public List loadActivatedRewards (String account)
throws PersistenceException
{
String condition = "where ACCOUNT = " + JDBCUtil.escape(account) +
" order by REWARD_ID";
return loadAll(_rewardsTable, condition);
}
/**
* Returns all RewardRecords that match either the account or redeemer identifier.
* The returned list will be sorted by rewardId.
*/
public List loadActivatedRewards (String account, String redeemerIdent)
throws PersistenceException
{
String condition = "where ACCOUNT = " + JDBCUtil.escape(account) +
" or REDEEMER_IDENT = " + JDBCUtil.escape(redeemerIdent) +
" order by REWARD_ID, PARAM";
return loadAll(_rewardsTable, condition);
}
/**
* Returns all RewardRecords for a specified rewardId that match
* either the account or redeemer identifier.
*/
public List loadActivatedReward (
String account, String redeemerIdent, int rewardId)
throws PersistenceException
{
String condition = "where REWARD_ID = " + rewardId +
" and (ACCOUNT = " + JDBCUtil.escape(account) +
" or REDEEMER_IDENT = " + JDBCUtil.escape(redeemerIdent) + ")";
return loadAll(_rewardsTable, condition);
}
/**
* Returns all RewardRecords for a specified rewardId and
* param that match either the account or redeemer identifier.
*/
public List loadActivatedReward (
String account, String redeemerIdent, int rewardId, String param)
throws PersistenceException
{
String paramStr = (param == null) ? "PARAM is NULL" : "PARAM = " + JDBCUtil.escape(param);
String condition = "where REWARD_ID = " + rewardId +
" and " + paramStr +
" and (ACCOUNT = " + JDBCUtil.escape(account) +
" or REDEEMER_IDENT = " + JDBCUtil.escape(redeemerIdent) + ")";
return loadAll(_rewardsTable, condition);
}
/**
* Updates the supplied RewardRecord with the indicated redeemerIdent and store it
* to the database.
*/
public boolean redeemReward (RewardRecord record, String redeemerIdent)
throws PersistenceException
{
String update = "update " + _rewardsTable.getName() + " set REDEEMER_IDENT = " +
JDBCUtil.escape(redeemerIdent) + " where REDEEMER_IDENT is NULL and REWARD_ID = " +
record.rewardId + " and ACCOUNT = " + JDBCUtil.escape(record.account) +
" and PARAM = " + JDBCUtil.escape(record.param);
return update(update) > 0;
}
/**
* Summarizes the reward data and purges expired rewards.
*/
public void purgeExpiredRewards ()
throws PersistenceException
{
executeUpdate(new Operation