/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *
 *   This file is part of
 *       _______   ______________  ______     _____
 *      / ____/ | / /  _/ ____/  |/  /   |   |__  /
 *     / __/ /  |/ // // / __/ /|_/ / /| |    /_ <
 *    / /___/ /|  // // /_/ / /  / / ___ |  ___/ /
 *   /_____/_/ |_/___/\____/_/  /_/_/  |_| /____/.
 *
 *   Copyright © 2003-2010 Brain Control, all rights reserved.
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include <d3d11.h>
#include <d3dx11.h>

#include "../../eshared.hpp"

eBinTree eStateDx11::m_states;

eU32 eStateDx11::_hash(eU32 hash, eU32 value) const
{
    return (eHashInt(value) + (hash<<4));
}

eBlendStateDx11::eBlendStateDx11() : 
    blendingOn(eFALSE),
    blendSrc(eBLEND_ONE),
    blendDst(eBLEND_ONE),
    blendOp(eBLENDOP_ADD),
    colorWriteOn(eTRUE)
{

}

eU32 eBlendStateDx11::getHash() const
{
    eU32 hash = 1;
    hash = _hash(hash, blendingOn);
    hash = _hash(hash, blendSrc);
    hash = _hash(hash, blendDst);
    hash = _hash(hash, blendOp);
    hash = _hash(hash, colorWriteOn);
    return hash;
}

void eBlendStateDx11::activate(eU32 index, ID3D11Device *device, ID3D11DeviceContext *deviceContext)
{
    eASSERT(device != eNULL);
    eASSERT(deviceContext != eNULL);

    eU32 hash = getHash();
    ID3D11BlendState *state = (ID3D11BlendState *)m_states.findNode(hash);

    if (!state)
    {
        D3D11_BLEND_DESC desc;

        D3D11_BLEND blendModes[] =
        {
            D3D11_BLEND_ZERO,
            D3D11_BLEND_ONE,
            D3D11_BLEND_SRC_COLOR,
            D3D11_BLEND_INV_SRC_COLOR,
            D3D11_BLEND_SRC_ALPHA,
            D3D11_BLEND_INV_SRC_ALPHA,
            D3D11_BLEND_DEST_ALPHA,
            D3D11_BLEND_INV_DEST_ALPHA,
            D3D11_BLEND_DEST_COLOR,
            D3D11_BLEND_INV_DEST_COLOR,
        };

        D3D11_BLEND_OP blendOps[] =
        {
            D3D11_BLEND_OP_ADD,
            D3D11_BLEND_OP_SUBTRACT,
            D3D11_BLEND_OP_REV_SUBTRACT,
            D3D11_BLEND_OP_MIN,
            D3D11_BLEND_OP_MAX
        };

        desc.AlphaToCoverageEnable = false;
        desc.IndependentBlendEnable = false;
        desc.RenderTarget[0].BlendEnable = blendingOn;
        desc.RenderTarget[0].BlendOp = blendOps[blendOp];
        desc.RenderTarget[0].BlendOpAlpha = desc.RenderTarget[0].BlendOp;
        desc.RenderTarget[0].SrcBlend = blendModes[blendSrc];
        desc.RenderTarget[0].SrcBlendAlpha = desc.RenderTarget[0].SrcBlend;
        desc.RenderTarget[0].DestBlend = blendModes[blendDst];
        desc.RenderTarget[0].DestBlendAlpha = desc.RenderTarget[0].DestBlend;
        desc.RenderTarget[0].RenderTargetWriteMask = colorWriteOn ? D3D11_COLOR_WRITE_ENABLE_ALL : 0;

        HRESULT result = device->CreateBlendState(&desc, &state);
        eASSERT(!FAILED(result));

        m_states.addNode(hash, state);
    }

    const eF32 blendFactor[] = { 1.0f, 1.0f, 1.0f, 1.0f };
    deviceContext->OMSetBlendState(state, blendFactor, 0xffffffff);
}

eSamplerStateDx11::eSamplerStateDx11() : 
    texFilter(eTEXFILTER_TRILINEAR),
    texAddrMode(eTEXADDRMODE_WRAP)
{

}

eU32 eSamplerStateDx11::getHash() const
{
    eU32 hash = 2;
    hash = _hash(hash, texFilter);
    hash = _hash(hash, texAddrMode);
    return hash;
}

void eSamplerStateDx11::activate(eU32 index, ID3D11Device *device, ID3D11DeviceContext *deviceContext)
{
    eASSERT(device != eNULL);
    eASSERT(deviceContext != eNULL);

    eU32 hash = getHash();
    ID3D11SamplerState *state = (ID3D11SamplerState *)m_states.findNode(hash);

    if (!state)
    {
        D3D11_SAMPLER_DESC desc;
        eMemSet(&desc, 0, sizeof(D3D11_SAMPLER_DESC));

        D3D11_FILTER filterModes[] =
        {
            D3D11_FILTER_ANISOTROPIC,
            D3D11_FILTER_ANISOTROPIC,
            D3D11_FILTER_ANISOTROPIC,
        };

        D3D11_TEXTURE_ADDRESS_MODE addressModes[] =
        {
            D3D11_TEXTURE_ADDRESS_WRAP,
            D3D11_TEXTURE_ADDRESS_CLAMP,
            D3D11_TEXTURE_ADDRESS_MIRROR,
        };

        desc.AddressU = addressModes[texAddrMode];
        desc.AddressV = addressModes[texAddrMode];
        desc.AddressW = addressModes[texAddrMode];
        desc.ComparisonFunc = D3D11_COMPARISON_LESS;
        desc.Filter = filterModes[texFilter];
        desc.MaxAnisotropy = 16;
		//desc.MinLOD = 0;
		//desc.MipLODBias = 0;
        desc.MaxLOD = D3D11_FLOAT32_MAX;

		//eColor::BLACK.toFloatRgba(desc.BorderColor);

        HRESULT result = device->CreateSamplerState(&desc, &state);
        eASSERT(!FAILED(result));

		m_states.addNode(hash, state);
    }

    deviceContext->PSSetSamplers(index, 1, &state);
}

eRasterizerStateDx11::eRasterizerStateDx11() : 
    cullingMode(eCULLING_BACK),
    scissorTestOn(eFALSE),
    alphaTestOn(eFALSE)
{

}

eU32 eRasterizerStateDx11::getHash() const
{
    eU32 hash = 3;
    hash = _hash(hash, cullingMode);
    hash = _hash(hash, scissorTestOn);
    hash = _hash(hash, alphaTestOn);
    return hash;
}

void eRasterizerStateDx11::activate(eU32 index, ID3D11Device *device, ID3D11DeviceContext *deviceContext)
{
    eASSERT(device != eNULL);
    eASSERT(deviceContext != eNULL);

    eU32 hash = getHash();
    ID3D11RasterizerState *state = (ID3D11RasterizerState *)m_states.findNode(hash);

    if (!state)
    {
        D3D11_RASTERIZER_DESC desc;
        eMemSet(&desc, 0, sizeof(D3D11_RASTERIZER_DESC));

        D3D11_CULL_MODE cullModes[] =
        {
            D3D11_CULL_NONE,
            D3D11_CULL_FRONT,
            D3D11_CULL_BACK
        };
    
        desc.AntialiasedLineEnable = true;
        desc.CullMode = cullModes[cullingMode];
        //desc.DepthBias = 0;
        //desc.DepthBiasClamp = 0.0f;
        desc.DepthClipEnable = true;
        desc.FillMode = D3D11_FILL_SOLID;
        //desc.FrontCounterClockwise = false;
        //desc.MultisampleEnable = false;
        desc.ScissorEnable = scissorTestOn;
        //desc.SlopeScaledDepthBias = 0.0f;

        HRESULT result = device->CreateRasterizerState(&desc, &state);
        eASSERT(!FAILED(result));

		m_states.addNode(hash, state);
    }

    deviceContext->RSSetState(state);
}

eDepthStencilStateDx11::eDepthStencilStateDx11() : 
    depthTestOn(eTRUE),
    zWriteOn(eTRUE),
    zFunc(eZFUNC_LESSEQUAL)
{

}

eU32 eDepthStencilStateDx11::getHash() const
{
    eU32 hash = 4;
    hash = _hash(hash, depthTestOn);
    hash = _hash(hash, zWriteOn);
    hash = _hash(hash, zFunc);
    return hash;
}

void eDepthStencilStateDx11::activate(eU32 index, ID3D11Device *device, ID3D11DeviceContext *deviceContext)
{
    eASSERT(device != eNULL);
    eASSERT(deviceContext != eNULL);

    eU32 hash = getHash();
    ID3D11DepthStencilState *state = (ID3D11DepthStencilState *)m_states.findNode(hash);

    if (!state)
    {
        D3D11_DEPTH_STENCIL_DESC desc;
        eMemSet(&desc, 0, sizeof(D3D11_DEPTH_STENCIL_DESC));
   
        D3D11_COMPARISON_FUNC depthFuncs[] =
        {
            D3D11_COMPARISON_NEVER,
            D3D11_COMPARISON_LESS,
            D3D11_COMPARISON_EQUAL,
            D3D11_COMPARISON_LESS_EQUAL,
            D3D11_COMPARISON_GREATER,
            D3D11_COMPARISON_NOT_EQUAL,
            D3D11_COMPARISON_GREATER_EQUAL,
            D3D11_COMPARISON_ALWAYS
        };

        // Set up the description of the stencil state.
        desc.DepthEnable = depthTestOn;
        desc.DepthWriteMask = zWriteOn ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO;
        desc.DepthFunc = depthFuncs[zFunc];

        /*
        desc.StencilEnable = false;
        desc.StencilReadMask = 0xFF;
        desc.StencilWriteMask = 0xFF;

        // Stencil operations if pixel is front-facing.
        desc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
        desc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR;
        desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
        desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;

        // Stencil operations if pixel is back-facing.
        desc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
        desc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR;
        desc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
        desc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
        */

        HRESULT result = device->CreateDepthStencilState(&desc, &state);
        eASSERT(!FAILED(result));

		m_states.addNode(hash, state);
    }

    deviceContext->OMSetDepthStencilState(state, 1);
}

