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

#include "VDDirect3D.h"



/* **********************************************************************************************************************
   Constructor
   **********************************************************************************************************************/

VDDirect3D::VDDirect3D() {
    direct3D = NULL;
    direct3DDevice = NULL;
    (void) SecureZeroMemory(&presentationParameters, sizeof(presentationParameters));
    sprite = NULL;
    tileSprites.clear();
    npcSprites.clear();
    playerSprite = NULL;
    font = NULL;
};



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

VDDirect3D::~VDDirect3D() {
    FreeMemory();
};



/* **********************************************************************************************************************
   FreeMemory
   ----------
   Releases all Direct3D COM objects.
   **********************************************************************************************************************/

void VDDirect3D::FreeMemory() {
    if (font != NULL) {
        (void) font->Release();
        font = NULL;
    };
    if (playerSprite != NULL) {
        playerSprite->FreeTexture();
        delete playerSprite;
        playerSprite = NULL;
    };
    if (sprite != NULL) {
        (void) sprite->Release();
        sprite = NULL;
    };
    if (direct3DDevice != NULL) {
        (void) direct3DDevice->Release();
        direct3DDevice = NULL;
    };
    if (direct3D != NULL) {
        (void) direct3D->Release();
        direct3D = NULL;
    };
};



/* **********************************************************************************************************************
   InitializeDirect3D
   ------------------
   Initializes and creates the Direct3D device.
   #param  window  Handle to the window.
   **********************************************************************************************************************/

void VDDirect3D::InitializeDirect3D(HWND window) {
    HRESULT returnValue = NULL;

    // create an IDirect3D9 object
    direct3D = Direct3DCreate9(D3D_SDK_VERSION);
    if (direct3D == NULL) {
        VDError("Failed to create Direct3D.", __FILE__, __LINE__);
    };

    // TODO: for full screen, use CheckDeviceType, GetDisplayMode is after the device is created, and EnumAdapterModes
    //       to find out the available options for full screen modes (16 bit, 32 bit, resolutions, etc.)

    // set up the presentation parameters
    presentationParameters.AutoDepthStencilFormat      = D3DFMT_UNKNOWN;          // format of automatic depth-stencil surface (ignored)
    presentationParameters.BackBufferCount             = 0;                       // number of back buffers
    presentationParameters.BackBufferFormat            = D3DFMT_UNKNOWN;          // back buffer format
    presentationParameters.BackBufferHeight            = 0;                       // height of swap chain's back buffer(s)
    presentationParameters.BackBufferWidth             = 0;                       // width of swap chain's back buffer(s)
    presentationParameters.EnableAutoDepthStencil      = FALSE;                   // Direct3D manages depth buffers
    //presentationParameters.Flags;                                                 // various flag types
    //presentationParameters.FullScreen_RefreshRateInHz;
    presentationParameters.hDeviceWindow               = window;
    //presentationParameters.MultiSampleQuality;
    //presentationParameters.MultiSampleType;
    //presentationParameters.PresentationInterval;
    presentationParameters.SwapEffect                  = D3DSWAPEFFECT_DISCARD;
    presentationParameters.Windowed                    = TRUE;

    // check which back buffer/display format is compatible
    D3DFORMAT format = D3DFMT_UNKNOWN;
    returnValue = direct3D->CheckDeviceType(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, D3DFMT_X8R8G8B8, TRUE);
    if (SUCCEEDED(returnValue)) {
        format = D3DFMT_X8R8G8B8;
    } else {
        returnValue = direct3D->CheckDeviceType(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, D3DFMT_X1R5G5B5, D3DFMT_X1R5G5B5, TRUE);
        if (SUCCEEDED(returnValue)) {
            format = D3DFMT_X1R5G5B5;
        } else {
            returnValue = direct3D->CheckDeviceType(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, D3DFMT_R5G6B5, D3DFMT_R5G6B5, TRUE);
            if (SUCCEEDED(returnValue)) {
                format = D3DFMT_R5G6B5;
            };
        };
    };
    presentationParameters.BackBufferFormat = format;

    // check hardware capabilities
    D3DCAPS9 deviceCapabilities;
    returnValue = direct3D->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &deviceCapabilities);
    if (FAILED(returnValue)) {
        FreeMemory();
        VDDirectXError("Failed to get the device capabilities.", returnValue, __FILE__, __LINE__);
    };
    if (deviceCapabilities.VertexProcessingCaps == 0) {
        if ((deviceCapabilities.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) == FALSE) {
            MessageBox(NULL, "Hardware vertex processing is not supported by the graphics card.", NULL, MB_OK);
        };
    };
    UINT adapterModeCount = direct3D->GetAdapterModeCount(D3DADAPTER_DEFAULT, format);
    D3DDISPLAYMODE displayMode = { 0 };
    for (UINT n = 0; n < adapterModeCount; n++) {
        returnValue = direct3D->EnumAdapterModes(D3DADAPTER_DEFAULT, format, n, &displayMode);
        if (FAILED(returnValue)) {
            MessageBox(NULL, "The display adapter does not support the Acceptable Hardware Format.", NULL, MB_OK);
        };
    };
    returnValue = direct3D->CheckDeviceType(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, format, format, TRUE);
    if (FAILED(returnValue)) {
        MessageBox(NULL, "The display adapter will not accept the Acceptable Hardware Format", NULL, MB_OK);
        returnValue = direct3D->CheckDeviceType(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, format, format, TRUE);
        if (FAILED(returnValue)) {
            MessageBox(NULL, "The software won't enough run this shit.  Whooaaa buddy.", NULL, MB_OK);
        };
    };

    // create a device to represent the display adapter 
    returnValue = direct3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window, D3DCREATE_HARDWARE_VERTEXPROCESSING, &presentationParameters, &direct3DDevice);
    if (FAILED(returnValue)) {
        returnValue = direct3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, window, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &presentationParameters, &direct3DDevice);
        if (FAILED(returnValue)) {
            presentationParameters.BackBufferFormat = D3DFMT_UNKNOWN;
            returnValue = direct3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, window, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &presentationParameters, &direct3DDevice);
            if (FAILED(returnValue)) {
                FreeMemory();
                VDDirectXError("Failed to create the display adapter device.", returnValue, __FILE__, __LINE__);
            };
        };
    };

    // create a sprite object
    returnValue = D3DXCreateSprite(direct3DDevice, &sprite);
    if (returnValue != D3D_OK) {
        FreeMemory();
        VDDirectXError("Failed to create the sprite object.", returnValue, __FILE__, __LINE__);
    };

    // create a font object
    returnValue = D3DXCreateFont(direct3DDevice, 16, 0, FW_NORMAL, 0, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, ANTIALIASED_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Arial", &font);
    if (returnValue != D3D_OK) {
        FreeMemory();
        VDDirectXError("Failed to create the font object.", returnValue, __FILE__, __LINE__);
    };
};



/* **********************************************************************************************************************
   Render
   ------
   Renders the graphics to the screen.
   #param  area           A rectangle representing the area to draw.
   #param  numUpdates     The number of updates of the game done before the call to render in one cycle.
   #param  windowWidth    Width of the window (used to offset the player in the main game, set to 0 for engine)
   #param  windowHeight   Height of the window (used to offset the player in the main game, set to 0 for engine)
   #param  userLocationX  The x location of the user.
   #param  userLocationY  The y location of the user.
   **********************************************************************************************************************/

void VDDirect3D::Render(const RECT *area, int numUpdates, int windowWidth, int windowHeight, int userLocationX, int userLocationY) {
    HRESULT returnValue = NULL;

    // clear the screen
    returnValue = direct3DDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
    if (returnValue != D3D_OK) {
        FreeMemory();
        VDDirectXError("Failed to clear render target surface.", returnValue, __FILE__, __LINE__);
    };

    // begin drawing
    returnValue = direct3DDevice->BeginScene();
    if (returnValue != D3D_OK) {
        FreeMemory();
        VDDirectXError("Failed to start the drawing scene.", returnValue, __FILE__, __LINE__);
    };

        // are we in the process of moving?
        if (playerSprite->IsMoving() == TRUE) {
            int direction = playerSprite->GetDirection();
            int numberOfPixelsToShiftSprites = playerSprite->GetMovingDuration();

            // make sure the movement doesn't exceed 32 pixels
            int playerMovementIncrement = numUpdates;
            while (playerMovementIncrement + playerSprite->GetMovingDuration() > 32) {
                playerMovementIncrement--;
            };

            // traverse through the list of tiles
            for (unsigned int n = 0; n < tileSprites.size(); n++) {
                // begin drawing sprites
                returnValue = sprite->Begin(D3DXSPRITE_ALPHABLEND);
                if (returnValue != S_OK) {
                    FreeMemory();
                    VDDirectXError("Failed to prepare device for drawing sprites.", returnValue, __FILE__, __LINE__);
                };

                    // background sprites
                    if (tileSprites.at(n).IsStatic() == FALSE && tileSprites.at(n).IsNPC() == FALSE) {
                        // translate the sprites slowly
                        switch (direction) {
                            case 1:
                                tileSprites.at(n).ShiftLocation(direct3DDevice,
                                                                (float) (windowWidth / 2) - userLocationX * 32.0f,
                                                                (float) (windowHeight / 2) - (userLocationY + 1) * 32.0f + (float) numberOfPixelsToShiftSprites);
                                break;
                            case 2:
                                tileSprites.at(n).ShiftLocation(direct3DDevice,
                                                                (float) (windowWidth / 2) - userLocationX * 32.0f,
                                                                (float) (windowHeight / 2) - (userLocationY - 1) * 32.0f - (float) numberOfPixelsToShiftSprites);
                                break;
                            case 3:
                                tileSprites.at(n).ShiftLocation(direct3DDevice,
                                                                (float) (windowWidth / 2) - (userLocationX + 1) * 32.0f + (float) numberOfPixelsToShiftSprites,
                                                                (float) (windowHeight / 2) - userLocationY * 32.0f);
                                break;
                            case 4:
                                tileSprites.at(n).ShiftLocation(direct3DDevice,
                                                                (float) (windowWidth / 2) - (userLocationX - 1) * 32.0f - (float) numberOfPixelsToShiftSprites,
                                                                (float) (windowHeight / 2) - userLocationY * 32.0f);
                                break;
                            default:
                                break;
                        };

                        // draw the sprites
                        tileSprites.at(n).Draw(sprite);
                    };

                // end drawing sprites
                returnValue = sprite->End();
                if (returnValue != S_OK) {
                    FreeMemory();
                    VDDirectXError("Failed to end the drawing scene.", returnValue, __FILE__, __LINE__);
                };
            };

            // traverse through the list of NPCs
            for (unsigned int n = 0; n < npcSprites.size(); n++) {
                // begin drawing sprites
                returnValue = sprite->Begin(D3DXSPRITE_ALPHABLEND);
                if (returnValue != S_OK) {
                    FreeMemory();
                    VDDirectXError("Failed to prepare device for drawing sprites.", returnValue, __FILE__, __LINE__);
                };
                    // translate the NPC sprite slowly
                    switch (direction) {
                        case 1:
                            npcSprites.at(n).ShiftLocation(direct3DDevice,
                                                           (float) (windowWidth / 2) - userLocationX * 32.0f,
                                                           (float) (windowHeight / 2) - (userLocationY + 1) * 32.0f + (float) numberOfPixelsToShiftSprites);
                            break;
                        case 2:
                            npcSprites.at(n).ShiftLocation(direct3DDevice,
                                                           (float) (windowWidth / 2) - userLocationX * 32.0f,
                                                           (float) (windowHeight / 2) - (userLocationY - 1) * 32.0f - (float) numberOfPixelsToShiftSprites);
                            break;
                        case 3:
                            npcSprites.at(n).ShiftLocation(direct3DDevice,
                                                           (float) (windowWidth / 2) - (userLocationX + 1) * 32.0f + (float) numberOfPixelsToShiftSprites,
                                                           (float) (windowHeight / 2) - userLocationY * 32.0f);
                            break;
                        case 4:
                            npcSprites.at(n).ShiftLocation(direct3DDevice,
                                                           (float) (windowWidth / 2) - (userLocationX - 1) * 32.0f - (float) numberOfPixelsToShiftSprites,
                                                           (float) (windowHeight / 2) - userLocationY * 32.0f);
                            break;
                        default:
                            break;
                    };

                    // is the NPC in the process of moving?
                    if (npcSprites.at(n).IsMoving() == TRUE /*&& npcSprites.at(n).GetMovingDuration() < 32 TODO*/) {
                        // make sure the movement doesn't exceed 32 pixels
                        int npcMovementIncrement = numUpdates;
                        while (npcMovementIncrement + npcSprites.at(n).GetMovingDuration() > 32) {
                            npcMovementIncrement--;
                        };

                        // TODO: change to animation
                        switch (npcSprites.at(n).GetDirection()) {
                            case 1:
                                npcSprites.at(n).AddToPosition(0.0f, (float) 0.0f - npcMovementIncrement);
                                break;
                            case 2:
                                npcSprites.at(n).AddToPosition(0.0f, (float) npcMovementIncrement);
                                break;
                            case 3:
                                npcSprites.at(n).AddToPosition((float) 0.0f - npcMovementIncrement, 0.0f);
                                break;
                            case 4:
                                npcSprites.at(n).AddToPosition((float) npcMovementIncrement, 0.0f);
                                break;
                            default:
                                break;
                        };

                        // increment the NPC moving process based on the game update cycle
                        npcSprites.at(n).AddToMovingDuration(npcMovementIncrement);

                        // movement spanned 32 pixels, stop moving
                        if (npcSprites.at(n).GetMovingDuration() == 32) {
                            npcSprites.at(n).SetIsMoving(FALSE);
                            npcSprites.at(n).SetMovingDuration(0);
                        };
                    };

                    // draw the NPC
                    npcSprites.at(n).Draw(sprite);

                // end drawing sprites
                returnValue = sprite->End();
                if (returnValue != S_OK) {
                    FreeMemory();
                    VDDirectXError("Failed to end the drawing scene.", returnValue, __FILE__, __LINE__);
                };
            };

            // begin drawing sprites
            returnValue = sprite->Begin(D3DXSPRITE_ALPHABLEND);
            if (returnValue != S_OK) {
                FreeMemory();
                VDDirectXError("Failed to prepare device for drawing sprites.", returnValue, __FILE__, __LINE__);
            };

                // get the current sourceRect position
                RECT sourceRect;
                (void) playerSprite->GetSourceRectangle(&sourceRect);
                int sourceRectLoc = (numberOfPixelsToShiftSprites - (numberOfPixelsToShiftSprites % 16)) / 16;

                // first calculate where exactly the sprite walking image should be (depends on the image)
                if (sourceRectLoc == 0) {
                    sourceRectLoc = 0;
                } else if (sourceRectLoc == 1) {
                    sourceRectLoc = 2;
                };

                // change the sprite according to the direction
                switch (direction) {
                    case 1:
                        sourceRect.left = sourceRectLoc * 32;
                        sourceRect.right = 32 + sourceRectLoc * 32;
                        break;
                    case 2:
                        sourceRect.left = (32 * 3) + sourceRectLoc * 32;
                        sourceRect.right = (32 * 4) + sourceRectLoc * 32;
                        break;
                    case 3:
                        sourceRect.left = (32 * 6) + sourceRectLoc * 32;
                        sourceRect.right = (32 * 7) + sourceRectLoc * 32;
                        break;
                    case 4:
                        sourceRect.left = (32 * 9) + sourceRectLoc * 32;
                        sourceRect.right = (32 * 10) + sourceRectLoc * 32;
                        break;
                    default:
                        break;
                };
                playerSprite->SetSourceRectangle(sourceRect.left, sourceRect.top, sourceRect.right, sourceRect.bottom);

                if (playerSprite != NULL) {
                    playerSprite->Draw(sprite);
                };

            // end drawing sprites
            returnValue = sprite->End();
            if (returnValue != S_OK) {
                FreeMemory();
                VDDirectXError("Failed to end the drawing scene.", returnValue, __FILE__, __LINE__);
            };

            // increment the player moving process based on the game update cycle
            playerSprite->AddToMovingDuration(playerMovementIncrement);

            // movement spanned 32 pixels, stop moving
            if (numberOfPixelsToShiftSprites == 32) {
                playerSprite->SetIsMoving(FALSE);
                playerSprite->SetMovingDuration(0);
            };

        // we're not moving
        } else {
            // traverse through the list of tiles
            for (unsigned int n = 0; n < tileSprites.size(); n++) {
                // begin drawing sprites
                returnValue = sprite->Begin(D3DXSPRITE_ALPHABLEND);
                if (returnValue != S_OK) {
                    FreeMemory();
                    VDDirectXError("Failed to prepare device for drawing sprites.", returnValue, __FILE__, __LINE__);
                };

                    // translate the sprites according to the player's position
                    tileSprites.at(n).ShiftLocation(direct3DDevice,
                                           (float) (windowWidth / 2) - userLocationX * 32.0f,
                                           (float) (windowHeight / 2) - userLocationY * 32.0f);
                    tileSprites.at(n).Draw(sprite);

                // end drawing sprites
                returnValue = sprite->End();
                if (returnValue != S_OK) {
                    FreeMemory();
                    VDDirectXError("Invalid call to ID3DXSprite::End.", returnValue, __FILE__, __LINE__);
                };
            };

            // traverse through the list of NPCs and objects
            for (unsigned int n = 0; n < npcSprites.size(); n++) {
                // begin drawing sprites
                returnValue = sprite->Begin(D3DXSPRITE_ALPHABLEND);
                if (returnValue != S_OK) {
                    FreeMemory();
                    VDDirectXError("Failed to prepare device for drawing sprites.", returnValue, __FILE__, __LINE__);
                };
                    // translate the NPC sprite according to the player's position
                    npcSprites.at(n).ShiftLocation(direct3DDevice,
                                           (float) (windowWidth / 2) - userLocationX * 32.0f,
                                           (float) (windowHeight / 2) - userLocationY * 32.0f);

                    // is the NPC in the process of moving?
                    if (npcSprites.at(n).IsMoving() == TRUE && npcSprites.at(n).GetMovingDuration() < 32) {
                        // make sure the movement doesn't exceed 32 pixels
                        int npcMovementIncrement = numUpdates;
                        while (npcMovementIncrement + npcSprites.at(n).GetMovingDuration() > 32) {
                            npcMovementIncrement--;
                        };

                        switch (npcSprites.at(n).GetDirection()) {
                            case 1:
                                npcSprites.at(n).AddToPosition(0.0f, (float) 0.0f - npcMovementIncrement);
                                break;
                            case 2:
                                npcSprites.at(n).AddToPosition(0.0f, (float) npcMovementIncrement);
                                break;
                            case 3:
                                npcSprites.at(n).AddToPosition((float) 0.0f - npcMovementIncrement,0.0f);
                                break;
                            case 4:
                                npcSprites.at(n).AddToPosition((float) npcMovementIncrement, 0.0f);
                                break;
                            default:
                                break;
                        };

                        // increment the NPC moving process based on the game update cycle
                        npcSprites.at(n).AddToMovingDuration(npcMovementIncrement);

                        // movement spanned 32 pixels, stop moving
                        if (npcSprites.at(n).GetMovingDuration() == 32) {
                            npcSprites.at(n).SetIsMoving(FALSE);
                            npcSprites.at(n).SetMovingDuration(0);
                        };
                    };

                    // draw the NPC
                    npcSprites.at(n).Draw(sprite);

                // end drawing sprites
                returnValue = sprite->End();
                if (returnValue != S_OK) {
                    FreeMemory();
                    VDDirectXError("Invalid call to ID3DXSprite::End.", returnValue, __FILE__, __LINE__);
                };
            };

            // begin drawing sprites
            returnValue = sprite->Begin(D3DXSPRITE_ALPHABLEND);
            if (returnValue != S_OK) {
                FreeMemory();
                VDDirectXError("Failed to prepare device for drawing sprites.", returnValue, __FILE__, __LINE__);
            };

                // change the sprite according to the direction
                RECT sourceRect;
                (void) playerSprite->GetSourceRectangle(&sourceRect);
                switch (playerSprite->GetDirection()) {
                    case 1:
                        sourceRect.left = 32;
                        sourceRect.right = 64;
                        break;
                    case 2:
                        sourceRect.left = 128;
                        sourceRect.right = 160;
                        break;
                    case 3:
                        sourceRect.left = 224;
                        sourceRect.right = 256;
                        break;
                    case 4:
                        sourceRect.left = 320;
                        sourceRect.right = 352;
                        break;
                    default:
                        break;
                };
                playerSprite->SetSourceRectangle(sourceRect.left, sourceRect.top, sourceRect.right, sourceRect.bottom);

                // draw the player
                if (playerSprite != NULL) {
                    playerSprite->Draw(sprite);
                };

            // end drawing sprites
            returnValue = sprite->End();
            if (returnValue != S_OK) {
                FreeMemory();
                VDDirectXError("Invalid call to ID3DXSprite::End.", returnValue, __FILE__, __LINE__);
            };
        };

        // draw text
        if (font != NULL) {
            for (list<Text>::iterator someText = text.begin(); someText != text.end(); ++someText) {
                returnValue = font->DrawText(NULL, someText->message, -1, &someText->textBox, DT_LEFT | DT_NOCLIP, someText->color);
                if (returnValue == 0) {
                    FreeMemory();
                    VDDirectXError("Failed to draw formatted text.", returnValue, __FILE__, __LINE__);
                };
            };
        };

    // end drawing
    returnValue = direct3DDevice->EndScene();
    if (returnValue != D3D_OK) {
        FreeMemory();
        VDDirectXError("Failed to end the drawing scene.", returnValue, __FILE__, __LINE__);
    };

    // present the content of the next buffer in the sequence of back buffers
    returnValue = direct3DDevice->Present(area, area, NULL, NULL);
    if (returnValue != D3D_OK) {
        FreeMemory();
        VDDirectXError("Failed to present the display buffer.", returnValue, __FILE__, __LINE__);
    };
};



/* **********************************************************************************************************************
   RenderSprite
   ------------
   Renders the graphics to the screen.
   #param  area     A rectangle representing the area to draw.
   #param  window   The overriding window to present the graphics to.
   #param  aSprite  The sprite to render in the rectangle.
   **********************************************************************************************************************/

void VDDirect3D::RenderSprite(const RECT *area, HWND window, VDSprite *aSprite) {
    HRESULT returnValue = NULL;

    // clear the screen
    returnValue = direct3DDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
    if (returnValue != D3D_OK) {
        FreeMemory();
        VDDirectXError("Failed to clear render target surface.", returnValue, __FILE__, __LINE__);
    };

    // begin drawing
    returnValue = direct3DDevice->BeginScene();
    if (returnValue != D3D_OK) {
        FreeMemory();
        VDDirectXError("Failed to start the drawing scene.", returnValue, __FILE__, __LINE__);
    };

        // begin drawing sprites
        returnValue = sprite->Begin(D3DXSPRITE_ALPHABLEND);
        if (returnValue != S_OK) {
            FreeMemory();
            VDDirectXError("Failed to prepare device for drawing sprites.", returnValue, __FILE__, __LINE__);
        };

            // draw the sprite
            if (aSprite != NULL) {
                aSprite->Draw(sprite);
            };

        // end drawing sprites
        returnValue = sprite->End();
        if (returnValue != S_OK) {
            FreeMemory();
            VDDirectXError("Invalid call to ID3DXSprite::End.", returnValue, __FILE__, __LINE__);
        };

    // end drawing
    returnValue = direct3DDevice->EndScene();
    if (returnValue != D3D_OK) {
        FreeMemory();
        VDDirectXError("Failed to end the drawing scene.", returnValue, __FILE__, __LINE__);
    };

    // present the content of the next buffer in the sequence of back buffers
    returnValue = direct3DDevice->Present(area, area, window, NULL);
    if (returnValue != D3D_OK) {
        FreeMemory();
        VDDirectXError("Failed to present the display buffer.", returnValue, __FILE__, __LINE__);
    };
};



/* **********************************************************************************************************************
   AddSprite
   ---------
   Creates a new 32x32 sprite and adds it to the list of sprites.
   #param  textureFile  Name of the texture (with directory structure included).
   #param  sourceRect   The source rectangle of the image to use.
   #param  x            X coordinate of the sprite on the screen.
   #param  y            Y coordinate of the sprite on the screen.
   #param  colorKey     Used to determine transparent colors.
   #param  isStatic     Does the sprite ever move?
   #param  isNPC        Is the sprite an NPC?
   #return  Pointer to the new sprite.
   **********************************************************************************************************************/

VDSprite* VDDirect3D::AddSprite(LPCTSTR textureFile, RECT sourceRect, float x, float y, D3DCOLOR colorKey, BOOL isStatic, BOOL isNPC) {
    // make sure the texture file exists
    if (strcmp(textureFile, "") == 0) {
        return NULL;
    };

    // create a temporary new sprite to hold information
    VDSprite *aSprite = new VDSprite(direct3DDevice, textureFile, sourceRect, D3DXVECTOR3(x, y, 0.0f), colorKey, isStatic, isNPC);

    if (isNPC == TRUE) {
        // add it to the list of sprites
        npcSprites.push_back(*aSprite);

        // delete temporary sprite
        delete aSprite; // TODO - is this safe to do if we delete aSprite which holds a reference to the texture object

        // return a pointer to the added sprite
        return &npcSprites.back();
    } else {
        tileSprites.push_back(*aSprite);
        delete aSprite;
        return &tileSprites.back();
    };
};



// TODO
VDSprite* VDDirect3D::AddPlayerSprite(LPCTSTR textureFile, RECT sourceRect, float x, float y, D3DCOLOR colorKey, int isStatic, int isNPC) {
    if (playerSprite != NULL) {
        delete playerSprite;
        playerSprite = NULL;
    };
    playerSprite = new VDSprite(direct3DDevice, textureFile, sourceRect, D3DXVECTOR3(x, y, 0.0f), colorKey, isStatic, isNPC);

    return playerSprite;
};

/* **********************************************************************************************************************
   RemoveTileSprite
   ----------------
   Looks for a tile in the given location and removes it if it exists.
   #param  x  X coordinate of the sprite.
   #param  y  Y coordinate of the sprite.
   **********************************************************************************************************************/

void VDDirect3D::RemoveTileSprite(float x, float y) {
    for (unsigned int n = 0; n < tileSprites.size(); n++) {
        if (tileSprites.at(n).GetPosition().x == x && tileSprites.at(n).GetPosition().y == y) {
            tileSprites.at(n).FreeTexture();
            std::swap(tileSprites.at(n), tileSprites.back());
            tileSprites.pop_back();
        };
    };
};



/* **********************************************************************************************************************
   RemoveNPCSprite
   ---------------
   Looks for an NPC in the given location and removes it if it exists.
   #param  x  X coordinate of the sprite.
   #param  y  Y coordinate of the sprite.
   **********************************************************************************************************************/

void VDDirect3D::RemoveNPCSprite(float x, float y) {
    for (unsigned int n = 0; n < npcSprites.size(); n++) {
        if (npcSprites.at(n).GetPosition().x == x && npcSprites.at(n).GetPosition().y == y) {
            npcSprites.at(n).FreeTexture();
            std::swap(npcSprites.at(n), npcSprites.back());
            npcSprites.pop_back();
        };
    };
};





/* **********************************************************************************************************************
   RemoveDeadSprites
   -----------------
   Traverses through all of the sprites, removing any that are marked as "Dead".
   **********************************************************************************************************************/

void VDDirect3D::RemoveDeadSprites() {
    for (unsigned int n = 0; n < tileSprites.size(); n++) {
        if (tileSprites.at(n).IsDead() == TRUE) {
            tileSprites.at(n).FreeTexture();
            std::swap(tileSprites.at(n), tileSprites.back());
            tileSprites.pop_back();
        };
    };
    for (unsigned int n = 0; n < npcSprites.size(); n++) {
        if (npcSprites.at(n).IsDead() == TRUE) {
            npcSprites.at(n).FreeTexture();
            std::swap(npcSprites.at(n), npcSprites.back());
            npcSprites.pop_back();
        };
    };
};



/* **********************************************************************************************************************
   ClearScreen
   -----------
   Remove all of the sprites.
   **********************************************************************************************************************/

void VDDirect3D::ClearScreen() {
    for (unsigned int n = 0; n < tileSprites.size(); n++) {
        tileSprites.at(n).FreeTexture();
    };
    tileSprites.clear();
    for (unsigned int n = 0; n < npcSprites.size(); n++) {
        npcSprites.at(n).FreeTexture();
    };
    npcSprites.clear();
};

void VDDirect3D::MovingPlayer(int direction) {
    playerSprite->MovingSprite(direction);
};

/* **********************************************************************************************************************
   IsNPCMoving
   -----------
   Checks if the NPC is in the process of moving.
   #param  x  The x position, in pixels, in relation to the rendering space of the NPC.
   #param  y  The y position, in pixels, in relation to the rendering space of the NPC.
   #return  TRUE if the NPC is moving, FALSE otherwise.
   **********************************************************************************************************************/

BOOL VDDirect3D::IsNPCMoving(float x, float y) {
    for (unsigned int n = 0; n < npcSprites.size(); n++) {
        if (npcSprites.at(n).GetPosition().x == x && npcSprites.at(n).GetPosition().y == y) {
            return npcSprites.at(n).IsMoving();
        };
    };

    return FALSE;
};



/* **********************************************************************************************************************
   MovingNPC
   ---------
   Let's the renderer know the NPC is moving.
   #param  x          The x position, in pixels, in relation to the rendering space of the NPC.
   #param  y          The y position, in pixels, in relation to the rendering space of the NPC.
   #param  direction  The direction in which the NPC is moving.
   **********************************************************************************************************************/

void VDDirect3D::MovingNPC(float x, float y, int direction) {
    for (unsigned int n = 0; n < npcSprites.size(); n++) {
        if (npcSprites.at(n).GetPosition().x == x && npcSprites.at(n).GetPosition().y == y) {
            npcSprites.at(n).MovingSprite(direction);
        };
    };
};



/* **********************************************************************************************************************
   AddText
   -------
   Adds text to be rendered.
   #param  theMessage  The message of the text.
   #param  theLeft     Left X coordinate of the text box.
   #param  theTop      Top Y coordinate of the text box.
   #param  theRight    Right X coordinate of the text box.
   #param  theBottom   Bottom Y coordinate of the text box.
   #param  theColor    The color of the text.
   #return  A pointer to the the message.
   **********************************************************************************************************************/

LPCSTR VDDirect3D::AddText(LPCSTR theMessage, int theLeft, int theTop, int theRight, int theBottom, D3DCOLOR theColor) {
    Text newText;
    newText.textBox.left = theLeft;
    newText.textBox.top = theTop;
    newText.textBox.right = theRight;
    newText.textBox.bottom = theBottom;
    errno_t returnValue = strcpy_s(newText.message, theMessage);
    if (returnValue != 0) {
        FreeMemory();
        VDError("Failed to set the name of the text.", __FILE__, __LINE__);
    };
    newText.color = theColor;
    text.push_back(newText);

    return text.back().message;
};



/* **********************************************************************************************************************
   RemoveText
   ----------
   Removes text from the screen that is the exact given string.
   #param  theMessage  The text to remove.
   **********************************************************************************************************************/

void VDDirect3D::RemoveText(LPCSTR theMessage) {
    for (list<Text>::iterator someText = text.begin(); someText != text.end();) {
        if (strcmp(theMessage, someText->message) == 0) {
            someText = text.erase(someText);
        } else {
            ++someText;
        };
    };
};



/* **********************************************************************************************************************
   RemoveTexts
   -----------
   Removes all text.
   **********************************************************************************************************************/

void VDDirect3D::RemoveTexts() {
    for (list<Text>::iterator someText = text.begin(); someText != text.end();) {
        someText = text.erase(someText);
    };
};



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

LPDIRECT3DDEVICE9 VDDirect3D::GetDirect3DRenderer() {
    return direct3DDevice;
};

LPD3DXSPRITE VDDirect3D::GetSpriteRenderer() {
    return sprite;
};