/**
 * An object to store the history data in the HTML5 database.
 *
 * Copyright 2008-2009 Palm, Inc. All rights reserved.
 */

/**
 * @constructor
 *
 * @param {Object} options
 * @param {Object} onSuccess
 * @param {Object} onFailure
 */
function HistoryStore(options, onSuccess, onFailure) {

	try {
		this.displayName = options.displayName || this.name;
		this.estimatedSize = options.estimatedSize;
		this.database = options.database;
		this.database.transaction(this._createTable.bind(this, onSuccess, onFailure));
	} catch (e) {
		Mojo.Log.logException(e, 'HistoryStore()');
	}
}

/** @private */
HistoryStore.prototype._addHistoryEntry = function(url, title, date, onSuccess, onFailure, transaction) {

	var utcNow = date.getUTC().getTime();
	var utcMidnight = date.getUTC().getMidnight().getTime();

	// I hope these operations happen in order. I haven't been able to verify that they
	// will be. If not then we will have to marshal the INSERT parameters to _deleteSuccess
	// so that it can initiate the second call.
	this._executeSql(transaction, function() {}, function(transaction, err) {
			Mojo.Log.error("Error deleting history item: code: %d, message:'%s'.", err.code, err.message);
		},
		"DELETE FROM 'history' WHERE date > ? AND url = ?", [utcMidnight, url] );

	this._executeSql(transaction, onSuccess, onFailure,
		"INSERT INTO 'history' (date, url, title) VALUES(?, ?, ?)", [utcNow, url, title]);
};

/**
 * Add a history entry.
 *
 * @param {String} url
 * @param {String} title
 * @param {Date} date (localtime)
 * @param {function} onSuccess
 * @param {function} onFailure
 */
HistoryStore.prototype.addHistoryEntry = function(url, title, date, onSuccess, onFailure) {

	try	{
		if (!url || (url.length === 0)) {
			onFailure("No URL", null);
			return;
		}
		this.database.transaction(this._addHistoryEntry.bind(this, url, title, date, onSuccess, onFailure));
	} catch (e) {
		Mojo.Log.logException(e);
		onFailure(undefined, e);
	}
};

HistoryStore.prototype._deleteHistoryBefore = function(date, onSuccess, onFailure, transaction) {
	var utc = date;
	if (date instanceof Date) {
		utc = date.getUTC().getTime();
	}

	this._executeSql(transaction, onSuccess, onFailure,
				"DELETE FROM 'history' WHERE date < ?", [utc]);
};

/**
 * Delete all history entries older than the supplied date.
 *
 * @param {Date} date The date to delete all history before. Can be a Date object or a number.
 *                     If it's a date object then the UTC time is used. If it's a number then
 *                     it must be UTC epoch value.
 */
HistoryStore.prototype.deleteHistoryBefore = function(date, onSuccess, onFailure) {

	try	{
		this.database.transaction(this._deleteHistoryBefore.bind(this, date, onSuccess, onFailure));
	} catch (e) {
		Mojo.Log.logException(e, 'deleteHistoryBefore');
		onFailure(undefined, e);
	}
};

/** @private */
HistoryStore.prototype._extractHistory = function(historyList, onSuccess, onFailure, offset, limit, transaction, resultSet) {
	try {

		for (var i = 0; i < resultSet.rows.length; i++) {
			var uriRef = resultSet.rows.item(i);

			var title = uriRef.title || null;
			historyList.addPage(uriRef.url, title, uriRef.date);
		}

		onSuccess(offset, historyList);

	} catch (e) {
		Mojo.Log.logException(e, '_extractHistory');
		onFailure(transaction, {code:0, message: e.message});
	}
};

/** @private */
HistoryStore.prototype._readHistory = function(historyList, filterText, onSuccess, onFailure, transaction, offset, limit) {
	var sql;
	if (filterText) {
		sql = "SELECT * FROM history WHERE (url LIKE '%" + filterText +
			"%') or (title LIKE '%" + filterText + "%') ORDER BY date DESC";
	}
	else {
		sql = "SELECT * FROM 'history'";
	}
	this._executeSql(transaction, this._extractHistory.bind(this, historyList, onSuccess, onFailure, offset, limit), onFailure, sql);
};

/**
 * Read all history from the database.
 *
 * @param {HistoryList} historyList
 * @param {String} filterText
 * @param {function} onSuccess
 * @param {function} onFailure
 */
HistoryStore.prototype.readHistory = function(historyList, filterText, onSuccess, onFailure, offset, limit) {
	this.database.transaction(this._readHistory.bind(this, historyList, filterText, onSuccess, onFailure, offset, limit));
};

/** @private */
HistoryStore.prototype._executeSql = function(transaction, onSuccess, onFailure, sqlString, valuesList) {
	valuesList = valuesList || [];
	onSuccess = onSuccess || function() {};
	onFailure = onFailure || function() {};
	
	try {
   		transaction.executeSql(sqlString, valuesList, onSuccess, onFailure);
	}
	catch (e) {
		onFailure(e, '_executeSql');
		throw e;
	}
};

/** @private */
HistoryStore.prototype._createTable = function(onSuccess, onFailure, transaction) {
	var sql = "CREATE TABLE IF NOT EXISTS 'history' " +
		"('id' INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " +
		"'date' INTEGER(8) NOT NULL DEFAULT '', 'url' TEXT NOT NULL DEFAULT '', 'title' TEXT NULL DEFAULT NULL)";
	this._executeSql(transaction, onSuccess, onFailure, sql);
};

HistoryStore.prototype.addHistory = function(url, title, date, onSuccess, onFailure) {

	try {
		if (!url || (url.length === 0)) {
			onFailure("No URL", null);
			return;
		}
		
		var utcNow = date.getUTC().getTime();
		var delcmd = "DELETE FROM history WHERE url = ?";
		var inscmd = "INSERT INTO history (date, url, title) VALUES(?, ?, ?)";

		this.database.transaction(function(transaction) {
			onSuccess = onSuccess || function(){};
			onFailure = onFailure || function(){};
			transaction.executeSql(delcmd, [url], onSuccess, onFailure);
			transaction.executeSql(inscmd, [utcNow, url, title], onSuccess, onFailure);
		});

	} catch (e) {

		onFailure(e.message, -1);
	}
};

HistoryStore.prototype.getCount = function(filterText, onSuccess, onFailure) {

	try {
		var count = "History Count";
		var cntcmd = "SELECT COUNT(1) as '" + count + "' FROM history";
		if (filterText && (filterText.length > 0)) {
			cntmd += " WHERE (url LIKE '%" + filterText + "%') or (title LIKE '%" + filterText + "%')";
		}
		
		this.database.transaction(function(transaction){
			onSuccess = onSuccess || function(){};
			onFailure = onFailure || function(){};
			transaction.executeSql(cntcmd, [], function(transaction, results){
				onSuccess(results.rows.item(0)[count]);
			}, function(transaction, error){
				onFailure(error.message, error.code);
			});
		});
	} catch (e) {
		
		onFailure(e.message, -1);
	}
};

HistoryStore.prototype.getHistory = function(historyList, filterText, onSuccess, onFailure, offset, limit) {

	try {
		var sqlcmd;
		if (filterText) {
			sqlcmd = "SELECT * FROM history WHERE (url LIKE '%" + filterText + "%') or (title LIKE '%" + filterText + "%') ORDER BY date DESC";
		} else {
			sqlcmd = "SELECT * FROM history ORDER BY date DESC";
		}

		// Add the limits
		if (limit !== undefined) {
			sqlcmd = sqlcmd + " LIMIT " + limit;
			if (offset !== undefined) {
				sqlcmd = sqlcmd + " OFFSET " + offset;
			}
		}

		this.database.transaction(function(transaction) {

			onSuccess = onSuccess || function(){};
			onFailure = onFailure || function(){};
			transaction.executeSql(sqlcmd,
				[],
				function(transaction, results) {

					for (var i = 0; i < results.rows.length; i++) {
						var uriRef = results.rows.item(i);
						var title = uriRef.title || null;
						historyList.addNewPage(uriRef.url, title, uriRef.date);
					}

					onSuccess(historyList, offset, limit);
				},
				onFailure);
		});

	} catch (e) {
		onFailure(e.message, -1);
	}
};
