#include "DirectDrawWrapper.h"

/*******************
**IUnknown methods**
********************/

// Retrieves pointers to the supported interfaces on an object.
HRESULT __stdcall IDirectDrawPaletteWrapper::QueryInterface(REFIID riid, LPVOID FAR * ppvObj)
{		
	debugMessage(1, "IDirectDrawPaletteWrapper::QueryInterface", "Partially Implemented");

	// Provide the directdraw interface for all versions up to 7
	if(riid == IID_IDirectDrawPalette)
	{
		// Set pointer to this interface
		ppvObj = (LPVOID *)this;
		// Increment reference count
		AddRef();
		// Return success
		return S_OK;
	}

	// Interface not supported
	return E_NOINTERFACE;
}

// Increments the reference count for an interface on an object.
ULONG __stdcall IDirectDrawPaletteWrapper::AddRef()
{
	debugMessage(1, "IDirectDrawPaletteWrapper::AddRef", "Partially Implemented");

	// Increment reference count
	ReferenceCount++;
	// Return current reference count
	return ReferenceCount;
}

// Decrements the reference count for an interface on an object.
ULONG __stdcall IDirectDrawPaletteWrapper::Release()
{
	debugMessage(1, "IDirectDrawPaletteWrapper::Release", "Partially Implemented");

	// Decrement reference count
	ReferenceCount--;
	// If reference count reaches 0 then free object
	if(ReferenceCount == 0)
	{
		// Free objects
		if(rawPalette != NULL) delete rawPalette;
		if(rgbPalette != NULL) delete rgbPalette;
	}
	// Return new reference count
	return ReferenceCount;
}

/*****************************
**IDirectDrawPalette methods**
******************************/

// Retrieves the capabilities of the palette object.
HRESULT __stdcall IDirectDrawPaletteWrapper::GetCaps(LPDWORD lpdwCaps)
{
	debugMessage(1, "IDirectDrawPaletteWrapper::GetCaps", "Partially Implemented");

	// lpdwCaps cannot be null
	if(lpdwCaps == NULL) return DDERR_INVALIDPARAMS;
	
	// set return data to current palette caps
	*lpdwCaps = paletteCaps;

	return DD_OK;
}

// Retrieves palette values from a DirectDrawPalette object. 
HRESULT __stdcall IDirectDrawPaletteWrapper::GetEntries(DWORD dwFlags, DWORD dwBase, DWORD dwNumEntries, LPPALETTEENTRY lpEntries)
{    
	// lpEntries cannot be null and dwFlags must be 0
	if(lpEntries == NULL) return DDERR_INVALIDPARAMS;

	// Copy raw palette entries to lpEntries(size dwNumEntries) starting at dwBase
	memcpy(lpEntries, &(rawPalette[dwBase]), sizeof(PALETTEENTRY) * min(dwNumEntries, entryCount - dwBase));

	/* 
    // NOTE: Debugging disabled for performance
    debugMessage(2, "IDirectDrawPaletteWrapper::GetEntries", "Retrieved Palette Entries");

    char message[2048] = "\0";
	sprintf_s(message, 2048, "dwBase: %d, dwNumEntries: %d", dwBase, dwNumEntries);
	debugMessage(2, "IDirectDrawPaletteWrapper::GetEntries", message);
    */
    
	// dwNumEntries is the number of palette entries that can fit in the array that lpEntries 
    // specifies. The colors of the palette entries are returned in sequence, from the value
    // of the dwStartingEntry parameter through the value of the dwCount parameter minus 1. 
    // (These parameters are set by IDirectDrawPalette::SetEntries.) 

	return DD_OK;
}

// Initializes the DirectDrawPalette object.
HRESULT __stdcall IDirectDrawPaletteWrapper::Initialize(LPDIRECTDRAW lpDDW, DWORD dwFlags, LPPALETTEENTRY lpDDColorTable)
{
	debugMessage(1, "IDirectDrawPaletteWrapper::Initialize", "Partially Implemented");

	// This method always returns already initialized
	return DDERR_ALREADYINITIALIZED;
}

// Changes entries in a DirectDrawPalette object immediately.
HRESULT __stdcall IDirectDrawPaletteWrapper::SetEntries(DWORD dwFlags, DWORD dwStartingEntry, DWORD dwCount, LPPALETTEENTRY lpEntries)
{
	// lpEntries cannot be null and dwFlags must be 0
	if(lpEntries == NULL) return DDERR_INVALIDPARAMS;

	// Copy raw palette entries from dwStartingEntry and of count dwCount
	memcpy(&(rawPalette[dwStartingEntry]), lpEntries, sizeof(PALETTEENTRY) * min(dwCount, entryCount - dwStartingEntry));

	// Translate new raw pallete entries to RGB(make sure not to go off the end of the memory)
	for(int i = dwStartingEntry; i < min(dwStartingEntry + dwCount, entryCount - dwStartingEntry); i++)
	{
		// Translate the raw palette to ARGB
		if(hasAlpha)
		{
			// Include peFlags as 8bit alpha
			rgbPalette[i] = rawPalette[i].peFlags << 24;
			rgbPalette[i] |= rawPalette[i].peRed << 16;
			rgbPalette[i] |= rawPalette[i].peGreen << 8;
			rgbPalette[i] |= rawPalette[i].peBlue;
		}
		else
		{
			// Alpha is always 255
			rgbPalette[i] = 0xFF000000;
			rgbPalette[i] |= rawPalette[i].peRed << 16;
			rgbPalette[i] |= rawPalette[i].peGreen << 8;
			rgbPalette[i] |= rawPalette[i].peBlue;
		}
	}

	/* 
    // NOTE: Debugging disabled for performance
    debugMessage(2, "IDirectDrawPaletteWrapper::SetEntries", "Set Palette Entries");

	char message[2048] = "\0";
	sprintf_s(message, 2048, "dwStartingEntry: %d, dwCount: %d", dwStartingEntry, dwCount);
	debugMessage(2, "IDirectDrawPaletteWrapper::SetEntries", message); */

	return DD_OK;
}

// Default constructor
IDirectDrawPaletteWrapper::IDirectDrawPaletteWrapper()
{
	// Init vars
	rgbPalette = NULL;
	rawPalette = NULL;

	ReferenceCount = 0;
	paletteCaps = 0;
	
	entryCount = 0;
	hasAlpha = false;

	// Create with flags
	AddRef();

	debugMessage(2, "IDirectDrawPaletteWrapper::IDirectDrawPaletteWrapper", "Created");
}

// Default destructor
IDirectDrawPaletteWrapper::~IDirectDrawPaletteWrapper()
{
	// Free used memory
	if(rgbPalette != NULL) 
	{
		delete rgbPalette;
		rgbPalette = NULL;
	}
	if(rawPalette != NULL)
	{
		delete rawPalette;
		rawPalette = NULL;
	}

	// Clean up
	Release();

	debugMessage(2, "IDirectDrawPaletteWrapper::~IDirectDrawPaletteWrapper", "Destroyed");
}

// Initialize wrapper function
HRESULT IDirectDrawPaletteWrapper::WrapperInitialize(DWORD dwFlags, LPPALETTEENTRY lpDDColorArray, LPDIRECTDRAWPALETTE FAR *lplpDDPalette)
{
	// Save palette caps
	paletteCaps = dwFlags;

	// Default to 256 entries
	entryCount = 256;

	// Create palette of requested bit size
	if(dwFlags & DDPCAPS_1BIT)
	{
		entryCount = 2;
	}
	else if(dwFlags & DDPCAPS_2BIT)
	{
		entryCount = 4;
	}
	else if(dwFlags & DDPCAPS_4BIT)
	{
		entryCount = 16;
	}
	else if(dwFlags & DDPCAPS_8BIT || dwFlags & DDPCAPS_ALLOW256)
	{
		entryCount = 256;
	}

	// Allocate raw ddraw palette
	rawPalette = new PALETTEENTRY[entryCount];
	// Memory failed to allocate, return out of memory
	if(rawPalette == NULL) 
	{
		debugMessage(0, "IDirectDrawPaletteWrapper::WrapperInitialize", "Failed to allocate raw palette memory");
		return DDERR_OUTOFMEMORY;
	}

	// Copy inital palette into raw palette
	memcpy(rawPalette, lpDDColorArray, sizeof(PALETTEENTRY) * entryCount);
	
	// Check flags for alpha
	if(dwFlags & DDPCAPS_ALPHA)
	{
		hasAlpha = true;
	}
	else
	{
		hasAlpha = false;
	}

	// Allocate rgb palette
	rgbPalette = new UINT32[entryCount];
	// Memory failed to allocate, return out of memory
	if(rgbPalette == NULL) 
	{
		debugMessage(0, "IDirectDrawPaletteWrapper::WrapperInitialize", "Failed to allocate RGB palette memory");
		return DDERR_OUTOFMEMORY;
	}
	
	// For all entries
	for(int i = 0; i < entryCount; i++)
	{
		// Translate the raw palette to ARGB
		if(hasAlpha)
		{
			// Include peFlags as 8bit alpha
			rgbPalette[i] = rawPalette[i].peFlags << 24;
			rgbPalette[i] |= rawPalette[i].peRed << 16;
			rgbPalette[i] |= rawPalette[i].peGreen << 8;
			rgbPalette[i] |= rawPalette[i].peBlue;
		}
		else
		{
			// Alpha is always 255
			rgbPalette[i] = 0xFF000000;
			rgbPalette[i] |= rawPalette[i].peRed << 16;
			rgbPalette[i] |= rawPalette[i].peGreen << 8;
			rgbPalette[i] |= rawPalette[i].peBlue;
		}
	}

	char message[2048] = "\0";
	sprintf_s(message, 2048, "Initialized");
	if(dwFlags & DDPCAPS_1BIT) strcat_s(message, 2048, ", DDPCAPS_1BIT");
	if(dwFlags & DDPCAPS_2BIT) strcat_s(message, 2048, ", DDPCAPS_2BIT");
	if(dwFlags & DDPCAPS_4BIT) strcat_s(message, 2048, ", DDPCAPS_4BIT");
	if(dwFlags & DDPCAPS_8BIT) strcat_s(message, 2048, ", DDPCAPS_8BIT");
	if(dwFlags & DDPCAPS_8BITENTRIES) strcat_s(message, 2048, ", DDPCAPS_8BITENTRIES");
	if(dwFlags & DDPCAPS_ALPHA) strcat_s(message, 2048, ", DDPCAPS_ALPHA");
	if(dwFlags & DDPCAPS_ALLOW256) strcat_s(message, 2048, ", DDPCAPS_ALLOW256");
	if(dwFlags & DDPCAPS_INITIALIZE) strcat_s(message, 2048, ", DDPCAPS_INITIALIZE");
	if(dwFlags & DDPCAPS_PRIMARYSURFACE) strcat_s(message, 2048, ", DDPCAPS_PRIMARYSURFACE");
	if(dwFlags & DDPCAPS_PRIMARYSURFACELEFT) strcat_s(message, 2048, ", DDPCAPS_PRIMARYSURFACELEFT");
	if(dwFlags & DDPCAPS_VSYNC) strcat_s(message, 2048, ", DDPCAPS_VSYNC");
	debugMessage(2, "IDirectDrawPaletteWrapper::WrapperInitialize", message);
	
    // Success
	return DD_OK;
}