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

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

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

#define eSHADER_MODEL_VERTEX "vs_4_0"
#define eSHADER_MODEL_PIXEL "ps_4_0"
#define eSHADER_MODEL_GEOMETRY "gs_4_0"
#define eSHADER_MODEL_DOMAIN "ds_4_0"
#define eSHADER_MODEL_HULL "hs_4_0"
#define eSHADER_MODEL_COMPUTE "cs_4_0"

eShaderDx11::eShaderDx11() { }

eShaderDx11::eShaderDx11(ID3D11Device *dev, ID3D11DeviceContext *devContext) :
    m_device(dev),
    m_deviceContext(devContext),
    m_shader(eNULL),
    m_byteCode(eNULL),
    m_byteCodeLen(0)
{
    eASSERT(m_device != eNULL);
    eASSERT(m_deviceContext != eNULL);
}

eShaderDx11::~eShaderDx11()
{
    eSAFE_RELEASE_COM(m_shader);
    eSAFE_DELETE_ARRAY(m_byteCode);
}

const eU8 * eShaderDx11::getByteCode()
{
    return m_byteCode;
}

eU32 eShaderDx11::getByteCodeLen()
{
    return m_byteCodeLen;
}

eBool eShaderDx11::compile(eShaderSource src, eChar *model)
{
    eSAFE_RELEASE_COM(m_shader);

#ifdef eDEBUG
    ID3DBlob *errMsg, *binary;
    HRESULT res = D3DX11CompileFromFile(src, eNULL, eNULL, "main", model,
        D3DCOMPILE_PACK_MATRIX_ROW_MAJOR | D3DCOMPILE_ENABLE_BACKWARDS_COMPATIBILITY, 0, eNULL, &binary, &errMsg, eNULL);

    if (FAILED(res))
    {
        if (res == E_FAIL)
        {
            eShowError(eString("Couldn't load shader \"")+src+"\"!");
        }
        else if (errMsg)
        {
            eShowError((const eChar *)errMsg->GetBufferPointer());
        }

        eASSERT(eFALSE);
        return eFALSE;
    }

    eSAFE_DELETE_ARRAY(m_byteCode);
    m_byteCodeLen = binary->GetBufferSize();
    m_byteCode = new eU8[m_byteCodeLen];
    eMemCopy(m_byteCode, binary->GetBufferPointer(), m_byteCodeLen);
#else
    m_byteCodeLen = src.length;
    m_byteCode = new eU8[m_byteCodeLen];
    eMemCopy(m_byteCode, src.data, m_byteCodeLen);
#endif

    return eTRUE;
}

ePixelShaderDx11::ePixelShaderDx11() { }
ePixelShaderDx11::ePixelShaderDx11(const ePixelShaderDx11 &ps) { }
ePixelShaderDx11 & ePixelShaderDx11::operator = (const ePixelShaderDx11 &ps) { return *this; }

ePixelShaderDx11::ePixelShaderDx11(ID3D11Device *dev, ID3D11DeviceContext *devContext, eShaderSource src) :
    eShaderDx11(dev, devContext)
{
    load(src);
}

eBool ePixelShaderDx11::load(eShaderSource src)
{
    if (!compile(src, eSHADER_MODEL_PIXEL))
        return eFALSE;

    HRESULT res = m_device->CreatePixelShader(m_byteCode, m_byteCodeLen, eNULL, (ID3D11PixelShader**)&m_shader);
    eASSERT(!FAILED(res));
    return eTRUE;
}

void ePixelShaderDx11::bind()
{
    eASSERT(m_shader != eNULL);
    m_deviceContext->PSSetShader((ID3D11PixelShader*)m_shader, eNULL, 0);
}

eVertexShaderDx11::eVertexShaderDx11() { }
eVertexShaderDx11::eVertexShaderDx11(const eVertexShaderDx11 &ps) { }
eVertexShaderDx11 & eVertexShaderDx11::operator = (const eVertexShaderDx11 &ps) { return *this; }

eVertexShaderDx11::eVertexShaderDx11(ID3D11Device *dev, ID3D11DeviceContext *devContext, eShaderSource src) :
    eShaderDx11(dev, devContext)
{
    load(src);
}

eBool eVertexShaderDx11::load(eShaderSource src)
{
    if (!compile(src, eSHADER_MODEL_VERTEX))
        return eFALSE;

    HRESULT res = m_device->CreateVertexShader(m_byteCode, m_byteCodeLen, eNULL, (ID3D11VertexShader**)&m_shader);
    eASSERT(!FAILED(res));
    return eTRUE;
}

void eVertexShaderDx11::bind()
{
    eASSERT(m_shader != eNULL);
    m_deviceContext->VSSetShader((ID3D11VertexShader*)m_shader, eNULL, 0);
}

eGeometryShaderDx11::eGeometryShaderDx11() { }
eGeometryShaderDx11::eGeometryShaderDx11(const eGeometryShaderDx11 &ps) { }
eGeometryShaderDx11 & eGeometryShaderDx11::operator = (const eGeometryShaderDx11 &ps) { return *this; }

eGeometryShaderDx11::eGeometryShaderDx11(ID3D11Device *dev, ID3D11DeviceContext *devContext, eShaderSource src) :
	eShaderDx11(dev, devContext)
{
	load(src);
}

eBool eGeometryShaderDx11::load(eShaderSource src)
{
	if (!compile(src, eSHADER_MODEL_GEOMETRY))
		return eFALSE;

	HRESULT res = m_device->CreateGeometryShader(m_byteCode, m_byteCodeLen, eNULL, (ID3D11GeometryShader**)&m_shader);
	eASSERT(!FAILED(res));
	return eTRUE;
}

void eGeometryShaderDx11::bind()
{
	eASSERT(m_shader != eNULL);
	m_deviceContext->GSSetShader((ID3D11GeometryShader*)m_shader, eNULL, 0);
}

eDomainShaderDx11::eDomainShaderDx11() { }
eDomainShaderDx11::eDomainShaderDx11(const eDomainShaderDx11 &ps) { }
eDomainShaderDx11 & eDomainShaderDx11::operator = (const eDomainShaderDx11 &ps) { return *this; }

eDomainShaderDx11::eDomainShaderDx11(ID3D11Device *dev, ID3D11DeviceContext *devContext, eShaderSource src) :
	eShaderDx11(dev, devContext)
{
	load(src);
}

eBool eDomainShaderDx11::load(eShaderSource src)
{
	if (!compile(src, eSHADER_MODEL_DOMAIN))
		return eFALSE;

	HRESULT res = m_device->CreateDomainShader(m_byteCode, m_byteCodeLen, eNULL, (ID3D11DomainShader**)&m_shader);
	eASSERT(!FAILED(res));
	return eTRUE;
}

void eDomainShaderDx11::bind()
{
	eASSERT(m_shader != eNULL);
	m_deviceContext->DSSetShader((ID3D11DomainShader*)m_shader, eNULL, 0);
}

eHullShaderDx11::eHullShaderDx11() { }
eHullShaderDx11::eHullShaderDx11(const eHullShaderDx11 &ps) { }
eHullShaderDx11 & eHullShaderDx11::operator = (const eHullShaderDx11 &ps) { return *this; }

eHullShaderDx11::eHullShaderDx11(ID3D11Device *dev, ID3D11DeviceContext *devContext, eShaderSource src) :
	eShaderDx11(dev, devContext)
{
	load(src);
}

eBool eHullShaderDx11::load(eShaderSource src)
{
	if (!compile(src, eSHADER_MODEL_HULL))
		return eFALSE;

	HRESULT res = m_device->CreateHullShader(m_byteCode, m_byteCodeLen, eNULL, (ID3D11HullShader**)&m_shader);
	eASSERT(!FAILED(res));
	return eTRUE;
}

void eHullShaderDx11::bind()
{
	eASSERT(m_shader != eNULL);
	m_deviceContext->HSSetShader((ID3D11HullShader*)m_shader, eNULL, 0);
}

eComputeShaderDx11::eComputeShaderDx11() { }
eComputeShaderDx11::eComputeShaderDx11(const eComputeShaderDx11 &ps) { }
eComputeShaderDx11 & eComputeShaderDx11::operator = (const eComputeShaderDx11 &ps) { return *this; }

eComputeShaderDx11::eComputeShaderDx11(ID3D11Device *dev, ID3D11DeviceContext *devContext, eShaderSource src) :
	eShaderDx11(dev, devContext)
{
	load(src);
}

eBool eComputeShaderDx11::load(eShaderSource src)
{
	if (!compile(src, eSHADER_MODEL_COMPUTE))
		return eFALSE;

	HRESULT res = m_device->CreateComputeShader(m_byteCode, m_byteCodeLen, eNULL, (ID3D11ComputeShader**)&m_shader);
	eASSERT(!FAILED(res));
	return eTRUE;
}

void eComputeShaderDx11::bind()
{
	eASSERT(m_shader != eNULL);
	m_deviceContext->CSSetShader((ID3D11ComputeShader*)m_shader, eNULL, 0);
}
