///////////////////////////////////////////////////////////////////////////////
/// @file deBitmap_DXTC.cpp
///
/// @brief DXTC compressed texture handling
///
/// @author Assassin
///
/// This file is the intellectual property of Novus Delta, LLC.. Usage of the
/// contents of this file is subject to the Destiny3D Member License which
/// can be found at http://www.destiny3d.com.  Any other usage is prohibited.
///
/// This file is distributed "AS IS" without warranty of any kind.  Novus
/// Delta, LLC. does not guarantee the fitness of the contents of this file
/// for any particular purpose.
///
/// Copyright (C) 2001-2003 Novus Delta, LLC. All Rights Reserved.
///
/// <hr>
///                                 Change History
/// <hr>
///
/// @date Jan 2004
/// @author Assassin
/// @remarks Creation
///
///////////////////////////////////////////////////////////////////////////////

// code within is adapted from nVidia DXTC Decompression code

#include "deBitmap_priv.hpp"

static const int LOW_5		= 0x001F;
static const int MID_6		= 0x07E0;
static const int HIGH_5		= 0xF800;

static const int MID_555	= 0x03E0;
static const int HI_555		= 0x7C00;

deBoolean DecompressDXTC(const void* source, void* target,
						 long Width, long Height, long TP,
						 IdeDriver::BPP SFormat, IdeDriver::BPP TFormat)
{
	if (SFormat < IdeDriver::BPP_DXT1 || SFormat > IdeDriver::BPP_DXT5)
		DEBUGRETURN(deFALSE, "DecompressDXTC : source format is not DXTC");
	if (TFormat <= IdeDriver::BPP_UNKNOWN || TFormat >= IdeDriver::BPP_COUNT)
		DEBUGRETURN(deFALSE, "DecompressDXTC : invalid format for target texture");
	if (TFormat >= IdeDriver::BPP_DXT1 && TFormat <= IdeDriver::BPP_DXT5)
		DEBUGRETURN(deFALSE, "DecompressDXTC : cannot convert to DXTC format");
	
#ifdef DEBUG
	if( (Width % 4 ) != 0 )
	{
		DEBUGMSG1("DecompressDXTC: Width not div by 4!  %d\n", target->m_Width );
	}
	if( (Height % 4) != 0 )
	{
		DEBUGMSG1("DecompressDXTC: Height not div by 4! %d\n", target->m_Height );
	}
#endif 
	
	switch(SFormat)
	{
	case IdeDriver::BPP_DXT1 :
		if (!DecompressDXT1(source, target, Width, Height, TP, TFormat))
			return deFALSE;
		break;
	case IdeDriver::BPP_DXT2 :
		if (!DecompressDXT2(source, target, Width, Height, TP, TFormat))
			return deFALSE;
		break;
	case IdeDriver::BPP_DXT3 :
		if (!DecompressDXT3(source, target, Width, Height, TP, TFormat))	
			return deFALSE;
		break;
	case IdeDriver::BPP_DXT4 :
		if (!DecompressDXT4(source, target, Width, Height, TP, TFormat))
			return deFALSE;
		break;
	case IdeDriver::BPP_DXT5 :
		if (!DecompressDXT5(source, target, Width, Height, TP, TFormat))		
			return deFALSE;
		break;
	default :
		return deFALSE; // shouldn't ever happen, due to format check above
	}

	return deTRUE;
}

struct DXTColBlock
{
	u16 col0;
	u16 col1;
	
	// no bit fields - use bytes
	u8 row[4];
};

struct DXTAlphaBlockExplicit
{
	u16 row[4];
};

struct DXTAlphaBlock3BitLinear
{
	u8 alpha0;
	u8 alpha1;
	
	u8 stuff[6];
};

// use cast to struct instead of RGBA_MAKE as struct is
//  much
struct Color8888
{	// change the order of names to change the order of the output ARGB or BGRA, etc...
	//  Last one is MSB, 1st is LSB. (on little-endian, at least)
	u8 b;		
	u8 g;
	u8 r;
	u8 a;
};


struct Color565
{
	unsigned nBlue  : 5;		// order of names changes
	unsigned nGreen : 6;		//  byte order of output to 32 bit
	unsigned nRed	: 5;
};

#pragma warning (push)
#pragma warning (disable : 4244)
static inline void GetColorBlockColors(DXTColBlock * pBlock,
									   deARGB * col_0, deARGB * col_1, 
									   deARGB * col_2, deARGB * col_3)
{
	// There are 4 methods to use - see the Time_ functions.
	// 1st = shift = does normal approach per byte for color comps
	// 2nd = use freak variable bit field color565 for component extraction
	// 3rd = use super-freak DWORD adds BEFORE shifting the color components
	//  This lets you do only 1 add per color instead of 3 BYTE adds and
	//  might be faster
	// Call RunTimingSession() to run each of them & output result to txt file
	
	
	// freak variable bit structure method
	// normal math
	// This method is fastest
	
	Color565 * pCol;
	u16 wrd;
	Color8888 newCol_0, newCol_1;
	Color8888 newCol_2, newCol_3;
	
	pCol = (Color565*) & (pBlock->col0 );
	
	newCol_0.a = 0xff;
	newCol_0.r = pCol->nRed;
	newCol_0.r <<= 3;				// shift to full precision
	newCol_0.g = pCol->nGreen;
	newCol_0.g <<= 2;
	newCol_0.b = pCol->nBlue;
	newCol_0.b <<= 3;
	
	pCol = (Color565*) & (pBlock->col1 );
	newCol_1.a = 0xff;
	newCol_1.r = pCol->nRed;
	newCol_1.r <<= 3;				// shift to full precision
	newCol_1.g = pCol->nGreen;
	newCol_1.g <<= 2;
	newCol_1.b = pCol->nBlue;
	newCol_1.b <<= 3;
	
	
	if( pBlock->col0 > pBlock->col1 )
	{
		// Four-color block: derive the other two colors.    
		// 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3
		// These two bit codes correspond to the 2-bit fields 
		// stored in the 64-bit block.
		
		wrd = ((u16)newCol_0.r * 2 + (u16)newCol_1.r )/3;
		// no +1 for rounding
		// as bits have been shifted to 888
		newCol_2.r = (u8)wrd;
		
		wrd = ((u16)newCol_0.g * 2 + (u16)newCol_1.g )/3;
		newCol_2.g = (u8)wrd;
		
		wrd = ((u16)newCol_0.b * 2 + (u16)newCol_1.b )/3;
		newCol_2.b = (u8)wrd;
		newCol_2.a = 0xff;
		
		wrd = ((u16)newCol_0.r + (u16)newCol_1.r *2 )/3;
		newCol_3.r = (u8)wrd;
		
		wrd = ((u16)newCol_0.g + (u16)newCol_1.g *2 )/3;
		newCol_3.g = (u8)wrd;
		
		wrd = ((u16)newCol_0.b + (u16)newCol_1.b *2 )/3;
		newCol_3.b = (u8)wrd;
		newCol_3.a = 0xff;
		
	}
	else
	{
		// Three-color block: derive the other color.
		// 00 = color_0,  01 = color_1,  10 = color_2,  
		// 11 = transparent.
		// These two bit codes correspond to the 2-bit fields 
		// stored in the 64-bit block. 
		
		// explicit for each component, unlike some refrasts...
		
		// TRACE("block has alpha\n");
		
		wrd = ((u16)newCol_0.r + (u16)newCol_1.r )/2;
		newCol_2.r = (u8)wrd;
		wrd = ((u16)newCol_0.g + (u16)newCol_1.g )/2;
		newCol_2.g = (u8)wrd;
		wrd = ((u16)newCol_0.b + (u16)newCol_1.b )/2;
		newCol_2.b = (u8)wrd;
		newCol_2.a = 0xff;
		
		newCol_3.r = 0x00;		// random color to indicate alpha
		newCol_3.g = 0xff;
		newCol_3.b = 0xff;
		newCol_3.a = 0x00;
		
	}
	*col_0 = *(deARGB*)&newCol_0;
	*col_1 = *(deARGB*)&newCol_1;
	*col_2 = *(deARGB*)&newCol_2;
	*col_3 = *(deARGB*)&newCol_3;
}			//  Get color block colors (...)

static inline void DecodeColorBlock(u8 * pImPos, const DXTColBlock * pColorBlock, int TP,
									IdeDriver::BPP format, int bytedepth,
									deARGB col_0, deARGB col_1,
									deARGB col_2, deARGB col_3 )
{
	// width is width of image in pixels
	
	
	DWORD bits;
	int r,n;
	
	// bit masks = 00000011, 00001100, 00110000, 11000000
	const DWORD masks[] = { 3, 3 << 2, 3 << 4, 3 << 6 };
	const int   shift[] = { 0, 2, 4, 6 };
	
	// r steps through lines in y
	for( r=0; r < 4; r++, pImPos += TP-4*bytedepth )
	{
		
		// width * bytedepth bytes per pixel per line
		// each j dxtc row is 4 lines of pixels
		
		// pImPos = (DWORD*)((DWORD)pBase + i*16 + (r+j*4) * m_nWidth * 4 );
		
		// n steps through pixels
		for( n=0; n < 4; n++ )
		{
			bits =		pColorBlock->row[r] & masks[n];
			bits >>=	shift[n];
			
			switch( bits )
			{
			case 0 :
				SetColorIn(pImPos, format, deMake_Color(col_0));
				//*pImPos = *col_0;
				pImPos += bytedepth;
				break;
			case 1 :
				SetColorIn(pImPos, format, deMake_Color(col_1));
				//*pImPos = *col_1;
				pImPos += bytedepth;
				break;
			case 2 :
				SetColorIn(pImPos, format, deMake_Color(col_2));
				//*pImPos = *col_2;
				pImPos += bytedepth;
				break;
			case 3 :
				SetColorIn(pImPos, format, deMake_Color(col_3));
				//*pImPos = *col_3;
				pImPos += bytedepth;
				break;
			default:
				DEBUGMSG1("Your logic is jacked! bits == 0x%x\n", bits );
				pImPos++;
				break;
			}
		}
	}
}

static inline void DecodeAlphaExplicit(u8 * pImPos, IdeDriver::BPP format, int bytedepth,
									   DXTAlphaBlockExplicit * pAlphaBlock, int TP)
{
	// alphazero is a bit mask that when & with the image color
	//  will zero the alpha bits, so if the image DWORDs  are
	//  ARGB then alphazero will be 0x00ffffff or if
	//  RGBA then alphazero will be 0xffffff00
	//  alphazero constructed automaticaly from field order of Color8888 structure
	
	// decodes to 32 bit format only
	
	
	int row, pix;
	
	WORD wrd;
	
	deColor col;
	int thisalpha;
	
	//TRACE("\n");
	
	for( row=0; row < 4; row++, pImPos += TP-4*bytedepth )
	{
		// pImPow += pImPos += width-4 moves to next row down
		
		wrd = pAlphaBlock->row[ row ];
		
		// TRACE("0x%.8x\t\t", wrd);
		
		for( pix = 0; pix < 4; pix++ )
		{
			// zero the alpha bits of image pixel
			GetColorIn(pImPos, format, col);
			
			thisalpha = wrd & 0x000f;		// get only low 4 bits
			//			col.a <<= 4;				// shift to full byte precision
			// NOTE:  with just a << 4 you'll never have alpha
			// of 0xff,  0xf0 is max so pure shift doesn't quite
			// cover full alpha range.
			// It's much cheaper than divide & scale though.
			// To correct for this, and get 0xff for max alpha,
			//  or the low bits back in after left shifting
			// This allows max 4 bit alpha to be 0xff alpha
			//  in final image, and is crude approach to full 
			//  range scale

			col.a = (thisalpha | (thisalpha << 4 )) / 255.0f;
			
			SetColorIn(pImPos, format, col);
			
			wrd >>= 4;		// move next bits to lowest 4
			
			pImPos += bytedepth;		// move to next pixel in the row
		}
	}
}
#pragma warning (pop)

deBoolean DecompressDXT1(const void* source, void* target,
						 long Width, long Height, long TP,
						 IdeDriver::BPP TFormat)
{
	// This was hacked up pretty quick & slopily
	// decompresses to 32 bit format 0xARGB

	int xblocks, yblocks;
	int TByteDepth;

	xblocks = Width / 4;
	yblocks = Height / 4;
	TByteDepth = BPPByteDepth(TFormat);

	int i,k;

	const u8 * pCompBytes = (u8*)source;
	u8* pBase  = (u8*) target;
	u8* pImPos = pBase;			// pos in decompressed data

	DXTColBlock * pBlock;

	deARGB col_0, col_1, col_2, col_3;

	//	TRACE("blocks: x: %d    y: %d\n", xblocks, yblocks );

	for( k=0; k < yblocks; k++ )
	{
		// 8 bytes per block
		pBlock = (DXTColBlock*) ( (u32)pCompBytes + k * xblocks * 8 );

		for( i=0; i < xblocks; i++, pBlock++ )
		{
			// inline func:
			GetColorBlockColors( pBlock, &col_0, &col_1, &col_2, &col_3);
			
			// now decode the color block into the bitmap bits
			// inline func:

			pImPos = pBase + (i*4*TByteDepth + k*4*TP);
			DE_ASSERT(pImPos < pBase + Height * TP);

			DecodeColorBlock( pImPos, pBlock, TP,
				TFormat, TByteDepth,
				col_0, col_1, col_2, col_3 );

			// Set to RGB test pattern
			//	pImPos = (DWORD*)((DWORD)pBase + i*4 + j*m_nWidth*4);
			//	*pImPos = ((i*4) << 16) | ((j*4) << 8 ) | ( (63-i)*4 );

			// checkerboard of only col_0 and col_1 basis colors:
			//	pImPos = (DWORD*)((DWORD)pBase + i*8 + j*m_nWidth*8);
			//	*pImPos = *((DWORD*)&col_0);
			//	pImPos += 1 + m_nWidth;
			//	*pImPos = *((DWORD*)&col_1);
			
		}
	}

	return deTRUE;
}

#pragma todo (implement DXT 2 4 5 decompression)
#pragma warning (disable : 4100)

deBoolean DecompressDXT2(const void* source, void* target,
						 long Width, long Height, long TP,
						 IdeDriver::BPP TFormat)
{
	DEBUGRETURN(deFALSE, "DecompressDXT2 : not implemented");
}

deBoolean DecompressDXT3(const void* source, void* target,
						 long Width, long Height, long TP,
						 IdeDriver::BPP TFormat)
{
	//DEBUGRETURN(deFALSE, "DecompressDXT3 : not implemented");
	int xblocks, yblocks;
	int TByteDepth;

	xblocks = Width / 4;
	yblocks = Height / 4;
	TByteDepth = BPPByteDepth(TFormat);

	int i,k;

	const u8 * pCompBytes = (u8*)source;
	u8 * pBase   = (u8*)  target;
	u8 * pImPos  = (u8*)  pBase;			// pos in decompressed data

	DXTColBlock				* pBlock;
	DXTAlphaBlockExplicit	* pAlphaBlock;

	deARGB col_0, col_1, col_2, col_3;

	//	TRACE("blocks: x: %d    y: %d\n", xblocks, yblocks );

	for( k=0; k < yblocks; k++ )
	{
		// 8 bytes per block
		// 1 block for alpha, 1 block for color

		pBlock = (DXTColBlock*) ( (u32)pCompBytes + k * xblocks * 16 );

		for( i=0; i < xblocks; i++, pBlock++ )
		{
			// Get alpha block
			pAlphaBlock = (DXTAlphaBlockExplicit*) pBlock;
			pBlock++;

			// inline func:
			// Get color block & colors
			GetColorBlockColors( pBlock, &col_0, &col_1, &col_2, &col_3 );

			// Decode the color block into the bitmap bits
			// inline func:

			pImPos = pBase + (i*4*TByteDepth + k*4*TP);
			DE_ASSERT(pImPos < pBase + Height * TP);

			DecodeColorBlock( pImPos, pBlock, TP,
				TFormat, TByteDepth,
				col_0, col_1, col_2, col_3 );

			// Overwrite the previous alpha bits with the alpha block
			//  info
			// inline func:
			DecodeAlphaExplicit( pImPos, TFormat, TByteDepth, pAlphaBlock, TP);
		}
	}

	return deTRUE;
}

deBoolean DecompressDXT4(const void* source, void* target,
						 long Width, long Height, long TP,
						 IdeDriver::BPP TFormat)
{
	DEBUGRETURN(deFALSE, "DecompressDXT4 : not implemented");
}

deBoolean DecompressDXT5(const void* source, void* target,
						 long Width, long Height, long TP,
						 IdeDriver::BPP TFormat)
{
	DEBUGRETURN(deFALSE, "DecompressDXT5 : not implemented");
}

