#ifndef __D3DDevice__
#define __D3DDevice__

#include <windows.h>
#include <wincodec.h>
#include <hash_map>
#include <string>
#include <sstream>

#include <assimp.hpp>
#include <aiMesh.h>
#include <aiMaterial.h>
#include <aiAnim.h>
#include <aiScene.h>
#include <aiPostProcess.h>



#include "../Core/types.h"
#include "../Core/RenderDevice.h"
#include "../Core/SharedResource.h"

#include "D3DDefs.h"
#include "DeviceResources.h"
#include "HlslShader.h"
#include "D3DRenderTarget.h"
#include "D3DDepthStencilBuffer.h"


/**************************************
*	D3DDevice class
**************************************/

class D3DDevice : public RenderDevice {
public:
    D3DDevice();
    virtual ~D3DDevice();

    const wchar_t* getDeviceName();
    const wchar_t* getDeviceInfo();

    void*  loadTextureFromMemory(byte* data, uint32 size);
    void*  loadTextureFromFloatArray(unsigned int width, unsigned int height, float* data);

    void** loadMeshArrayFromMemory(aiMesh** rawMeshes, uint32 meshNum);


    Shader* createShader(const wchar_t* fileName);

    RenderTarget* createRenderTarget(uint32 width, uint32 height, uint32 sampleCount);
    DepthStencilBuffer* createDepthStencilBuffer(uint32 width, uint32 height, uint32 sampleCount);

protected:
    class D3DPerfMonitor : public RenderDevice::PerfMonitor {
    public:
        D3DPerfMonitor(ID3D10Device1* device);
        ~D3DPerfMonitor();

        void begin();
        void end();

        bool isValid();
        uint64 getFrequency();

    protected:
        ID3D10Device1*  _device;
        ID3D10Query*    _query;

        bool    _valid;
        uint64  _frequency;
    };

    class D3DPerfCounter : public RenderDevice::PerfCounter {
    public:
        D3DPerfCounter(ID3D10Device1* device);
        ~D3DPerfCounter();

        void mark();
        uint64 recall();

    protected:
        ID3D10Device1*  _device;
        ID3D10Query*    _query;
    
        uint64 _tickCount;
    };

    RenderDevice::PerfMonitor* createPerfMonitor();
    RenderDevice::PerfCounter* createPerfCounter();


    HRESULT create();
    void    destroy();

    void drawText(Text* text);
    void drawSprite(Sprite* sprite);
    void drawGeode(Geode* geode);

    void clear(float r, float g, float b, float a);
    void flush();

    void setRenderTargets(RenderTarget** renderTargets, DepthStencilBuffer* depthStencilBuffer);
    void clearRenderTargets(float r, float g, float b, float a);

    HRESULT createDeviceIndependentResources();
    HRESULT createDeviceResources();
    HRESULT recreateSizedResources();
    HRESULT createD3DDeviceResources();

    void discardDeviceResources();


    DXGI_ADAPTER_DESC       _adapterDesc;
    wchar_t                 _deviceInfo[1024];

    IDXGIFactory*           _dxgiFactory;
    IDXGIDevice*            _dxgiDevice;
    IDXGIAdapter*           _adapter;
    IDXGISwapChain*         _swapChain;
    ID3D10Device1*          _device;

    ID3D10RasterizerState*  _state;

    ID3D10Texture2D*         _renderTarget;
    ID3D10RenderTargetView*  _renderTargetView;

    ID3D10Texture2D*        _depthStencil;
    ID3D10DepthStencilView* _depthStencilView;


    ID3D10Buffer*       _spriteVertexBuffer;
    ID3D10Buffer*       _spriteIndexBuffer;

    static const Vertex _spriteVertexArray[];
    static const uint32 _spriteIndexArray[];

    stdext::hash_map<std::wstring, HlslShader*> _shaders;

    UINT _renderTargetCount;
    ID3D10RenderTargetView* _viewList[16];
    ID3D10DepthStencilView* _depthBufferView;
};


/**************************************
*	Device factory methods
**************************************/

extern "C" __declspec(dllexport) const wchar_t*  getDeviceName();
extern "C" __declspec(dllexport) Device*         acquireDevice();
extern "C" __declspec(dllexport) void            releaseDevice(Device* device);

#endif