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

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

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

static const struct FormatInfo
{
    DXGI_FORMAT     dxgiFormat;
    eU32            pixelSize;
}

FORMAT_INFOS[] =
{
    { DXGI_FORMAT_R8G8B8A8_UNORM,      4},
    { DXGI_FORMAT_R16G16B16A16_UNORM,  8},
    { DXGI_FORMAT_R16G16B16A16_FLOAT,  8},
    { DXGI_FORMAT_D16_UNORM,           2},
    { DXGI_FORMAT_D24_UNORM_S8_UINT,   4},
    { DXGI_FORMAT_R16_FLOAT,           2},
    { DXGI_FORMAT_R32_FLOAT,           4},
    { DXGI_FORMAT_R16G16_FLOAT,        4},
    { DXGI_FORMAT_R32G32_FLOAT,        8}
};

eTextureDx11::eTextureDx11()
{

}

eTextureDx11::eTextureDx11(ID3D11Device *dev, ID3D11DeviceContext *devContext, eBool dynamic, eU32 width, eU32 height, eU32 depth, eBool renderTarget, eBool mipMapped, eFormat format) :
    eResourceDx11(dev, devContext, dynamic),
    m_width(width),
    m_height(height),
    m_renderTarget(renderTarget),
    m_mipMapped(mipMapped),
    m_format(format),
    m_dxgiFormat(FORMAT_INFOS[format].dxgiFormat),
    m_pixelSize(FORMAT_INFOS[format].pixelSize),
    m_data(eNULL),
	m_isCube(eFALSE)
{

}

eTextureDx11::~eTextureDx11()
{
    release();
}

void eTextureDx11::release()
{
    releaseResource();
    eSAFE_DELETE_ARRAY(m_data);
}

eSize eTextureDx11::getSize() const
{
    return eSize(m_width, m_height);
}

eU32 eTextureDx11::getWidth() const
{
    return m_width;
}

eU32 eTextureDx11::getHeight() const
{
    return m_height;
}

eU32 eTextureDx11::getDepth() const
{
    return m_depth;
}

eFormat eTextureDx11::getFormat() const
{
    return m_format;
}

eBool eTextureDx11::isMipMapped() const
{
    return m_mipMapped;
}

eBool eTextureDx11::isRenderTarget() const
{
    return m_renderTarget;
}

eBool eTextureDx11::isCube() const
{
	return m_isCube;
}

eTexture2dDx11::eTexture2dDx11()
{
}

eTexture2dDx11::eTexture2dDx11(const eTexture2dDx11 &tex)
{
}

eTexture2dDx11 & eTexture2dDx11::operator = (eTexture2dDx11 &tex)
{
    return *this;
}

eTexture2dDx11::eTexture2dDx11(ID3D11Device *dev, ID3D11DeviceContext *devContext, eU32 width, eU32 height, const ePtr data, eBool renderTarget, eBool mipMapped, eBool dynamic, eFormat format) :
    eTextureDx11(dev, devContext, dynamic, width, height, 0, renderTarget, mipMapped, format)
{
    eASSERT(width*height > 0);

    // Only create data array if texture is not a target.
    if (!renderTarget)
    {
        m_data = new eU8[width*height*m_pixelSize];
        eASSERT(m_data != eNULL);

        if (data)
            eMemCopy(m_data, data, width*height*m_pixelSize);
        else
            eMemSet(m_data, 255, width*height*m_pixelSize);
    }

    upload();
}

eTexture2dDx11::~eTexture2dDx11()
{
    release();
}

eBool eTexture2dDx11::upload()
{
    eASSERT(m_resource == eNULL);

    D3D11_TEXTURE2D_DESC desc;
    eMemSet(&desc, 0, sizeof(D3D11_TEXTURE2D_DESC));
    desc.Format = m_dxgiFormat;
	desc.ArraySize = 1;
    desc.Width = m_width;
    desc.Height = m_height;
    desc.MipLevels = 1; //m_mipMapped ? 0 : 1;
	desc.SampleDesc.Count = 1;
	desc.SampleDesc.Quality = 0;

	if (!m_renderTarget)
	{
		desc.Usage = m_dynamic ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
		desc.CPUAccessFlags = m_dynamic ? D3D11_CPU_ACCESS_WRITE : 0;
		desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;

		D3D11_SUBRESOURCE_DATA data;
		data.pSysMem = m_data;
		data.SysMemPitch = m_width * m_pixelSize;
		data.SysMemSlicePitch = 0;

		HRESULT result = m_device->CreateTexture2D(&desc, &data, (ID3D11Texture2D**)&m_resource);
		eASSERT(!FAILED(result));

		createShaderResourceView();
	}
	else
	{
		eBool isDepthMap = eFALSE;
		if (m_format == eFORMAT_DEPTH16 ||
			m_format == eFORMAT_DEPTH24X8)
			isDepthMap = eTRUE;

		desc.Usage = D3D11_USAGE_DEFAULT;

		if (!isDepthMap)
			desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
		else
			desc.BindFlags = D3D11_BIND_DEPTH_STENCIL;

		HRESULT result = m_device->CreateTexture2D(&desc, eNULL, (ID3D11Texture2D**)&m_resource);
		eASSERT(!FAILED(result));

		if (isDepthMap)
			createDepthStencilView();
		else
		{
			createRenderTargetView();
			createShaderResourceView();
		}
	}

    return eTRUE;
}

#ifndef eINTRO
void eTexture2dDx11::saveToFile(const eChar *fileName)
{
    eASSERT(fileName != eNULL);
    D3DX11SaveTextureToFile(m_deviceContext, m_resource, D3DX11_IFF_BMP, fileName);
}
#endif

// 3D texture implementation starts here.
eTexture3dDx11::eTexture3dDx11()
{
}

eTexture3dDx11::eTexture3dDx11(const eTexture3dDx11 &tex)
{
}

eTexture3dDx11 & eTexture3dDx11::operator = (eTexture3dDx11 &tex)
{
    return *this;
}

eTexture3dDx11::eTexture3dDx11(ID3D11Device *dev, ID3D11DeviceContext *devContext, eU32 width, eU32 height, eU32 depth, eBool mipMapped, eBool dynamic, eFormat format) :
    eTextureDx11(dev, devContext, dynamic, width, height, 0, eFALSE, mipMapped, format)
{
    eASSERT(format != eFORMAT_DEPTH16 && format != eFORMAT_DEPTH24X8);
    eASSERT(width*height*depth > 0);

    const eU32 byteSize = width*height*depth*m_pixelSize;
    m_data = new eU8[byteSize];
    eASSERT(m_data != eNULL);
    eMemSet(m_data, 255, byteSize);

    upload();
}

eTexture3dDx11::~eTexture3dDx11()
{
    release();
}

eBool eTexture3dDx11::upload()
{
    eASSERT(m_resource == eNULL);

    D3D11_TEXTURE3D_DESC desc;
    eMemSet(&desc, 0, sizeof(D3D11_TEXTURE2D_DESC));
    desc.Format = m_dxgiFormat;
    desc.Width = m_width;
    desc.Height = m_height;
    desc.Depth = m_depth;
    desc.MipLevels = 1;  //m_mipMapped ? 0 : 1;
    desc.Usage = m_dynamic ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
	desc.CPUAccessFlags = m_dynamic ? D3D11_CPU_ACCESS_WRITE : 0;
	desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;

    HRESULT result = m_device->CreateTexture3D(&desc, eNULL, (ID3D11Texture3D**)&m_resource);
    eASSERT(!FAILED(result));

    createShaderResourceView();

    return eTRUE;
}

// Cube texture implementation starts here.
eTextureCubeDx11::eTextureCubeDx11()
{
}

eTextureCubeDx11::eTextureCubeDx11(const eTextureCubeDx11 &tex)
{
}

eTextureCubeDx11 & eTextureCubeDx11::operator = (eTextureCubeDx11 &tex)
{
    return *this;
}

eTextureCubeDx11::eTextureCubeDx11(ID3D11Device *dev, ID3D11DeviceContext *devContext, eU32 width, eBool renderTarget, eBool mipMapped, eBool dynamic, eFormat format) :
    eTextureDx11(dev, devContext, dynamic, width, 0, 0, renderTarget, mipMapped, format)
{
    eASSERT(width > 0);

	m_isCube = eTRUE;
    if (!renderTarget)
    {
        const eU32 byteSize = width*width*m_pixelSize;
        m_data = new eU8[byteSize*eCMFACE_COUNT];
        eASSERT(m_data != eNULL);
        eMemSet(m_data, 255, byteSize*eCMFACE_COUNT);
    }
	
    upload();
}

eTextureCubeDx11::~eTextureCubeDx11()
{
    release();
}

eBool eTextureCubeDx11::upload()
{
    eASSERT(m_resource == eNULL);

    D3D11_TEXTURE2D_DESC desc;
    eMemSet(&desc, 0, sizeof(D3D11_TEXTURE2D_DESC));
    desc.Format = m_dxgiFormat;
    desc.Width = m_width;
	desc.Height = m_width;
    desc.MipLevels = 1;
    desc.Usage = m_dynamic ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
	desc.CPUAccessFlags = m_dynamic ? D3D11_CPU_ACCESS_WRITE : 0;
	desc.SampleDesc.Quality = 0;
	desc.SampleDesc.Count = 1;
    desc.ArraySize = 6;
	desc.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE;
	desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | (m_renderTarget ? D3D11_BIND_RENDER_TARGET : 0);

    HRESULT result = m_device->CreateTexture2D(&desc, eNULL, (ID3D11Texture2D**)&m_resource);
    eASSERT(!FAILED(result));

    createShaderResourceView();

	if (m_renderTarget)
	{
		createRenderTargetViewsCube();
	}

    return eTRUE;
}

#ifndef eINTRO
void eTextureCubeDx11::saveToFile(const eChar *fileName)
{
    eASSERT(fileName != eNULL);
    D3DX11SaveTextureToFile(m_deviceContext, m_resource, D3DX11_IFF_DDS, fileName);
}
#endif

