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

// Name of the array which holds key items.
const string KEYITEM_ARRAY = "KEYITEM_ARRAY";

// Name of the variable stored on the module which holds number of IDs
const string KEYITEM_NUMBER_OF_IDS = "KEYITEM_NUMBER_OF_IDS";

/////////////////////////////////////
// CATEGORY IDENTIFICATION NUMBERS //
/////////////////////////////////////

const int KEYITEM_CATEGORY_MAPS = 1;
const int KEYITEM_CATEGORY_QUEST = 2;
const int KEYITEM_CATEGORY_NOTES = 3;


// Adds a key item to oPC's list of key items.
// If successful, return value will be TRUE.
// If the PC already has a key item, return value will be FALSE.
// oPC = The player object
// iKeyItemID = The particular key item ID number listed in the database
// bDisplayMessage = If TRUE, this will display a message to the PC
int KEYITEM_AddKeyItem(object oPC, int iKeyItemID, int bDisplayMessage = TRUE);
// Returns TRUE if oPC has a particular key item.
// Returns FALSE if oPC does not have the key item.
// oPC = The player object
// iKeyItemID = The particular key item ID number listed in the database
int KEYITEM_GetKeyItem(object oPC, int iKeyItemID);
// Removes a key item from oPC's list of key items.
// If successful, return value will be TRUE
// If the PC doesn't have this key item, return value will be FALSE.
// oPC = The player object
// iKeyItemID = The particular key item ID number listed in the database
// bDisplayMessage = If TRUE, this will display a message to the PC
int KEYITEM_RemoveKeyItem(object oPC, int iKeyItemID, int bDisplayMessage = TRUE);
// Returns a description for a particular key item.
// If a TLK number is referenced in the database, the description
// will be pulled from the reo_tlk_# file (where # is the version number of the TLK file)
// Otherwise the description will be pulled directly from the field.
// It's suggested that descriptions longer than 8 characters be put into the TLK to save on bandwidth.
// iKeyItemID = If this is set to -1, the description will be found by using the key item's name.
//              Otherwise it'll use the Key Item's ID to find the description.
// sName = If this is set to "", the description will be found by using the key item's ID number
//         Otherwise it'll use the Key Item's name to find the description.
string KEYITEM_GetDescription(int iKeyItemID, string sName = "");
// Call this on the module's OnLoad event.
// It will store the maximum ID number of key items as a variable on the module.
// This saves a lot of database querying for the same value.
// This number is what determines how many zeroes players' key item arrays need
void KEYITEM_OnModuleLoad();
// Call this on the module's OnAcquired event.
// It will convert physical items that are acquired into key items. This is especially
// useful for the search system.
void KEYITEM_OnModuleAcquireItem();
// Called from a the Actions Taken scripts named "key_item_at_buy#"
// Place the following variables on a key item merchant:
//
//      KEY_ITEM_MERCHANT_ID_#, where # is the key item number (1-9) and the value is the key item ID number in the database
//      KEY_ITEM_MERCHANT_PRICE_#, where # is the key item number (1-9) and the value is the price of the key item
void KEYITEM_PurchaseKeyItem(int iNPCKeyItemID);


int KEYITEM_AddKeyItem(object oPC, int iKeyItemID, int bDisplayMessage = TRUE)
{
    // Already have this key item - return FALSE
    if(KEYITEM_GetKeyItem(oPC, iKeyItemID)) return FALSE;
    else
    {
        object oDatabase = GetItemPossessedBy(oPC, PC_DATABASE);
        string sKeyItemName = GetMySQLData(KEYITEM_MYSQL_TABLE, "Name", iKeyItemID);
        string sArray = GetLocalString(oDatabase, KEYITEM_ARRAY);
        int iLength = GetStringLength(sArray);
        string sLeft = GetStringLeft(sArray, iKeyItemID-1);
        string sRight = GetStringRight(sArray, iLength-iKeyItemID);

        // Modify array and update it
        sArray = sLeft + "1" + sRight;
        SetLocalString(oDatabase, KEYITEM_ARRAY, sArray);

        // Inform PC that they now have a new key item
        if(bDisplayMessage)
            SendMessageToPC(oPC, ColorTokenGreen() + "You acquired key item: " + sKeyItemName + "!" + ColorTokenEnd());

        return TRUE;
    }
}

int KEYITEM_GetKeyItem(object oPC, int iKeyItemID)
{
    object oModule = GetModule();
    object oDatabase = GetItemPossessedBy(oPC, PC_DATABASE);
    string sArray = GetLocalString(oDatabase, KEYITEM_ARRAY);
    int iLength = GetStringLength(sArray);
    int iNumberIDs = GetLocalInt(oModule, KEYITEM_NUMBER_OF_IDS);

    // Add zeroes to the string array if needed.
    int iCurrentSlot;
    for(iCurrentSlot = iLength; iCurrentSlot < iNumberIDs; iCurrentSlot++)
    {
        sArray += "0";
    }

    // Update the array (in case zeroes were added)
    SetLocalString(oDatabase, KEYITEM_ARRAY, sArray);

    // Return TRUE if the PC has that key item
    if(GetSubString(sArray, iKeyItemID-1, 1) == "1") return TRUE;
    else return FALSE;

}

int KEYITEM_RemoveKeyItem(object oPC, int iKeyItemID, int bDisplayMessage = TRUE)
{
    // Doesn't have this key item - return FALSE
    if(!KEYITEM_GetKeyItem(oPC, iKeyItemID)) return FALSE;
    else
    {
        object oDatabase = GetItemPossessedBy(oPC, PC_DATABASE);
        string sKeyItemName = GetMySQLData(KEYITEM_MYSQL_TABLE, "Name", iKeyItemID);
        string sArray = GetLocalString(oDatabase, KEYITEM_ARRAY);
        int iLength = GetStringLength(sArray);
        string sLeft = GetStringLeft(sArray, iKeyItemID-1);
        string sRight = GetStringRight(sArray, iLength-iKeyItemID);

        // Modify array and update it
        sArray = sLeft + "0" + sRight;
        SetLocalString(oDatabase, KEYITEM_ARRAY, sArray);

        // Inform PC that they lost a key item
        if(bDisplayMessage)
            SendMessageToPC(oPC, ColorTokenRed() + "You lost key item: " + sKeyItemName + "!" + ColorTokenEnd());
        //SendMessageToPC(oPC, "DEBUG: " + sArray); // DEBUG

        return TRUE;
    }
}

string KEYITEM_GetDescription(int iKeyItemID, string sName = "")
{
    string sDescription;
    if(sName == "")
    {
        sDescription = GetMySQLData(KEYITEM_MYSQL_TABLE, "Description", iKeyItemID);
    }
    else
    {
        sDescription = GetMySQLDataKeyString(KEYITEM_MYSQL_TABLE, "Description", sName, "Name");
    }

    int iDescription = StringToInt(sDescription);

    // Pull from the TLK file
    if(iDescription > 0)
    {
        return GetStringByStrRef(iDescription);
    }
    // Pull from the database
    else
    {
        return sDescription;
    }
}

void KEYITEM_OnModuleLoad()
{
    object oModule = OBJECT_SELF;
    string sSQL = "SELECT MAX(ID) FROM `" + KEYITEM_MYSQL_TABLE + "`;";
    SQLExecDirect(sSQL);

    if(SQLFetch() == SQL_SUCCESS)
    {
        SetLocalInt(oModule, KEYITEM_NUMBER_OF_IDS, StringToInt(SQLGetData(1)));
    }
}

void KEYITEM_OnModuleAcquireItem()
{
    object oModule = OBJECT_SELF;
    object oPC = GetModuleItemAcquiredBy();
    object oItem = GetModuleItemAcquired();
    object oContainer = GetModuleItemAcquiredFrom();
    string sTag = GetTag(oItem);

    // Item acquired was a key item - convert it to an actual key item
    // and destroy the physical item
    if(GetStringLeft(sTag, 8) == "KEYITEM_")
    {
        // Key item was acquired from a search container. Check for next state in the quest
        // and move the PC onto it.
        if(GetIsObjectValid(oContainer) && GetResRef(oContainer) == "srch_container")
        {
            int iQuestID = GetLocalInt(oItem, "QST_ID_NUMBER");
            int iQuestCurrentState = GetLocalInt(oItem, "QST_CURRENT_STATE");
            int iQuestNextState = GetLocalInt(oItem, "QST_NEXT_STATE");
            string sJournal = GetMySQLData(QST_TABLE_NAME, "Journal", iQuestID, "ID");
            int iPCState = RetrieveQuestState(sJournal, oPC);

            // PC must be on the current step of the quest
            if(iPCState == iQuestCurrentState)
            {
                AddPersistentJournalQuestEntry(sJournal, iQuestNextState, oPC, FALSE, FALSE, FALSE);
            }
        }
        DestroyObject(oItem);
        KEYITEM_AddKeyItem(oPC, GetLocalInt(oItem, "KEYITEM_ID"), TRUE);
    }
}

void KEYITEM_PurchaseKeyItem(int iNPCKeyItemID)
{
    object oPC = GetPCSpeaker();

    int iKeyItemID = GetLocalInt(OBJECT_SELF, "KEY_ITEM_MERCHANT_ID_" + IntToString(iNPCKeyItemID));
    int iPrice = GetLocalInt(OBJECT_SELF, "KEY_ITEM_MERCHANT_PRICE_" + IntToString(iNPCKeyItemID));
    int iGold = GetGold(oPC);

    // Already have this key item
    if(KEYITEM_GetKeyItem(oPC, iKeyItemID))
    {
        FloatingTextStringOnCreature(ColorTokenRed() + "You already have this key item." + ColorTokenEnd(), oPC, FALSE);
        return;
    }

    // Not enough money
    if(iPrice > iGold)
    {
        FloatingTextStringOnCreature(ColorTokenRed() + "You do not have enough money to purchase that key item." + ColorTokenEnd(), oPC, FALSE);
        return;
    }

    // Take the gold and give the key item
    TakeGoldFromCreature(iPrice, oPC, TRUE);
    KEYITEM_AddKeyItem(oPC, iKeyItemID, TRUE);
}

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