--[[
	Auctioneer Addon for World of Warcraft(tm).
	Version: 3.9.0.1257 (Kangaroo)
	Revision: $Id: BeanCounterDB.lua 1106 2006-12-04 01:05:50Z esamynn $

	BeanCounterDB - BeanCounter database functions

	License:
		This program is free software; you can redistribute it and/or
		modify it under the terms of the GNU General Public License
		as published by the Free Software Foundation; either version 2
		of the License, or (at your option) any later version.

		This program is distributed in the hope that it will be useful,
		but WITHOUT ANY WARRANTY; without even the implied warranty of
		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
		GNU General Public License for more details.

		You should have received a copy of the GNU General Public License
		along with this program(see GPL.txt); if not, write to the Free Software
		Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

	Note:
		This AddOn's source code is specifically designed to work with
		World of Warcraft's interpreted AddOn system.
		You have an implicit licence to use this AddOn with these facilities
		since that is it's designated purpose as per:
		http://www.fsf.org/licensing/licenses/gpl-faq.html#InterpreterIncompat
--]]

-------------------------------------------------------------------------------
-- Global Data Members
-------------------------------------------------------------------------------
BeanCounterAccountDB = {};	-- Database on disk
BeanCounterRealmDB = nil; 	-- Currently loaded database (reference to BeanCounterAccountDB[realm])

-------------------------------------------------------------------------------
-- Local Data Members
-------------------------------------------------------------------------------
local currentPlayerId = nil;

-------------------------------------------------------------------------------
-- Constants
-------------------------------------------------------------------------------
local NIL_VALUE = "<nil>";
local CURRENT_DATABASE_VERSION = 30002;

-------------------------------------------------------------------------------
-- Function Prototypes
-------------------------------------------------------------------------------
local loadDatabase;
local upgradeDatabase;
local upgradePurchases30000To30001;
local upgradePurchases30001To30002;
local upgradeSales30000To30002;
local clearDatabase;
local getPlayerName;
local getPlayerId;
local getCurrentPlayerId;
local copyItemRecords;
local stringFromBoolean;
local booleanFromString;
local stringFromNumber;
local numberFromString;
local nilSafeStringFromString;
local stringFromNilSafeString;
local debugPrint;

-------------------------------------------------------------------------------
-- Loads the BeanCounter database
-------------------------------------------------------------------------------
function loadDatabase(realm)
	-- Load or create the database.
	BeanCounterRealmDB = BeanCounterAccountDB[realm];
	if (BeanCounterRealmDB == nil) then
		-- Create a new database.
		BeanCounterRealmDB = {};
		BeanCounterAccountDB[realm] = BeanCounterRealmDB;
		clearDatabase();

		-- Import the old AHPurchases and AHSales tables if we are loading
		-- this realm's database.
		if (realm == GetRealmName() and (AHPurchases or AHSales)) then
			-- Import AHPurchases.
			if (AHPurchases) then
				upgradePurchases30000To30001(AHPurchases);
				upgradePurchases30001To30002(AHPurchases, BeanCounterRealmDB.players);
				copyItemRecords(AHPurchases.PendingBids, BeanCounterRealmDB.pendingBids);
				copyItemRecords(AHPurchases.CompletedBids, BeanCounterRealmDB.completedBids);
				copyItemRecords(AHPurchases.Purchases, BeanCounterRealmDB.purchases);
				AHPurchases = nil;
			end

			-- Import AHSales.
			if (AHSales) then
				upgradeSales30000To30002(AHSales, BeanCounterRealmDB.players);
				copyItemRecords(AHSales.PendingAuctions, BeanCounterRealmDB.pendingAuctions);
				copyItemRecords(AHSales.CompletedAuctions, BeanCounterRealmDB.completedAuctions);
				copyItemRecords(AHSales.Sales, BeanCounterRealmDB.sales);
				AHSales = nil;
			end
		end
	end
	
	-- Upgrade the database before returning.
	upgradeDatabase(BeanCounterRealmDB);

	-- Cache the current player id.
	currentPlayerId = getPlayerId(UnitName("player"), true);
end

-------------------------------------------------------------------------------
-- Upgrades the BeanCounter database.
-------------------------------------------------------------------------------
function upgradeDatabase(database)
end

-------------------------------------------------------------------------------
-- Upgrades the AHPurchases table from version 30000 to 30001. This upgrade
-- added a timeLeft field to the pending bids.
-------------------------------------------------------------------------------
function upgradePurchases30000To30001(purchases)
	if (purchases.version == 30000) then
		if (purchases.PendingBids) then
			for item in pairs(purchases.PendingBids) do
				local pendingBidsTable = purchases.PendingBids[item];
				for index in pairs(pendingBidsTable) do
					pendingBidsTable[index] = pendingBidsTable[index]..";1"
					debugPrint("Upgraded AHPurchases.PendingBids["..item.."]["..index.."] = "..pendingBidsTable[index]);
				end
			end
		end
		purchases.version = 30001;
		debugPrint("AHPurchases upgraded to 30001");
	end
end

-------------------------------------------------------------------------------
-- Upgrades the AHPurchases table from version 30001 to 30002. This upgrade
-- added a buyer id field to the tables.
-------------------------------------------------------------------------------
function upgradePurchases30001To30002(database, players)
	if (database.version == 30001) then
		-- Get or create a player id
		local playerId = nil;
		local playerName = UnitName("player");
		for id in pairs(players) do
			if (players[id] == playerName) then
				playerId = id;
				break;
			end
		end
		if (playerId == nil) then
			table.insert(players, playerName);
			playerId = #players;
		end
		
		-- Add the player id to each pending bid.
		if (database.PendingBids) then
			for item in pairs(database.PendingBids) do
				local pendingBidsTable = database.PendingBids[item];
				for index in pairs(pendingBidsTable) do
					pendingBidsTable[index] = pendingBidsTable[index]..";"..stringFromNumber(playerId);
					debugPrint("Upgraded pendingBids["..item.."]["..index.."] = "..pendingBidsTable[index]);
				end
			end
		end

		-- Add the player id to each completed bid.
		if (database.CompletedBids) then
			for item in pairs(database.CompletedBids) do
				local completedBidsTable = database.CompletedBids[item];
				for index in pairs(completedBidsTable) do
					completedBidsTable[index] = completedBidsTable[index]..";"..stringFromNumber(playerId);
					debugPrint("Upgraded completedBids["..item.."]["..index.."] = "..completedBidsTable[index]);
				end
			end
		end

		-- Add the player id to each purchase.
		if (database.Purchases) then
			for item in pairs(database.Purchases) do
				local purchasesTable = database.Purchases[item];
				for index in pairs(purchasesTable) do
					purchasesTable[index] = purchasesTable[index]..";"..stringFromNumber(playerId);
					debugPrint("Upgraded purchases["..item.."]["..index.."] = "..purchasesTable[index]);
				end
			end
		end

		database.version = 30002;
		debugPrint("AHPurchases upgraded to 30002");
	end
end

-------------------------------------------------------------------------------
-- Upgrades the AHSales table from version 30000 to 30002. This upgrade added
-- a seller id field to the tables.
-------------------------------------------------------------------------------
function upgradeSales30000To30002(database, players)
	if (database.version == 30000) then
		-- Get or create a player id
		local playerId = nil;
		local playerName = UnitName("player");
		for id in pairs(players) do
			if (players[id] == playerName) then
				playerId = id;
				break;
			end
		end
		if (playerId == nil) then
			table.insert(players, playerName);
			playerId = #players;
		end
		
		-- Add the player id to each pending auction.
		if (database.PendingAuctions) then
			for item in pairs(database.PendingAuctions) do
				local pendingAuctionsTable = database.PendingAuctions[item];
				for index in pairs(pendingAuctionsTable) do
					pendingAuctionsTable[index] = pendingAuctionsTable[index]..";"..stringFromNumber(playerId);
					debugPrint("Upgraded pendingAuctions["..item.."]["..index.."] = "..pendingAuctionsTable[index]);
				end
			end
		end

		-- Add the player id to each completed auction.
		if (database.CompletedAuctions) then
			for item in pairs(database.CompletedAuctions) do
				local completedAuctionsTable = database.CompletedAuctions[item];
				for index in pairs(completedAuctionsTable) do
					completedAuctionsTable[index] = completedAuctionsTable[index]..";"..stringFromNumber(playerId);
					debugPrint("Upgraded completedAuctions["..item.."]["..index.."] = "..completedAuctionsTable[index]);
				end
			end
		end

		-- Add the player id to each sale.
		if (database.Sales) then
			for item in pairs(database.Sales) do
				local salesTable = database.Sales[item];
				for index in pairs(salesTable) do
					salesTable[index] = salesTable[index]..";"..stringFromNumber(playerId);
					debugPrint("Upgraded sales["..item.."]["..index.."] = "..salesTable[index]);
				end
			end
		end
		
		database.version = 30002;
		debugPrint("AHSales upgraded to 30002");
	end
end

-------------------------------------------------------------------------------
-- Clears the database
-------------------------------------------------------------------------------
function clearDatabase()
	BeanCounterRealmDB.version = CURRENT_DATABASE_VERSION;
	BeanCounterRealmDB.players = {};
	BeanCounterRealmDB.pendingBids = {};
	BeanCounterRealmDB.completedBids = {};
	BeanCounterRealmDB.purchases = {};
	BeanCounterRealmDB.pendingAuctions = {};
	BeanCounterRealmDB.completedAuctions = {};
	BeanCounterRealmDB.sales = {};
	debugPrint("Reset Database");
end

-------------------------------------------------------------------------------
-- Gets a player's name
-------------------------------------------------------------------------------
function getPlayerName(id)
	if (id ~= nil and id <= #(BeanCounterRealmDB.players)) then
		return BeanCounterRealmDB.players[id];
	end
	return "Unknown Player";
end

-------------------------------------------------------------------------------
-- Gets a player's id
-------------------------------------------------------------------------------
function getPlayerId(name, create)
	if (name ~= nil) then
		for id in pairs(BeanCounterRealmDB.players) do
			if (BeanCounterRealmDB.players[id] == name) then
				return id;
			end
		end
		if (create) then
			debugPrint("Adding player "..name.." to the database");
			table.insert(BeanCounterRealmDB.players, name);
			return #(BeanCounterRealmDB.players);
		end
	end
	return nil;
end

-------------------------------------------------------------------------------
-- Gets the current player's id
-------------------------------------------------------------------------------
function getCurrentPlayerId()
	return currentPlayerId;
end

-------------------------------------------------------------------------------
-- Copies the item records from one table to another.
-------------------------------------------------------------------------------
function copyItemRecords(source, destination)
	if (source and destination) then
		for item in pairs(source) do
			-- Get the source item table.
			sourceItems = source[item];
			if (#sourceItems > 0) then
				-- Get or create the destination item table.
				destinationItems = destination[item];
				if (destinationItems == nil) then
					destinationItems = {};
					destination[item] = destinationItems;
				end

				-- Copy from the source to the destination table.			
				for index in pairs(sourceItems) do
					table.insert(destinationItems, sourceItems[index]);
				end
			end
		end
	end
end

-------------------------------------------------------------------------------
-- Converts boolean into a numeric string.
-------------------------------------------------------------------------------
function stringFromBoolean(boolean)
	if (boolean == nil) then
		return NIL_VALUE;
	elseif (boolean) then
		return "1";
	end
	return "0";
end

-------------------------------------------------------------------------------
-- Converts a numeric string into a boolean.
-------------------------------------------------------------------------------
function booleanFromString(string)
	if (string == NIL_VALUE) then
		return nil;
	elseif (string == "0") then
		return false;
	end
	return true;
end

-------------------------------------------------------------------------------
-- Converts number into a numeric string.
-------------------------------------------------------------------------------
function stringFromNumber(number)
	if (number == nil) then
		return NIL_VALUE;
	end
	return tostring(number);
end

-------------------------------------------------------------------------------
-- Converts numeric string into a number.
-------------------------------------------------------------------------------
function numberFromString(number)
	if (number == NIL_VALUE) then
		return nil;
	end
	return tonumber(number);
end

-------------------------------------------------------------------------------
-- Converts a string into a nil safe string (nil -> "<nil>")
-------------------------------------------------------------------------------
function nilSafeStringFromString(string)
	if (string == nil) then
		return NIL_VALUE;
	end
	return string;
end

-------------------------------------------------------------------------------
-- Converts a nil safe string into a string ("<nil>" -> nil)
-------------------------------------------------------------------------------
function stringFromNilSafeString(nilSafeString)
	if (nilSafeString == NIL_VALUE) then
		return nil;
	end
	return nilSafeString;
end

-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
function debugPrint(message)
	BeanCounter.DebugPrint("[BeanCounter.Database] "..message);
end

-------------------------------------------------------------------------------
-- Public API
-------------------------------------------------------------------------------
BeanCounter.Database = 
{
	Load = loadDatabase;
	Clear = clearDatabase;
	GetPlayerName = getPlayerName;
	GetPlayerId = getPlayerId;
	GetCurrentPlayerId = getCurrentPlayerId;
	StringFromBoolean = stringFromBoolean;
	BooleanFromString = booleanFromString;
	StringFromNumber = stringFromNumber;
	NumberFromString = numberFromString;
	NilSafeStringFromString = nilSafeStringFromString;
	StringFromNilSafeString = stringFromNilSafeString;
};
