#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <ddraw.h>
#include "../imagelib/cImage.h"
#include "cImgFile.h"

/*
#ifdef _DEBUG
  #pragma comment(lib, "FilePackage\\imagelib\\imagelib_d.lib")
#else
  #pragma comment(lib, "FilePackage\\imagelib\\imagelib.lib")
#endif *//*_DEBUG*/

cImgFile::cImgFile() : cFileObj()
{
	m_fileType		= FILE_TYPE_IMAGE;		// 圖形格式
	m_imageFormat	= IMAGE_UNKNOWN;		// 圖形格式
	m_width			= 0;					// 圖形寬度
	m_height		= 0;					// 圖形高度
	m_mipmapLevels	= 0;					// mipmap數量 (0或1都代表沒有mipmap)
}

cImgFile::~cImgFile()
{
	Destroy();
}

void cImgFile::Destroy()
{
	cFileObj::Destroy();
	m_imageFormat	= IMAGE_UNKNOWN;		// 圖形格式
	m_width			= 0;					// 圖形寬度
	m_height		= 0;					// 圖形高度
	m_mipmapLevels	= 0;					// mipmap數量 (0或1都代表沒有mipmap)
}

bool cImgFile::LoadFromFile( const TCHAR *workDirStr, const TCHAR *fileNameStr, CompressFormat format, void *pSpecialData )
{
	Destroy();

	TCHAR fullNameStr[MAX_PATH];
	_stprintf( fullNameStr, "%s%s", workDirStr, fileNameStr );
	if ( !Read( fullNameStr, (ReadImageOptions*)pSpecialData ) ) return false;

	m_fileType			= FILE_TYPE_IMAGE;
	m_compressFormat	= COMPRESS_FMT_NORMAL;
	SetFileName( fileNameStr );
	ConvertFormat( format );			// 轉換為指定壓縮格式

	return true;
}

bool cImgFile::Read( const TCHAR *fileNameStr, ReadImageOptions *pSpecialData )
{
	assert( fileNameStr );

	cImage img;
	if ( !img.Load( fileNameStr ) )
	{
		if ( ReadDDS( fileNameStr, pSpecialData ) )
			return true;
		return false;
	}	
	
	ReadImageOptions info;
	if ( pSpecialData ) info = *pSpecialData;
	if ( img.GetFormat() != PF_A8R8G8B8 ) img.ConvertFormat( PF_A8R8G8B8 );
	
	m_width	= img.GetXSize();
	m_height = img.GetYSize();

	switch ( info.fmt )
	{
		case IMAGE_DXT1:
		case IMAGE_DXT1a:
		case IMAGE_DXT3:
		case IMAGE_DXT5:
		{
			CompressionOptions options;
			if ( !info.mipmapBool ) options.mipMapGeneration = kNoMipMaps;
			if ( info.fmt == IMAGE_DXT1 ) options.textureFormat = kDXT1;
			else if ( info.fmt == IMAGE_DXT1a ) options.textureFormat = kDXT1a;
			else if ( info.fmt == IMAGE_DXT3 ) options.textureFormat = kDXT3;
			else if ( info.fmt == IMAGE_DXT5 ) options.textureFormat = kDXT5;
			m_pData = CompressDXT( img.GetPixels(), m_width, m_height, m_dataSize, m_mipmapLevels, &options );
			m_imageFormat = info.fmt;
			break;
		}
		case IMAGE_R5G6B5:
		case IMAGE_A1R5G5B5:
		{
			m_dataSize = m_width * m_height * 2;
			m_pData = new BYTE[m_dataSize];
			m_imageFormat = info.fmt;
			Transfrom( m_pData, m_imageFormat, 0, 0, m_width, m_height, img.GetPixels(), m_width, m_height );
			break;
		}
		case IMAGE_R8G8B8:
		{
			m_dataSize = m_width * m_height * 3;
			m_pData = new BYTE[m_dataSize];
			m_imageFormat = info.fmt;
			Transfrom( m_pData, m_imageFormat, 0, 0, m_width, m_height, img.GetPixels(), m_width, m_height );
			break;
		}
		default:
		{
			m_dataSize = m_width * m_height * 4;
			m_pData = new BYTE[m_dataSize];
			m_imageFormat = IMAGE_A8R8G8B8;
			Transfrom( m_pData, m_imageFormat, 0, 0, m_width, m_height, img.GetPixels(), m_width, m_height );
			break;
		}
	}

	img.Destroy();

	return true;
}

bool cImgFile::ReadDDS( const TCHAR *fileNameStr, ReadImageOptions *pSpecialData )
{
	FILE *fptr;	
	if ( (fptr = fopen( fileNameStr, "rb" )) == NULL )
		return false;

	int magicNumber;
	fread( &magicNumber, sizeof(int), 1, fptr );
	if ( memcmp(&magicNumber, "DDS ", 4) != 0 )
	{
		fclose( fptr );
		return false;
	}

	DDSURFACEDESC2 ddsd;
	fread( &ddsd, sizeof(DDSURFACEDESC2), 1, fptr );

	int size		= 0;
	m_width			= (int)ddsd.dwWidth;
	m_height		= (int)ddsd.dwHeight;
	m_mipmapLevels	= (int)ddsd.dwMipMapCount;

	if ( m_mipmapLevels <= 0 )
		 m_mipmapLevels = 1;
	else
	{
		int max = GetMipmapCount( m_width, m_height );
		if ( m_mipmapLevels > max )
			m_mipmapLevels = max;
	}
	
	int w = max( MIN_UNIT, ddsd.dwWidth );
	int h = max( MIN_UNIT, ddsd.dwHeight );
	for (int i = 0; i < m_mipmapLevels; i++ )
	{
		size += w * h;
		if ( w > MIN_UNIT ) w >>= 1;
		if ( h > MIN_UNIT ) h >>= 1;
	}

	if ( size <= 0 )
	{
		fclose( fptr );
		return false;
	}

	switch ( ddsd.ddpfPixelFormat.dwFourCC )
	{
	case DXT_1:
		size /= 2;
		m_imageFormat = IMAGE_DXT1a;
		break;

	case DXT_3:
		m_imageFormat = IMAGE_DXT3;
		break;

	case DXT_5:
		m_imageFormat = IMAGE_DXT5;
		break;

	default:
		fclose( fptr );
		return false;
	}
	
	m_dataSize = size;
	m_pData = new BYTE[m_dataSize];
	fread( m_pData, m_dataSize, 1, fptr );

	fclose( fptr );
	return true;
}

bool cImgFile::BltToRGB( BYTE *pDest, ImageFormat format, int x, int y, int width, int height )
{
	if ( pDest == NULL || m_pData == NULL ) return false;
	if ( format != IMAGE_R5G6B5 || format != IMAGE_A1R5G5B5 || format != IMAGE_R8G8B8 || format != IMAGE_A8R8G8B8 ) return false;

	RGBQUAD *pPixels = (RGBQUAD*)m_pData;
	RGBQUAD *pNew = NULL;
	if ( m_imageFormat != IMAGE_A8R8G8B8 ) {
		pNew = new RGBQUAD[m_width * m_height];
		switch ( m_imageFormat )
		{
			case IMAGE_R5G6B5:
			{
				WORD *pSrc = (WORD*)m_pData;
				for ( int i = 0; i < m_width * m_height; i++ ) {
					pNew[i].rgbReserved	= 0xFF;
					pNew[i].rgbBlue		= g_5BitsTo8BitsArray[pSrc[i] & 0x1F];
					pNew[i].rgbGreen	= g_6BitsTo8BitsArray[(pSrc[i] >> 5) & 0x3F];
					pNew[i].rgbRed		= g_5BitsTo8BitsArray[(pSrc[i] >> 11) & 0x1F];
				}
				break;
			}
			case IMAGE_A1R5G5B5:
			{
				WORD *pSrc = (WORD*)m_pData;
				for ( int i = 0; i < m_width * m_height; i++ ) {
					pNew[i].rgbReserved	= ( pSrc[i] & 0x8000 ) ? 0xFF : 0;
					pNew[i].rgbBlue		= g_5BitsTo8BitsArray[pSrc[i] & 0x1F];
					pNew[i].rgbGreen	= g_5BitsTo8BitsArray[(pSrc[i] >> 5) & 0x1F];
					pNew[i].rgbRed		= g_5BitsTo8BitsArray[(pSrc[i] >> 10) & 0x1F];
				}
				break;
			}
			case IMAGE_R8G8B8:
			{
				BGRCOLOR *pSrc = (BGRCOLOR*)m_pData;
				for ( int i = 0; i < m_width * m_height; i++ ) {
					pNew[i].rgbBlue		= pSrc[i].r;
					pNew[i].rgbGreen	= pSrc[i].g;
					pNew[i].rgbRed		= pSrc[i].r;
					pNew[i].rgbReserved	= 0xFF;
				}
				break;
			}
			case IMAGE_DXT1a:
			case IMAGE_DXT1:
				PlotDXT1( (BYTE*)pNew, m_pData, m_width, m_height );
				break;
			case IMAGE_DXT3:
				PlotDXT3( (BYTE*)pNew, m_pData, m_width, m_height );
				break;
			case IMAGE_DXT5:
				PlotDXT5( (BYTE*)pNew, m_pData, m_width, m_height );
				break;
		}
		pPixels = pNew;
	}

	Transfrom( pDest, format, x, y, width, height, pPixels, m_width, m_height );
	if ( pNew ) delete [] pNew;

	return true;
}

inline DWORD TransferFormatType( ImageFormat format )
{
	if ( format == IMAGE_DXT1 ) return kDXT1;		
	else if ( format == IMAGE_DXT1a ) return kDXT1a;
	else if ( format == IMAGE_DXT3 ) return kDXT3;
	else if ( format == IMAGE_DXT5 ) return kDXT5;
	return format;
}

bool cImgFile::BltToDXT( void *pDest, ImageFormat format, int x, int y, int width, int height, int mipmapLevels, DWORD color, DWORD rop )
{
	if ( pDest == NULL || m_pData == NULL || !IsDXTFormat(format) || !IsDXTFormat(m_imageFormat) ) return false;
	
	int pow_w = GetPower2( m_width );
	int pow_h = GetPower2( m_height );
	BYTE *pSrc = m_pData;
	int size = 0;
	if ( mipmapLevels > 0 ) {
		int w = max( MIN_UNIT, pow_w );
		int h = max( MIN_UNIT, pow_h );
		for ( int levels = min( mipmapLevels, m_mipmapLevels ); levels > 0; levels-- ) {
			size += w * h;
			if ( w > MIN_UNIT ) w >>= 1;
			if ( h > MIN_UNIT ) h >>= 1;
			if ( pow_w > MIN_UNIT ) pow_w >>= 1;
			if ( pow_h > MIN_UNIT ) pow_h >>= 1;
		}
	}
	if ( m_imageFormat == IMAGE_DXT1 || m_imageFormat == IMAGE_DXT1a ) size /= 2;
	pSrc += size;

	return BltDXT( pDest, TransferFormatType(format), x, y, width, height, pSrc, TransferFormatType(m_imageFormat), pow_w, pow_h, color, rop );
}

void cImgFile::SaveHeader( cListData &header )
{
	cFileObj::SaveHeader( header );	
	header.Push( &m_imageFormat, sizeof(ImageFormat) );
	header.Push( &m_width, sizeof(int) );
	header.Push( &m_height, sizeof(int) );
	header.Push( &m_mipmapLevels, sizeof(int) );
}

void cImgFile::LoadHeader( HANDLE hFile )
{
	DWORD readBytesOut;
	cFileObj::LoadHeader( hFile );
	if ( m_fileType == FILE_TYPE_IMAGE ) {
		ReadFile( hFile, &m_imageFormat, sizeof(ImageFormat), &readBytesOut, NULL );
		ReadFile( hFile, &m_width, sizeof(int), &readBytesOut, NULL );
		ReadFile( hFile, &m_height, sizeof(int), &readBytesOut, NULL );
		ReadFile( hFile, &m_mipmapLevels, sizeof(int), &readBytesOut, NULL );
	}
}

bool cImgFile::Transfrom( void *pDest, ImageFormat format, int x, int y, int width, int height, void *pSrc, int srcWidth, int srcHeight)
{
	RGBQUAD *pPixels = (RGBQUAD *)pSrc;
	switch ( format )
	{
		case IMAGE_R5G6B5:
		case IMAGE_A1R5G5B5:
		{
			WORD *pLine = (WORD*)pDest + y * width;
			for ( int yy = 0; yy < srcHeight; yy++ ) {
				if ( yy + y >= height ) break;
				for ( int xx = 0; xx < srcWidth; xx++ ) {
					if ( xx + x >= width ) break;
					if ( format == IMAGE_R5G6B5 ) {
						pLine[xx + x] = RGBToR5G6B5( pPixels[xx].rgbRed, pPixels[xx].rgbGreen, pPixels[xx].rgbBlue );
					} else if ( format == IMAGE_A1R5G5B5 ) {
						pLine[xx + x] = RGBToR5G6B5( pPixels[xx].rgbRed, pPixels[xx].rgbGreen, pPixels[xx].rgbBlue );
						if ( pPixels[xx].rgbReserved & 0x80 ) pLine[xx + x] |= 0x8000;
					}
				}
				pPixels += srcWidth;
				pLine += width;
			}
			break;
		}
		case IMAGE_R8G8B8:
		{
			BGRCOLOR *pLine = (BGRCOLOR*)pDest + y * width;
			for ( int yy = 0; yy < srcHeight; yy++ ) {
				if ( yy + y >= height ) break;
				for ( int xx = 0; xx < srcWidth; xx++ ) {
					if ( xx + x >= width ) break;
					pLine[xx].r = pPixels[xx].rgbBlue;
					pLine[xx].g = pPixels[xx].rgbGreen;
					pLine[xx].b = pPixels[xx].rgbRed;
				}
				pPixels += srcWidth;
				pLine += width;				
			}
			break;
		}
		case IMAGE_A8R8G8B8:
		{
			RGBQUAD* pLine = (RGBQUAD*)pDest + y * width;
			for ( int yy = 0; yy < srcHeight; yy++ ) {
				if ( yy + y >= height ) break;
				memcpy( pLine + x, pPixels, sizeof(RGBQUAD) * min(width - x, srcWidth) );
				pLine += width;
				pPixels += srcWidth;
			}
			break;
		}
		default:
			return false;
	}
	return true;
}
