#include "dxtexture.h"
#include "texturemanager.h"
#include "devicestatemanager.h"

DXSurface::DXSurface(TextureManager* const manager, const unsigned miplevels, const unsigned usage, const FORMAT format, const POOL pool, const XString& filename, const DXSURFACETYPE type) : m_pManager(manager), m_Type(type)
{
	m_pSurface = NULL;

	m_MipLevels = miplevels;
	m_Usage = usage;
	m_Format = format;
	m_Pool = pool;
	m_strFileName = filename;

	m_MemoryUsage = 0;
}

DXSurface::~DXSurface(void)
{
	m_pManager->MemoryReleased(m_MemoryUsage, m_Pool);
	m_pManager->Release(this);

	if(m_Pool == POOL_DEFAULT)
	{
	//	wlog("Release Surface: ID:%d Type=%d Pool=%d Format=%d Usage=%d Memory=%d", (unsigned)this, m_Type, m_Pool, m_Format, m_Usage, m_pSurface);
	}

	SAFERELEASE(m_pSurface);
}
 
void DXSurface::DoInvalidate(void)
{
	if(m_Pool != D3DPOOL_DEFAULT)
		return ;

	//wlog("Invalidate Surface: ID:%d Type=%d Pool=%d Format=%d Usage=%d Memory=%d", (unsigned)this, m_Type, m_Pool, m_Format, m_Usage, m_pSurface);

	SAFERELEASE(m_pSurface);
}

void DXSurface::UpdateMem(const unsigned xl, const unsigned yl, const unsigned zl)
{
	unsigned bytes;

	if (m_Format == FMT_L8)
		bytes = 1;
	else if (m_Format == FMT_R16F)
		bytes = 2;
	else if (m_Format == FMT_A8R8G8B8 || m_Format == FMT_R32F || m_Format == FMT_G16R16)
		bytes = 4;
	else if (m_Format == FMT_A16B16G16R16F)
		bytes = 8;
	else if (m_Format == FMT_A32B32G32R32F)
		bytes = 16;
	else
		bytes = 4; //just use 4 in case we format is unknown for this calculation

	unsigned rowbytes = bytes * xl;
	rowbytes = ((rowbytes + 3) / 4) * 4; //round up to 32bit alignment
	
	unsigned l0size = rowbytes * yl * zl;

	m_MemoryUsage = (m_MipLevels == 1 ? l0size : l0size * 4 / 3);

	m_pManager->MemoryAllocated(m_MemoryUsage, m_Pool);

	//perload hack
	//if (m_Pool == POOL_MANAGED && m_pSurface)
	//	m_pSurface->PreLoad();

	if(m_Pool == POOL_DEFAULT)
	{
	//	wlog("Create Surface: ID:%d Type=%d Pool=%d Format=%d Usage=%d Memory=%d", (unsigned)this, m_Type, m_Pool, m_Format, m_Usage, m_pSurface);
	}
}

DXTexture::DXTexture(TextureManager* const manager, const unsigned xl, const unsigned yl, const unsigned levels, const unsigned usage, const FORMAT format, const POOL pool) : DXSurface(manager, levels, usage, format, pool, "", DXST_TEXTURE)
{
	m_Xl = xl;
	m_Yl = yl;

	ZeroMemory(&m_LockedData, sizeof(m_LockedData));

	HRESULT hr;
	if(FAILED(hr = m_pManager->GetDevice()->GetDevice()->CreateTexture(m_Xl, m_Yl, m_MipLevels, m_Usage, (D3DFORMAT)m_Format, (D3DPOOL)m_Pool, (LPDIRECT3DTEXTURE9*)&m_pSurface, NULL)))
	{
		wlog("Error creating texture: %r", hr);
	}
		
	m_bInvalidated = false;

	UpdateMem(m_Xl, m_Yl, 1);
}

DXTexture::DXTexture(TextureManager* const manager, const XString& filename, const unsigned xl, const unsigned yl, const unsigned levels, const unsigned usage, const FORMAT format, const POOL pool) : DXSurface(manager, levels, usage, format, pool, filename, DXST_TEXTURE)
{
	m_Xl = xl;
	m_Yl = yl;

	ZeroMemory(&m_LockedData, sizeof(m_LockedData));

	D3DXCreateTextureFromFileEx(m_pManager->GetDevice()->GetDevice(), m_strFileName.getbuffer(), m_Xl, m_Yl, m_MipLevels, m_Usage, (D3DFORMAT)m_Format, (D3DPOOL)m_Pool, D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, (LPDIRECT3DTEXTURE9*)&m_pSurface);

	if((m_Xl == D3DX_DEFAULT || m_Yl == D3DX_DEFAULT || m_Xl == 0 || m_Yl == 0 || m_Xl == D3DX_DEFAULT_NONPOW2 || m_Yl == D3DX_DEFAULT_NONPOW2) && m_pSurface)
	{
		D3DSURFACE_DESC desc;
		GetTextureSurface()->GetLevelDesc(0,&desc);
		m_Xl = desc.Width;
		m_Yl = desc.Height;
	}

	m_bInvalidated = false;

	UpdateMem(m_Xl, m_Yl, 1);
}

/*DXTexture::DXTexture(TextureManager* const manager, const XString& filename, const FORMAT format, const POOL pool) : m_Manager(manager)
{
	m_Filename = filename;
	m_Format = format;
	m_Pool = pool;

	m_Xl = 0;
	m_Yl = 0;
	m_Levels = 1;
	m_Usage = 0;

	m_Surface = NULL;
	ZeroMemory(&m_LockedData, sizeof(m_LockedData));
	
	D3DXCreateTextureFromFileEx(m_Manager->GetDevice()->GetDevice(),m_Filename.getbuffer(),m_Xl,m_Yl,m_Levels,m_Usage,(D3DFORMAT)m_Format,(D3DPOOL)m_Pool,D3DX_DEFAULT,D3DX_DEFAULT,0,NULL,NULL,&m_Surface);

	if((m_Xl == D3DX_DEFAULT || m_Yl == D3DX_DEFAULT || m_Xl == 0 || m_Yl == 0) && m_Surface)
	{
		D3DSURFACE_DESC desc;
		m_Surface->GetLevelDesc(0,&desc);
		m_Xl = desc.Width;
		m_Yl = desc.Height;
	}

	m_bInvalidated = false;
}*/

DXTexture::DXTexture(TextureManager* const manager, DXTexture* const tex, const unsigned xl, const unsigned yl, const unsigned levels, const unsigned usage, const FORMAT format, const POOL pool) : DXSurface(manager, levels, usage, format, pool, tex->GetFilename(), DXST_TEXTURE)
{
	m_Xl = xl;
	m_Yl = yl;

	ZeroMemory(&m_LockedData, sizeof(m_LockedData));

	HRESULT hr;
	if(FAILED(hr = m_pManager->GetDevice()->GetDevice()->CreateTexture(m_Xl, m_Yl, m_MipLevels, m_Usage, (D3DFORMAT)m_Format, (D3DPOOL)m_Pool, (LPDIRECT3DTEXTURE9*)&m_pSurface, NULL)))
	{
		wlog("Error creating texture: %r",hr);
	}
	
	//TODO: implement proper scaling
	if(m_pSurface)
	{
		D3DLOCKED_RECT lrDst, lrSrc;

		GetTextureSurface()->LockRect(0, &lrDst, NULL, 0);
		tex->GetTextureSurface()->LockRect(0, &lrSrc, NULL, D3DLOCK_READONLY);

		for(unsigned l = 0; l < m_Yl; l++);
			memcpy(lrDst.pBits, lrSrc.pBits, _min(lrDst.Pitch, lrSrc.Pitch));
		
		GetTextureSurface()->UnlockRect(0);
		tex->GetTextureSurface()->UnlockRect(0);
	}
		
	m_bInvalidated = false;

	UpdateMem(m_Xl, m_Yl, 1);
}

void DXTexture::DoRestore(void)
{
	if(m_Pool != D3DPOOL_DEFAULT)
		return ;

	wlog("Restore Texture: ID:%d Type=%d Pool=%d Format=%d Usage=%d Memory=%d", (unsigned)this, m_Type, m_Pool, m_Format, m_Usage, m_pSurface);

	if(m_strFileName == "")
	{
		m_pManager->GetDevice()->GetDevice()->CreateTexture(m_Xl, m_Yl, m_MipLevels, m_Usage, (D3DFORMAT)m_Format, (D3DPOOL)m_Pool, (LPDIRECT3DTEXTURE9*)&m_pSurface, NULL);
	}
	else
	{
		D3DXCreateTextureFromFileEx(m_pManager->GetDevice()->GetDevice(),m_strFileName.getbuffer(),m_Xl,m_Yl,m_MipLevels,m_Usage,(D3DFORMAT)m_Format,(D3DPOOL)m_Pool,D3DX_DEFAULT,D3DX_DEFAULT,0,NULL,NULL,(LPDIRECT3DTEXTURE9*)&m_pSurface);
	}
}

const LOCKED_RECT* DXTexture::Lock(void)
{
	if(!m_LockedData.pBits)
		GetTextureSurface()->LockRect(0, (D3DLOCKED_RECT*)&m_LockedData, NULL, 0);
	return &m_LockedData;
}

void DXTexture::Unlock(void)
{
	ZeroMemory(&m_LockedData, sizeof(m_LockedData));
	GetTextureSurface()->UnlockRect(0);
}

DXCubeTexture::DXCubeTexture(TextureManager* const manager, const unsigned edgelength, const unsigned levels, const unsigned usage, const FORMAT format, const POOL pool) :
					DXSurface(manager, levels, usage, format, pool, "", DXST_CUBETEXTURE)
{
	m_EdgeLength = edgelength;
	
	HRESULT hr;
	if(FAILED(hr = m_pManager->GetDevice()->GetDevice()->CreateCubeTexture(m_EdgeLength, m_MipLevels, m_Usage, (D3DFORMAT)m_Format, (D3DPOOL)m_Pool, (LPDIRECT3DCUBETEXTURE9*)&m_pSurface, NULL)))
	{
		wlog("Error creating cubetexture: %r", hr);
	}
		
	m_bInvalidated = false;

	UpdateMem(m_EdgeLength, m_EdgeLength, 6);
}

DXCubeTexture::DXCubeTexture(TextureManager* const manager, const XString& filename, const unsigned edgelength, const unsigned levels, const unsigned usage, const FORMAT format, const POOL pool) :
					DXSurface(manager, levels, usage, format, pool, filename, DXST_CUBETEXTURE)
{
	m_EdgeLength = edgelength;

	D3DXCreateCubeTextureFromFileEx(m_pManager->GetDevice()->GetDevice(), m_strFileName.getbuffer(), m_EdgeLength, m_MipLevels, m_Usage, (D3DFORMAT)m_Format, (D3DPOOL)m_Pool, D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, (LPDIRECT3DCUBETEXTURE9*)&m_pSurface);

	if((m_EdgeLength == D3DX_DEFAULT || m_EdgeLength == 0) && m_pSurface)
	{
		D3DSURFACE_DESC desc;
		GetCubeTexture()->GetLevelDesc(0,&desc);
		m_EdgeLength = desc.Width;
	}

	m_bInvalidated = false;

	UpdateMem(m_EdgeLength, m_EdgeLength, 6);
}

void DXCubeTexture::DoRestore(void)
{
	if(m_Pool != D3DPOOL_DEFAULT)
		return ;

	if(m_strFileName == "")
	{
		m_pManager->GetDevice()->GetDevice()->CreateCubeTexture(m_EdgeLength, m_MipLevels, m_Usage, (D3DFORMAT)m_Format, (D3DPOOL)m_Pool, (LPDIRECT3DCUBETEXTURE9*)&m_pSurface, NULL);
	}
	else
	{
		D3DXCreateCubeTextureFromFileEx(m_pManager->GetDevice()->GetDevice(), m_strFileName.getbuffer(), m_EdgeLength, m_MipLevels, m_Usage, (D3DFORMAT)m_Format, (D3DPOOL)m_Pool, D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, (LPDIRECT3DCUBETEXTURE9*)&m_pSurface);
	}
}