// Created by Zunath

#include "inc_system_const"
#include "inc_mysql_tables"
#include "colors_inc"

///////////////
// CONSTANTS //
///////////////

// Absolute limit to the number of items a single bank table can hold.
// By default, this is 100. Not sure what the exact limit is, but I wouldn't
// try raising this too much or you'll get some lag when PCs open their banks.
const int BANK_HARD_ITEM_CAP = 100;

////////////////////
// BANK FUNCTIONS //
////////////////////

// Returns oPC's unique ID number.
int BANK_GetPCIDNumber(object oPC);

// Creates bank tables if they do not exist in the MySQL
// database. Call this on module load.
void BANK_CreateTables();

// Returns the max number of items oPC can store in bank
// sBank.
int BANK_GetPCMaxSize(object oPC, string sBank);

// Checks PC's bank list to see if their max item size is at least
// 10. If it is lower, then they are set to 10. This is done for
// each bank in the game.
// Call this function on module client enter
void BANK_UpdatePCBankList(object oPC);

// Removes all stored item data from bank table sTable.
// This is called when a PC dies, or when PC is adding/removing
// new items.
void BANK_RemovePCBankData(object oPC, string sTable);

// Cycles through bank placeable and stores items in the database.
// If PC's max item limit is reached, all items afterwards are returned
// to their inventory.
// If no items are in the container, nothing is stored
void BANK_UpdatePCBank(object oPC, object oBank);

// Call on the bank terminal's OnOpen event.
// Loads stored bank items and locks terminal so that other PCs cannot steal
// user's items.
void BANK_BankOnOpened();

// Call on the bank terminal's OnClosed event.
// Destroys all of the bank;s inventory items and unlocks it for the next PC's use.
void BANK_BankOnClosed();

// Sets oPC's item limit for sBank to iLimit.
// oPC must be a player object
// sBank must be a valid bank table name (in MySQL)
// iLimit must be the new item limit for oPC.
// NOTE: Do not set LOWER than PC's current item limit or they may lose items
// NOTE 2: If iLimit is set lower than 1, it will be set to 1. If it is set above the cap,
// it will be set to the cap. (Determined by BANK_HARD_ITEM_CAP constant above)
void BANK_SetPCBankLimit(object oPC, string sBank, int iLimit);

// Internal function - Creates the necessary code to make
// a new bank table. Do not call this on its own.
void BANK_CreateTable(string sTableName);

/////////////////////////////////////////////////////////////

int BANK_GetPCIDNumber(object oPC)
{
    object oDatabase = GetItemPossessedBy(oPC, PC_DATABASE);
    return GetLocalInt(oDatabase, PC_ID_NUMBER);
}

int BANK_GetPCMaxSize(object oPC, string sBank)
{
    object oDatabase = GetItemPossessedBy(oPC, PC_DATABASE);
    int iAmount = GetLocalInt(oDatabase, sBank);

    return iAmount;
}

void BANK_UpdatePCBankList(object oPC)
{
    object oDatabase = GetItemPossessedBy(oPC, PC_DATABASE);
    if(GetLocalInt(oDatabase, "BANK_INGLES_WAREHOUSE") < 5)
    {
        SetLocalInt(oDatabase, "BANK_INGLES_WAREHOUSE", 5);
    }
    if(GetLocalInt(oDatabase, "BANK_UBCS_CAMPSITE") < 5)
    {
        SetLocalInt(oDatabase, "BANK_UBCS_CAMPSITE", 5);
    }
}

void BANK_RemovePCBankData(object oPC, string sTable)
{
    int iID = BANK_GetPCIDNumber(oPC);
    string sSQL = "DELETE FROM " + sTable + " WHERE ID=" + IntToString(iID) + ";";

    SQLExecDirect(sSQL);
}

void BANK_UpdatePCBank(object oPC, object oBank)
{
    string sTable = GetTag(oBank);
    int iMaxItems = BANK_GetPCMaxSize(oPC, sTable);
    int iID = BANK_GetPCIDNumber(oPC);
    string sSQL = "INSERT INTO " + sTable + "(ID, Name, Account, CDKey) VALUES (" + IntToString(iID) + ", '" + SQLEncodeSpecialChars(GetName(oPC)) + "', '" + SQLEncodeSpecialChars(GetPCPlayerName(oPC)) + "', '" + GetPCPublicCDKey(oPC) + "')";
    int iCurItem = 1;
    int iReachedLimit = FALSE;

    // First remove existing data
    BANK_RemovePCBankData(oPC, sTable);

    // Cycle through bank inventory and store items
    object oItem = GetFirstItemInInventory(oBank);
    while(GetIsObjectValid(oItem))
    {
        // Haven't reached the limit yet.
        if(iMaxItems >= iCurItem)
        {
            // Create a new row using their ID, if this is the first item in the bank
            if(iCurItem == 1)
            {
                SQLExecDirect(sSQL);
            }

            sSQL = "UPDATE " + sTable + " SET Item" + IntToString(iCurItem) + "=%s WHERE ID=" + IntToString(iID) + ";";
            SetLocalString(GetModule(), "NWNX!ODBC!SETSCORCOSQL", sSQL);
            StoreCampaignObject ("NWNX", "-", oItem);
        }
        // Reached the limit - return the item
        else
        {
            AssignCommand(oBank, ActionGiveItem(oItem, oPC));
            iReachedLimit = TRUE;
        }

        // Add one to the tally and move to the next item in bank's inventory
        iCurItem = iCurItem + 1;
        oItem = GetNextItemInInventory(oBank);
    }
    if(iReachedLimit == TRUE)
    {
        SendMessageToPC(oPC, ColorTokenRed() + "You have reached your item limit for this item box. Excess items have been returned to you." + ColorTokenEnd());
    }
    else
    {
        SendMessageToPC(oPC, ColorTokenWhite() + "Item Limit: " + IntToString(iCurItem-1) + " / " + ColorTokenRed() + IntToString(iMaxItems));
    }
}

void BANK_BankOnOpened()
{
    object oBank = OBJECT_SELF;
    object oPC = GetLastOpenedBy();
    object oItem;
    string sBank = GetTag(oBank);
    string sSQL;
    int iCurItem = 1;
    int iMaxItems = BANK_GetPCMaxSize(oPC, sBank);
    int iID = BANK_GetPCIDNumber(oPC);

    // Locks bank so other PC's can't steal player's items
    SetLocked(oBank, TRUE);

    // Load all bank items into bank terminal's inventory
    while(iCurItem <= iMaxItems)
    {
        sSQL = "SELECT Item" + IntToString(iCurItem) + " FROM " + sBank +  " WHERE ID=" + IntToString(iID) +";";
        SetLocalString(GetModule(), "NWNX!ODBC!SETSCORCOSQL", sSQL);
        oItem = RetrieveCampaignObject ("NWNX", "-", GetLocation(oBank), oBank);
        // No need to keep checking the table - there are no more items being stored at the moment
        if(!GetIsObjectValid(oItem)){break;}

        // Prevent duplication of items in containers
        if(GetHasInventory(oItem))
        {
            object oCycle = GetFirstItemInInventory(oItem);
            while(GetIsObjectValid(oCycle))
            {
                DestroyObject(oCycle);
                oCycle = GetNextItemInInventory(oItem);
            }
        }

        iCurItem = iCurItem + 1;
    }

    // Inform player of how to quit banking
    SendMessageToPC(oPC, "To quit storing items, walk away from the item box.");
}

void BANK_BankOnClosed()
{
    object oBank = OBJECT_SELF;
    object oPC = GetLastClosedBy();

    object oInventory = GetFirstItemInInventory();
    while(GetIsObjectValid(oInventory))
    {
        DestroyObject(oInventory);
        oInventory = GetNextItemInInventory();
    }

    // Allow another PC to use it.
    SetLocked(oBank, FALSE);
}

void BANK_SetPCBankLimit(object oPC, string sBank, int iLimit)
{
    object oDatabase = GetItemPossessedBy(oPC, PC_DATABASE);
    // Prevent from going out of the 1-BANK_HARD_ITEM_CAP range
    if(iLimit < 1){iLimit = 1;}
    else if(iLimit > BANK_HARD_ITEM_CAP){iLimit = BANK_HARD_ITEM_CAP;}

    SetLocalInt(oDatabase, sBank, iLimit);
}

void BANK_CreateTable(string sTableName)
{
    // Table exists - cancel
    if(DoesMySQLTableExist(sTableName)){return;}
    int iLoop;

    string sSQL = "CREATE TABLE `" + sTableName + "` (" +
                "`ID` INTEGER  NULL," +
                "`Name` TEXT DEFAULT NULL," +
                "`Account` TEXT DEFAULT NULL," +
                "`CDKey` TEXT DEFAULT NULL," +
                "PRIMARY KEY (`ID`))" +
                "ENGINE = MyISAM;";

    SQLExecDirect(sSQL);

    sSQL = "ALTER TABLE `" + sTableName + "` ADD COLUMN `Item1` BLOB DEFAULT NULL AFTER `CDKey`;";
    SQLExecDirect(sSQL);

    // Now cycle through, creating more columns until the hard cap is reached.
    // Probably not the most efficient way, but a single create table call only got me
    // to 50 items.
    for(iLoop = 1; iLoop <= BANK_HARD_ITEM_CAP; iLoop++)
    {
        sSQL = "ALTER TABLE `" + sTableName + "` ADD COLUMN `Item" + IntToString(iLoop) +  "` BLOB DEFAULT NULL AFTER `Item" + IntToString(iLoop-1) + "`;";
        SQLExecDirect(sSQL);
    }
}

void BANK_CreateTables()
{
    // Add more tables as more banks are created
    BANK_CreateTable("BANK_INGLES_WAREHOUSE");
    BANK_CreateTable("BANK_UBCS_CAMPSITE");
}

// Error checking
//void main(){}