/* **********************************************************************************************************************
   Included Header Files
   **********************************************************************************************************************/

#include "VDNPC.h"



/* **********************************************************************************************************************
   Constructors
   **********************************************************************************************************************/

VDNPC::VDNPC() {
    status = 0;
    movementSpeed = 3;
    movementTimer = 0.0f;
};

VDNPC::VDNPC(LPCSTR aName) {
    status = 1;
    errno_t returnValue = strcpy_s(name, aName);
    if (returnValue != 0) {
        VDError("Failed to set the name of the NPC.", __FILE__, __LINE__);
    };
    movementSpeed = 3;
    movementTimer = 0.0f;
};



/* **********************************************************************************************************************
   Destructor
   **********************************************************************************************************************/

VDNPC::~VDNPC() {
};



/* **********************************************************************************************************************
   AddDialogue
   -----------
   Adds a new dialogue to the list.
   #param  id    The ID of the dialogue.
   #param  text  The text of the dialogue.
   #return  TRUE if a new one is added, FALSE if the ID already exists.
   **********************************************************************************************************************/

void VDNPC::AddDialogue(int id, LPCSTR text) {
    // look through all the dialogues
    for (unsigned int n = 0; n < dialogues.size(); n++) {
        // one with this ID exists, only change the text
        if (dialogues.at(n).GetID() == id) {
            dialogues.at(n).SetText(text);
            return;
        };
    };

    // add the new dialogue
    VDDialogue dialogue = VDDialogue(id, text);
    dialogues.push_back(dialogue);
};



/* **********************************************************************************************************************
   RemoveDialogue
   --------------
   Removes a dialogue from the list.
   #param  id  The ID of the dialogue.
   **********************************************************************************************************************/

void VDNPC::RemoveDialogue(int id) {
    for (unsigned int n = 0; n < dialogues.size(); n++) {
        if (dialogues.at(n).GetID() == id) {
            dialogues.erase(dialogues.begin() + n);
        };
    };
};



/* **********************************************************************************************************************
   AddOption
   ---------
   Tells the dialogue to add a new option to its list.
   #param  dialogueID  The ID of the dialogue.
   #param  text        The string to add.
   #param  nextID      ID of the next dialogue.
   **********************************************************************************************************************/

void VDNPC::AddOption(int dialogueID, LPCSTR text, int nextID) {
    for (unsigned int n = 0; n < dialogues.size(); n++) {
        if (dialogues.at(n).GetID() == dialogueID) {
            dialogues.at(n).AddOption(text, nextID);
        };
    };
};



/* **********************************************************************************************************************
   RemoveOption
   ------------
   Tells the dialogue to remove an option from its list.
   #param  dialogueID  The ID of the dialogue.
   #param  optionName  The string to remove.
   **********************************************************************************************************************/

void VDNPC::RemoveOption(int dialogueID, LPCSTR optionName) {
    for (unsigned int n = 0; n < dialogues.size(); n++) {
        if (dialogues.at(n).GetID() == dialogueID) {
            dialogues.at(n).RemoveOption(optionName);
        };
    };
};



int VDNPC::RunLogic(double currentTime) {
    if (movementTimer == 0) {
        movementTimer = currentTime;
    };

    // 3 seconds have past since last movement
    if (currentTime - movementTimer >= movementSpeed) {
        if (status == 1) {
            int direction = 0;

            // reset the movement timer
            movementTimer = 0;

            // randomize movement
            direction = rand() % 5;

            // return the random direction
            return direction;
        };
    };

    return 0;
};



/* **********************************************************************************************************************
   WriteNPC
   --------
   Saves the NPC to hard disk.
   #param  anOpenFile  Handle to a file that's already open.
   **********************************************************************************************************************/

void VDNPC::WriteNPC(HANDLE anOpenFile) {
    BOOL returnValue = FALSE;
    DWORD numberOfBytesWritten = 0;
    size_t numberOfDialogues = 0;

    // write the name
    returnValue = WriteFile(anOpenFile, &name, sizeof(name), &numberOfBytesWritten, NULL);
    if (returnValue == FALSE) {
        VDError("Failed to write NPC data to file.", __FILE__, __LINE__);
    };

    // write the x location
    returnValue = WriteFile(anOpenFile, &currentLocationX, sizeof(currentLocationX), &numberOfBytesWritten, NULL);
    if (returnValue == FALSE) {
        VDError("Failed to write NPC data to file.", __FILE__, __LINE__);
    };

    // write the y location
    returnValue = WriteFile(anOpenFile, &currentLocationY, sizeof(currentLocationY), &numberOfBytesWritten, NULL);
    if (returnValue == FALSE) {
        VDError("Failed to write NPC data to file.", __FILE__, __LINE__);
    };

    // write the image file location
    returnValue = WriteFile(anOpenFile, &imageFileLocation, sizeof(imageFileLocation), &numberOfBytesWritten, NULL);
    if (returnValue == FALSE) {
        VDError("Failed to write NPC data to file.", __FILE__, __LINE__);
    };

    // write the status
    returnValue = WriteFile(anOpenFile, &status, sizeof(status), &numberOfBytesWritten, NULL);
    if (returnValue == FALSE) {
        VDError("Failed to write NPC data to file.", __FILE__, __LINE__);
    };

    // write the direction
    returnValue = WriteFile(anOpenFile, &direction, sizeof(direction), &numberOfBytesWritten, NULL);
    if (returnValue == FALSE) {
        VDError("Failed to write NPC data to file.", __FILE__, __LINE__);
    };

    // write the color key
    returnValue = WriteFile(anOpenFile, &colorKey, sizeof(colorKey), &numberOfBytesWritten, NULL);
    if (returnValue == FALSE) {
        VDError("Failed to write NPC data to file.", __FILE__, __LINE__);
    };

    // make sure we let it know how many dialogue elements we're adding
    numberOfDialogues = dialogues.size();
    returnValue = WriteFile(anOpenFile, &numberOfDialogues, sizeof(numberOfDialogues), &numberOfBytesWritten, NULL);
    if (returnValue == FALSE) {
        VDError("Failed to write NPC data to file.", __FILE__, __LINE__);
    };

    // start writing all the dialogues
    for (unsigned int n = 0; n < numberOfDialogues; n++) {
        dialogues.at(n).WriteDialogue(anOpenFile);
    };
};



/* **********************************************************************************************************************
   LoadNPC
   -------
   Loads the NPC from hard disk.
   #param  anOpenFile  Handle to a file that's already open.
   **********************************************************************************************************************/

void VDNPC::LoadNPC(HANDLE anOpenFile) {
    BOOL returnValue = FALSE;
    DWORD numberOfBytesRead = 0;
    size_t numberOfDialogues = 0;

    // load the name
    returnValue = ReadFile(anOpenFile, &name, sizeof(name), &numberOfBytesRead, NULL);
    if (returnValue == FALSE) {
        VDError("Failed to read NPC data.", __FILE__, __LINE__);
    };

    // load the x location
    returnValue = ReadFile(anOpenFile, &currentLocationX, sizeof(currentLocationX), &numberOfBytesRead, NULL);
    if (returnValue == FALSE) {
        VDError("Failed to read NPC data.", __FILE__, __LINE__);
    };

    // load the y location
    returnValue = ReadFile(anOpenFile, &currentLocationY, sizeof(currentLocationY), &numberOfBytesRead, NULL);
    if (returnValue == FALSE) {
        VDError("Failed to read NPC data.", __FILE__, __LINE__);
    };

    // load the image file location
    returnValue = ReadFile(anOpenFile, &imageFileLocation, sizeof(imageFileLocation), &numberOfBytesRead, NULL);
    if (returnValue == FALSE) {
        VDError("Failed to read NPC data.", __FILE__, __LINE__);
    };

    // load the status
    returnValue = ReadFile(anOpenFile, &status, sizeof(status), &numberOfBytesRead, NULL);
    if (returnValue == FALSE) {
        VDError("Failed to read NPC data.", __FILE__, __LINE__);
    };

    // load the direction
    returnValue = ReadFile(anOpenFile, &direction, sizeof(direction), &numberOfBytesRead, NULL);
    if (returnValue == FALSE) {
        VDError("Failed to read NPC data.", __FILE__, __LINE__);
    };

    // load the color key
    returnValue = ReadFile(anOpenFile, &colorKey, sizeof(colorKey), &numberOfBytesRead, NULL);
    if (returnValue == FALSE) {
        VDError("Failed to read NPC data.", __FILE__, __LINE__);
    };

    // load how many dialogue elements we're adding
    returnValue = ReadFile(anOpenFile, &numberOfDialogues, sizeof(numberOfDialogues), &numberOfBytesRead, NULL);
    if (returnValue == FALSE) {
        VDError("Failed to read NPC data.", __FILE__, __LINE__);
    };

    // load all the dialogues
    for (size_t n = 0; n < numberOfDialogues; n++) {
        VDDialogue newDialogue;
        newDialogue.ReadDialogue(anOpenFile);
        dialogues.push_back(newDialogue);
    };
};



/* **********************************************************************************************************************
   Accessors and Modifiers
   **********************************************************************************************************************/

int VDNPC::GetStatus() {
    return status;
};

void VDNPC::SetStatus(int aStatus) {
    status = aStatus;
};

size_t VDNPC::GetDialoguesSize() {
    return dialogues.size();
};

LPCSTR VDNPC::GetDialogueText(unsigned int index) {
    return dialogues.at(index).GetText();
};

int VDNPC::GetDialogueID(unsigned int index) {
    return dialogues.at(index).GetID();
};

size_t VDNPC::GetOptionsSize(int dialogueID) {
    for (unsigned int n = 0; n < dialogues.size(); n++) {
        if (dialogues.at(n).GetID() == dialogueID) {
            return dialogues.at(n).GetOptionsSize();
        };
    };

    return NULL;
};

LPCSTR VDNPC::GetOption(int dialogueID, unsigned int optionIndex) {
    for (unsigned int n = 0; n < dialogues.size(); n++) {
        if (dialogues.at(n).GetID() == dialogueID) {
            return dialogues.at(n).GetOption(optionIndex);
        };
    };

    return NULL;
};

int VDNPC::GetOptionNextID(int dialogueID, unsigned int optionIndex) {
    for (unsigned int n = 0; n < dialogues.size(); n++) {
        if (dialogues.at(n).GetID() == dialogueID) {
            return dialogues.at(n).GetOptionNextID(optionIndex);
        };
    };

    return NULL;
};