//	Darkstar Engine  (c) UnderWorld Interactive Studio (all coding by Svarog    2007-2008)
//
//      Данный файл является частью Darkstar Engine. Любое использование кода файла
//      без согласия компании, является нарушением закона об авторском праве (в слу-
//      чае если не оговорено обратное).
//
//      Имя файла: render_target_d3d10.cpp
//      Краткое описание: Render Target object for direct3D 10
// 

#ifdef GD_D3D10

#include <Darkstar/Graphic System/Render Target/render_target_d3d10.hpp>
#include <Darkstar/Kernel/logh.hpp>
#include <Darkstar/Graphic System/Direct3D/graphicDriver.hpp>

using namespace gs;
using rs::RTTexture;
using rs::BaseTexture;
using ker::Log;

RenderTarget::RenderTarget()
{
	layers_number = 0;
	depth_stencil_view.resize(1);
	depth_stencil_view[0] = NULL;
	render_target_views.resize(1);
	render_target_views[0] = NULL;
	darkstar_texture.reset();
	texture_data = NULL;
	texture_data_without_aa = NULL;
	width = height = 0;
};

RenderTarget::~RenderTarget()
{
	if(render_target_views.size() > 0)
	{
		for(unsigned int i = 0; i < static_cast<unsigned int>(render_target_views.size()); ++i)
		{
			if(render_target_views[i])
			{
				if(FAILED(render_target_views[i]->Release()))
				{
					lwar << "Cannot release render target view";
					return;
				};
				render_target_views[i] = NULL;
			};
		};
	};
	if(depth_stencil_view.size() > 0)
	{
		for(unsigned int i = 0; i < static_cast<unsigned int>(render_target_views.size()); ++i)
		{
			if(depth_stencil_view[i])
			{
				if(FAILED(depth_stencil_view[i]->Release()))
				{
					lwar << "Cannot release depth stencil view";
					return;
				};
				depth_stencil_view[i] = NULL;
			};
		};
	};
	if(texture_data)
	{
		if(FAILED(texture_data->Release()))
		{
			lwar << "Cannot release texture";
			return;
		};
		if(texture_data == texture_data_without_aa)
			texture_data_without_aa = NULL;
		texture_data = NULL;
	};
	if(texture_data_without_aa)
	{
		if(FAILED(texture_data_without_aa->Release()))
		{
			lwar << "Cannot release texture";
			return;
		};
		texture_data_without_aa = NULL;
	};
};

shared_ptr<BaseTexture> RenderTarget::getTexture() const
{
	return darkstar_texture;
};

bool RenderTarget::create2D(const unsigned int width, const unsigned int height, const DRTFORMAT& format, 
						  const DRTAATYPE& aa_type, bool need_texture)
{
	ID3D10Device* device = GraphicDriver::ptr()->getDevice();

	D3D10_TEXTURE2D_DESC tex_desc;
	tex_desc.Width = width;
	tex_desc.Height = height;
	tex_desc.Format = getSpecRTFormat(format);
	tex_desc.SampleDesc.Count = aa_type.count;
	tex_desc.SampleDesc.Quality = aa_type.quality;
	tex_desc.MipLevels = 1;
	tex_desc.ArraySize = 1;
	if(isDepthStencilFormat(format))
	{
		tex_desc.BindFlags = D3D10_BIND_DEPTH_STENCIL;
	}
	else
		tex_desc.BindFlags = D3D10_BIND_RENDER_TARGET;
	if(need_texture)
		tex_desc.BindFlags |= D3D10_BIND_SHADER_RESOURCE;
	tex_desc.CPUAccessFlags = 0;
	tex_desc.Usage = D3D10_USAGE_DEFAULT;
	tex_desc.MiscFlags = 0;
	ID3D10Texture2D* texture2D;
	if(FAILED(device->CreateTexture2D(&tex_desc, NULL, &texture2D)))
	{
		lwar << "Cannot create 2D texture with width = " << width << " height = " << height << " aa_type.count = " << aa_type.count << " aa_type.quality = " << aa_type.quality;
		return false;
	};
	if(isDepthStencilFormat(format))
	{
		if(!need_texture)
		{
			if(FAILED(device->CreateDepthStencilView(texture2D, NULL, &depth_stencil_view[0])))
			{
				lwar << "Cannot create depth stencil view with width = " << width << " height = " << height << " aa_type.count = " << aa_type.count << " aa_type.quality = " << aa_type.quality;
				texture2D->Release();
				return false;
			};
		}
		else
		{
			// NEED TO CREATE DEPTH STENCIL VIEW WITH TEXTURE
		};
	}
	else
	{
		if(FAILED(device->CreateRenderTargetView(texture2D, NULL, &render_target_views[0])))
		{
			lwar << "Cannot create render target view with width = " << width << " height = " << height << " aa_type.count = " << aa_type.count << " aa_type.quality = " << aa_type.quality;
			texture2D->Release();
			return false;
		};
		if(need_texture)
		{
			darkstar_texture.reset(new RTTexture());
			ID3D10ShaderResourceView* srv;
			if(aa_type.count == 1)
			{
				if(FAILED(device->CreateShaderResourceView(texture2D, NULL, &srv)))
				{
					lwar << "Cannot create direct3d shader resource view";
					texture2D->Release();
					return false;
				};
				texture_data_without_aa = texture2D;
			}
			else
			{
				tex_desc.SampleDesc.Count = 1;
				tex_desc.SampleDesc.Quality = 0;
				if(FAILED(device->CreateTexture2D(&tex_desc, NULL, &texture_data_without_aa)))
				{
					lwar << "Cannot create texture without multi sampling";
					texture2D->Release();
					return false;
				};
	
				if(FAILED(device->CreateShaderResourceView(texture_data_without_aa, NULL, &srv)))
				{
					lwar << "Cannot create direct3d shader resource view";
					texture2D->Release();
					texture_data_without_aa->Release();
					return false;
				};
			};
			darkstar_texture->setShaderResourceView(srv);
		};
	};
	this->aa_type = aa_type;
	this->format = format;
	this->width = width;
	this->height = height;
	this->need_texture = need_texture;
	layers_number = 1;
	texture_data = texture2D;
	return true;
};

bool RenderTarget::create2D(ID3D10Texture2D* rt_text)
{
	D3D10_TEXTURE2D_DESC desc;
	rt_text->GetDesc(&desc);	
	width = desc.Width;
	height = desc.Height;
	format = getDarkRTFormat(desc.Format);
	aa_type.count = desc.SampleDesc.Count;
	aa_type.quality = desc.SampleDesc.Quality;
	need_texture = false;
	layers_number = 1;
	if(format == DRTFMT_D16_UNORM || format == DRTFMT_D24_UNORM_S8_UINT)
	{
		if(FAILED(GraphicDriver::ptr()->getDevice()->CreateDepthStencilView(rt_text, NULL, &depth_stencil_view[0])))
		{
			lwar << "Cannot create depth stencil view with width = " << width << " height = " << height << " aa_type.count = " << aa_type.count << " aa_type.quality = " << aa_type.quality;
				return false;
		};
	}
	else
	{
		if(FAILED(GraphicDriver::ptr()->getDevice()->CreateRenderTargetView(rt_text, NULL, &render_target_views[0])))
		{
			lwar << "Cannot create render target view with width = " << width << " height = " << height << " aa_type.count = " << aa_type.count << " aa_type.quality = " << aa_type.quality;
			return false;
		};
	};
	return true;
};

bool RenderTarget::createCube(const unsigned int width, const unsigned int height, const DRTFORMAT& format)
{
	ID3D10Device* device = GraphicDriver::ptr()->getDevice();

	D3D10_TEXTURE2D_DESC texture_cube_desc;
	texture_cube_desc.ArraySize = 6;
	if(!isDepthStencilFormat(format))
		texture_cube_desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
	else
		texture_cube_desc.BindFlags = D3D10_BIND_DEPTH_STENCIL;
	texture_cube_desc.CPUAccessFlags = 0;
	texture_cube_desc.Format = getSpecRTFormat(format);
	texture_cube_desc.Height = height;
	texture_cube_desc.Width = width;
	texture_cube_desc.MipLevels = 1;
	texture_cube_desc.MiscFlags = D3D10_RESOURCE_MISC_TEXTURECUBE;
	texture_cube_desc.SampleDesc.Count = 1;
	texture_cube_desc.SampleDesc.Quality = 0;
	texture_cube_desc.Usage = D3D10_USAGE_DEFAULT;

	if(FAILED(device->CreateTexture2D(&texture_cube_desc, NULL, &texture_data)))
	{
		lwar << "Cannot create cube texture with width = " << width << " and height = " << height;
		return false;
	};

	if(!isDepthStencilFormat(format))
	{
		render_target_views.resize(6);
		layers_number = 6;

		D3D10_RENDER_TARGET_VIEW_DESC render_target_view_desc;
		render_target_view_desc.Format = getSpecRTFormat(format);
		render_target_view_desc.ViewDimension = D3D10_RTV_DIMENSION_TEXTURE2DARRAY;
		render_target_view_desc.Texture2DArray.ArraySize = 1;
		render_target_view_desc.Texture2DArray.MipSlice = 0;
		
		for(unsigned int i = 0; i < layers_number; ++i)
		{
			render_target_view_desc.Texture2DArray.FirstArraySlice = i;
			if(FAILED(device->CreateRenderTargetView(texture_data, &render_target_view_desc, &render_target_views[i])))
			{
				lwar << "Cannot create render target view for cube texture";
				return false;
			};
		};

		D3D10_SHADER_RESOURCE_VIEW_DESC shader_resource_view_desc;
		shader_resource_view_desc.Format = getSpecRTFormat(format);
		shader_resource_view_desc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURECUBE;
		shader_resource_view_desc.TextureCube.MipLevels = 1;
		shader_resource_view_desc.TextureCube.MostDetailedMip = 0;

		ID3D10ShaderResourceView* srv;
		if(FAILED(device->CreateShaderResourceView(texture_data, &shader_resource_view_desc, &srv)))
		{
			lwar << "Cannot create shader resource view for cube texture";
			return false;
		};

		darkstar_texture.reset(new RTTexture());
		darkstar_texture->setShaderResourceView(srv);
		texture_data_without_aa = texture_data;
		this->need_texture = true;
	}
	else
	{
		depth_stencil_view.resize(6);
		layers_number = 6;

		D3D10_DEPTH_STENCIL_VIEW_DESC depth_stencil_view_desc;
		depth_stencil_view_desc.Format = getSpecRTFormat(format);
		depth_stencil_view_desc.ViewDimension = D3D10_DSV_DIMENSION_TEXTURE2DARRAY;
		depth_stencil_view_desc.Texture2DArray.ArraySize = 1;
		depth_stencil_view_desc.Texture2DArray.MipSlice = 0;

		for(unsigned int i = 0; i < layers_number; ++i)
		{
			depth_stencil_view_desc.Texture2DArray.FirstArraySlice = i;
			if(FAILED(device->CreateDepthStencilView(texture_data, &depth_stencil_view_desc, &depth_stencil_view[i])))
			{
				lwar << "Cannot create depth stencil view for cube texture";
				return false;
			};
		};
		this->need_texture = false;
	};

	this->aa_type = WITHOUT_AA;
	this->format = format;
	this->width = width;
	this->height = height;

	return true;
};

ID3D10RenderTargetView* RenderTarget::getRenderTargetView(const unsigned int layer_number) const
{
	return render_target_views[layer_number];
};

ID3D10DepthStencilView* RenderTarget::getDepthStencilView(const unsigned int layer_number) const
{
	return depth_stencil_view[layer_number];
};

unsigned int RenderTarget::getWidth() const
{
	return width;
};

unsigned int RenderTarget::getHeight() const
{
	return height;
};

const DRTAATYPE& RenderTarget::getAAType() const
{
	return aa_type;
};

const DRTFORMAT& RenderTarget::getFormat() const
{
	return format;
};

bool RenderTarget::getIsNeedTexture() const
{
	return need_texture;
};
	
ID3D10Texture2D* RenderTarget::getDataTexture() const
{
	return texture_data;
};

ID3D10Texture2D* RenderTarget::getDataWithoutAATexture() const
{
	return texture_data_without_aa;
};

unsigned int RenderTarget::getLayersNumber() const
{
	return layers_number;
};

#endif
