///////////////////////////////////////////////////////////////////////////////////////////
// Texture.cpp
//
//  Implementation of the Texture class, which wraps a Direct3D 8 texture.
//
//  Texture creates a texture object without any filtering, in the managed pool. This makes
// it primarily useful for sprite textures. If you want to change this behaviour, you can
// override Create.
//
//  If you don't want to use the Direct3D support for managed textures, the best way to
// deal with it is to hook into the Device object's Initialized and Release events,
// instead of the Created and Destroy. Release the texture on Lost, and recreate 
// it in Initialized. This has the advantage of preloading the textures into video memory
// before you have to use them (managed textures don't), and makes the system memory 
// footprint lower - but it's slower to reactivate, since the texture must be loaded from 
// disk rather than memory.

#include "StdAfx.h"
#include "Texture.h"
#include "Utility.h"

using namespace System::Runtime::InteropServices;

namespace Sunlight
{
    namespace DirectX
    {
        namespace Graphics
        {
            Texture::Texture() :
                m_pDeviceObject(NULL), 
                m_pTexture(NULL), 
                Filename(NULL),
                m_bCreated(false)
            {
            }
            
            Texture::~Texture()
            {
            }
            
            // Create the texture object.
            void Texture::Create()
            {
                if (m_bCreated)
                    return;

                if (m_pDeviceObject == NULL)
                    throw new ArgumentNullException(S"DeviceObject");

                if (!m_pDeviceObject->IsCreated)
                    return;

                IDirect3DTexture8 __nogc *pTexture;

#ifdef _UNICODE
                LPWSTR tszFilename = (LPWSTR)(void *)Marshal::StringToCoTaskMemUni(Filename);
#else
                LPSTR tszFilename = (LPSTR)(void *)Marshal::StringToCoTaskMemAnsi(Filename);
#endif
                HRESULT h = ::D3DXCreateTextureFromFileEx((LPDIRECT3DDEVICE8)m_pDeviceObject->Direct3DDevice,
                    tszFilename, D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 
                    0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_FILTER_NONE, D3DX_DEFAULT,
                    0, NULL, NULL, &pTexture);

                CoTaskMemFree(tszFilename);

                if (FAILED(h))
                {
                    if (h == ERROR_FILE_NOT_FOUND)
                        throw new IO::FileNotFoundException(String::Format(S"Texture file {0} not found.", Filename), Filename);
                    if (h == D3DXERR_INVALIDDATA)
                        throw new IO::FileLoadException(String::Format(S"Texture file {0} could not be loaded.", Filename), Filename);
                    throw new Sunlight::DirectX::DirectXException(S"IDirect3DDevice8::CreateVertexBuffer", h);
                }
                m_pTexture = pTexture;
                m_bCreated = true;
            }

            // Destroy the texture object.
            void Texture::Destroy()
            {
                if (m_pTexture != NULL)
                {
                    m_pTexture->Release();
                    m_pTexture = NULL;
                }
                m_bCreated = false;
            }

            void Texture::OnDeviceCreated(Object * /*sender*/, EventArgs * /*e*/)
            {
                Create();
            }
            void Texture::OnDeviceDestroyed(Object * /*sender*/, EventArgs * /*e*/)
            {
                Destroy();
            }

            // The Device object with which this texture will be created.
            Device *Texture::get_DeviceObject()
            {
                return m_pDeviceObject;
            }
            void Texture::set_DeviceObject(Device *pDevice)
            {
                m_pDeviceObject = pDevice;

                // Hook the device's Created event so we can (re-)create our texture when it is created
                __hook(&Device::Created, m_pDeviceObject, &Texture::OnDeviceCreated, this);
                // Hook the device's Destroy event so we can release our texture when it is destroyed
                __hook(&Device::Releasing, m_pDeviceObject, &Texture::OnDeviceDestroyed, this);
            }
            // The Direct3D texture object underlying this object.
            IDirect3DTexture8 __nogc *Texture::get_Direct3DTexture()
            {
                return m_pTexture;
            }
            // The width of the texture.
            int Texture::get_Width()
            {
                if (!m_bCreated)
                    return 0;

                D3DSURFACE_DESC desc;
                m_pTexture->GetLevelDesc(0, &desc);
                return desc.Width;
            }

            // The height of the texture.
            int Texture::get_Height()
            {
                if (!m_bCreated)
                    return 0;

                D3DSURFACE_DESC desc;
                m_pTexture->GetLevelDesc(0, &desc);
                return desc.Height;
            }
        }
    }
}