// DXGL
// Copyright (C) 2011-2016 William Feely

// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

#include "common.h"
#include "BufferObject.h"
#include "glDirectDrawPalette.h"
#include "glTexture.h"
#include "glRenderer.h"
#include "util.h"
#include "timer.h"

extern DXGLCFG dxglcfg;
void glDirectDrawSurface7_RenderScreen(LPDIRECTDRAWSURFACE7 surface, int vsync, BOOL settime);

static const DDSURFACEDESC2 ddsd256pal =
{
	sizeof(DDSURFACEDESC2),
	DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS | DDSD_PIXELFORMAT,
	1,
	256,
	256 * 4,
	0,
	0,
	0,
	0,
	NULL,
	{ 0,0 },
	{ 0,0 },
	{ 0,0 },
	{ 0,0 },
	{
		sizeof(DDPIXELFORMAT),
		DDPF_RGB,
	0,
	32,
	0xFF,
	0xFF00,
	0xFF0000,
	0
	},
	{
		DDSCAPS_TEXTURE,
		0,
	0,
	0
	},
	0,
};

const unsigned char DefaultPalette[1024] = {
0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x80,0x80,0x00,0x00,
0x00,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x00,0x80,0x80,0x00,0xc0,0xc0,0xc0,0x00,
0xa0,0xa0,0xa0,0x00,0xf0,0xf0,0xf0,0x00,0x40,0x20,0x00,0x00,0x60,0x20,0x00,0x00,
0x80,0x20,0x00,0x00,0xa0,0x20,0x00,0x00,0xc0,0x20,0x00,0x00,0xe0,0x20,0x00,0x00,
0x00,0x40,0x00,0x00,0x20,0x40,0x00,0x00,0x40,0x40,0x00,0x00,0x60,0x40,0x00,0x00,
0x80,0x40,0x00,0x00,0xa0,0x40,0x00,0x00,0xc0,0x40,0x00,0x00,0xe0,0x40,0x00,0x00,
0x00,0x60,0x00,0x00,0x20,0x60,0x00,0x00,0x40,0x60,0x00,0x00,0x60,0x60,0x00,0x00,
0x80,0x60,0x00,0x00,0xa0,0x60,0x00,0x00,0xc0,0x60,0x00,0x00,0xe0,0x60,0x00,0x00,
0x00,0x80,0x00,0x00,0x20,0x80,0x00,0x00,0x40,0x80,0x00,0x00,0x60,0x80,0x00,0x00,
0x80,0x80,0x00,0x00,0xa0,0x80,0x00,0x00,0xc0,0x80,0x00,0x00,0xe0,0x80,0x00,0x00,
0x00,0xa0,0x00,0x00,0x20,0xa0,0x00,0x00,0x40,0xa0,0x00,0x00,0x60,0xa0,0x00,0x00,
0x80,0xa0,0x00,0x00,0xa0,0xa0,0x00,0x00,0xc0,0xa0,0x00,0x00,0xe0,0xa0,0x00,0x00,
0x00,0xc0,0x00,0x00,0x20,0xc0,0x00,0x00,0x40,0xc0,0x00,0x00,0x60,0xc0,0x00,0x00,
0x80,0xc0,0x00,0x00,0xa0,0xc0,0x00,0x00,0xc0,0xc0,0x00,0x00,0xe0,0xc0,0x00,0x00,
0x00,0xe0,0x00,0x00,0x20,0xe0,0x00,0x00,0x40,0xe0,0x00,0x00,0x60,0xe0,0x00,0x00,
0x80,0xe0,0x00,0x00,0xa0,0xe0,0x00,0x00,0xc0,0xe0,0x00,0x00,0xe0,0xe0,0x00,0x00,
0x00,0x00,0x40,0x00,0x20,0x00,0x40,0x00,0x40,0x00,0x40,0x00,0x60,0x00,0x40,0x00,
0x80,0x00,0x40,0x00,0xa0,0x00,0x40,0x00,0xc0,0x00,0x40,0x00,0xe0,0x00,0x40,0x00,
0x00,0x20,0x40,0x00,0x20,0x20,0x40,0x00,0x40,0x20,0x40,0x00,0x60,0x20,0x40,0x00,
0x80,0x20,0x40,0x00,0xa0,0x20,0x40,0x00,0xc0,0x20,0x40,0x00,0xe0,0x20,0x40,0x00,
0x00,0x40,0x40,0x00,0x20,0x40,0x40,0x00,0x40,0x40,0x40,0x00,0x60,0x40,0x40,0x00,
0x80,0x40,0x40,0x00,0xa0,0x40,0x40,0x00,0xc0,0x40,0x40,0x00,0xe0,0x40,0x40,0x00,
0x00,0x60,0x40,0x00,0x20,0x60,0x40,0x00,0x40,0x60,0x40,0x00,0x60,0x60,0x40,0x00,
0x80,0x60,0x40,0x00,0xa0,0x60,0x40,0x00,0xc0,0x60,0x40,0x00,0xe0,0x60,0x40,0x00,
0x00,0x80,0x40,0x00,0x20,0x80,0x40,0x00,0x40,0x80,0x40,0x00,0x60,0x80,0x40,0x00,
0x80,0x80,0x40,0x00,0xa0,0x80,0x40,0x00,0xc0,0x80,0x40,0x00,0xe0,0x80,0x40,0x00,
0x00,0xa0,0x40,0x00,0x20,0xa0,0x40,0x00,0x40,0xa0,0x40,0x00,0x60,0xa0,0x40,0x00,
0x80,0xa0,0x40,0x00,0xa0,0xa0,0x40,0x00,0xc0,0xa0,0x40,0x00,0xe0,0xa0,0x40,0x00,
0x00,0xc0,0x40,0x00,0x20,0xc0,0x40,0x00,0x40,0xc0,0x40,0x00,0x60,0xc0,0x40,0x00,
0x80,0xc0,0x40,0x00,0xa0,0xc0,0x40,0x00,0xc0,0xc0,0x40,0x00,0xe0,0xc0,0x40,0x00,
0x00,0xe0,0x40,0x00,0x20,0xe0,0x40,0x00,0x40,0xe0,0x40,0x00,0x60,0xe0,0x40,0x00,
0x80,0xe0,0x40,0x00,0xa0,0xe0,0x40,0x00,0xc0,0xe0,0x40,0x00,0xe0,0xe0,0x40,0x00,
0x00,0x00,0x80,0x00,0x20,0x00,0x80,0x00,0x40,0x00,0x80,0x00,0x60,0x00,0x80,0x00,
0x80,0x00,0x80,0x00,0xa0,0x00,0x80,0x00,0xc0,0x00,0x80,0x00,0xe0,0x00,0x80,0x00,
0x00,0x20,0x80,0x00,0x20,0x20,0x80,0x00,0x40,0x20,0x80,0x00,0x60,0x20,0x80,0x00,
0x80,0x20,0x80,0x00,0xa0,0x20,0x80,0x00,0xc0,0x20,0x80,0x00,0xe0,0x20,0x80,0x00,
0x00,0x40,0x80,0x00,0x20,0x40,0x80,0x00,0x40,0x40,0x80,0x00,0x60,0x40,0x80,0x00,
0x80,0x40,0x80,0x00,0xa0,0x40,0x80,0x00,0xc0,0x40,0x80,0x00,0xe0,0x40,0x80,0x00,
0x00,0x60,0x80,0x00,0x20,0x60,0x80,0x00,0x40,0x60,0x80,0x00,0x60,0x60,0x80,0x00,
0x80,0x60,0x80,0x00,0xa0,0x60,0x80,0x00,0xc0,0x60,0x80,0x00,0xe0,0x60,0x80,0x00,
0x00,0x80,0x80,0x00,0x20,0x80,0x80,0x00,0x40,0x80,0x80,0x00,0x60,0x80,0x80,0x00,
0x80,0x80,0x80,0x00,0xa0,0x80,0x80,0x00,0xc0,0x80,0x80,0x00,0xe0,0x80,0x80,0x00,
0x00,0xa0,0x80,0x00,0x20,0xa0,0x80,0x00,0x40,0xa0,0x80,0x00,0x60,0xa0,0x80,0x00,
0x80,0xa0,0x80,0x00,0xa0,0xa0,0x80,0x00,0xc0,0xa0,0x80,0x00,0xe0,0xa0,0x80,0x00,
0x00,0xc0,0x80,0x00,0x20,0xc0,0x80,0x00,0x40,0xc0,0x80,0x00,0x60,0xc0,0x80,0x00,
0x80,0xc0,0x80,0x00,0xa0,0xc0,0x80,0x00,0xc0,0xc0,0x80,0x00,0xe0,0xc0,0x80,0x00,
0x00,0xe0,0x80,0x00,0x20,0xe0,0x80,0x00,0x40,0xe0,0x80,0x00,0x60,0xe0,0x80,0x00,
0x80,0xe0,0x80,0x00,0xa0,0xe0,0x80,0x00,0xc0,0xe0,0x80,0x00,0xe0,0xe0,0x80,0x00,
0x00,0x00,0xc0,0x00,0x20,0x00,0xc0,0x00,0x40,0x00,0xc0,0x00,0x60,0x00,0xc0,0x00,
0x80,0x00,0xc0,0x00,0xa0,0x00,0xc0,0x00,0xc0,0x00,0xc0,0x00,0xe0,0x00,0xc0,0x00,
0x00,0x20,0xc0,0x00,0x20,0x20,0xc0,0x00,0x40,0x20,0xc0,0x00,0x60,0x20,0xc0,0x00,
0x80,0x20,0xc0,0x00,0xa0,0x20,0xc0,0x00,0xc0,0x20,0xc0,0x00,0xe0,0x20,0xc0,0x00,
0x00,0x40,0xc0,0x00,0x20,0x40,0xc0,0x00,0x40,0x40,0xc0,0x00,0x60,0x40,0xc0,0x00,
0x80,0x40,0xc0,0x00,0xa0,0x40,0xc0,0x00,0xc0,0x40,0xc0,0x00,0xe0,0x40,0xc0,0x00,
0x00,0x60,0xc0,0x00,0x20,0x60,0xc0,0x00,0x40,0x60,0xc0,0x00,0x60,0x60,0xc0,0x00,
0x80,0x60,0xc0,0x00,0xa0,0x60,0xc0,0x00,0xc0,0x60,0xc0,0x00,0xe0,0x60,0xc0,0x00,
0x00,0x80,0xc0,0x00,0x20,0x80,0xc0,0x00,0x40,0x80,0xc0,0x00,0x60,0x80,0xc0,0x00,
0x80,0x80,0xc0,0x00,0xa0,0x80,0xc0,0x00,0xc0,0x80,0xc0,0x00,0xe0,0x80,0xc0,0x00,
0x00,0xa0,0xc0,0x00,0x20,0xa0,0xc0,0x00,0x40,0xa0,0xc0,0x00,0x60,0xa0,0xc0,0x00,
0x80,0xa0,0xc0,0x00,0xa0,0xa0,0xc0,0x00,0xc0,0xa0,0xc0,0x00,0xe0,0xa0,0xc0,0x00,
0x00,0xc0,0xc0,0x00,0x20,0xc0,0xc0,0x00,0x40,0xc0,0xc0,0x00,0x60,0xc0,0xc0,0x00,
0x80,0xc0,0xc0,0x00,0xa0,0xc0,0xc0,0x00,0xff,0xfb,0xf0,0x00,0xa0,0xa0,0xa4,0x00,
0x80,0x80,0x80,0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0x00,0x00,
0x00,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0x00,0xff,0xff,0x00,0xff,0xff,0xff,0x00
}; // Grabbed from a Windows DDRAW run


HRESULT WINAPI glDirectDrawPalette_QueryInterface(glDirectDrawPalette *This, REFIID riid, void** ppvObj)
{
	TRACE_ENTER(3,14,This,24,&riid,14,ppvObj);
	if (!IsReadablePointer(This, sizeof(glDirectDrawPalette))) TRACE_RET(HRESULT, 23, DDERR_INVALIDOBJECT);
	if(!ppvObj) TRACE_RET(HRESULT,23,DDERR_INVALIDPARAMS);
	if(!memcmp(riid,&IID_IUnknown,sizeof(GUID)))
	{
		glDirectDrawPalette_AddRef(This);
		*ppvObj = This;
		TRACE_VAR("*ppvObj",14,*ppvObj);
		TRACE_EXIT(23,DD_OK);
		return DD_OK;
	}
	TRACE_EXIT(23,E_NOINTERFACE);
	ERR(E_NOINTERFACE);
}

ULONG WINAPI glDirectDrawPalette_AddRef(glDirectDrawPalette *This)
{
	TRACE_ENTER(1,14,This);
	if (!IsReadablePointer(This, sizeof(glDirectDrawPalette))) TRACE_RET(ULONG, 8, 0);
	InterlockedIncrement(&This->refcount);
	TRACE_EXIT(8,This->refcount);
	return This->refcount;
}

ULONG WINAPI glDirectDrawPalette_Release(glDirectDrawPalette *This)
{
	ULONG ret;
	TRACE_ENTER(1, 14, This);
	if (!IsReadablePointer(This, sizeof(glDirectDrawPalette))) TRACE_RET(ULONG, 8, 0);
	ret = InterlockedDecrement(&This->refcount);
	if (This->refcount == 0)
	{
		if (This->creator) This->creator->lpVtbl->Release(This->creator);
		if (This->texture) glTexture_Release(This->texture, FALSE);
		free(This);
	}
	TRACE_EXIT(8,ret);
	return ret;
}

HRESULT WINAPI glDirectDrawPalette_GetCaps(glDirectDrawPalette *This, LPDWORD lpdwCaps)
{
	TRACE_ENTER(2,14,This,14,lpdwCaps);
	if (!IsReadablePointer(This, sizeof(glDirectDrawPalette))) TRACE_RET(HRESULT, 23, DDERR_INVALIDOBJECT);
	if (!IsReadablePointer(lpdwCaps, sizeof(LPDWORD))) TRACE_RET(HRESULT, 23, DDERR_INVALIDPARAMS);
	*lpdwCaps = This->flags;
	TRACE_VAR("*lpdwCaps",9,*lpdwCaps);
	TRACE_EXIT(23,DD_OK);
	return DD_OK;
}

HRESULT WINAPI glDirectDrawPalette_GetEntries(glDirectDrawPalette *This, DWORD dwFlags, DWORD dwBase, DWORD dwNumEntries, LPPALETTEENTRY lpEntries)
{
	DWORD allentries = 256;
	DWORD entrysize;
	TRACE_ENTER(5, 14, This, 9, dwFlags, 8, dwBase, 8, dwNumEntries, 14, lpEntries);
	if (!IsReadablePointer(This, sizeof(glDirectDrawPalette))) TRACE_RET(HRESULT,23,DDERR_INVALIDOBJECT);
	if(This->flags & DDPCAPS_1BIT) allentries=2;
	if(This->flags & DDPCAPS_2BIT) allentries=4;
	if(This->flags & DDPCAPS_4BIT) allentries=16;
	if(This->flags & DDPCAPS_8BIT) allentries=256;
	if(This->flags & DDPCAPS_8BITENTRIES) entrysize = 1;
	else entrysize = sizeof(PALETTEENTRY);
	if((dwBase + dwNumEntries) > allentries) TRACE_RET(HRESULT,23,DDERR_INVALIDPARAMS);
	memcpy(lpEntries,((char *)This->palette)+(dwBase*entrysize),dwNumEntries*entrysize);
	TRACE_EXIT(23,DD_OK);
	return DD_OK;
}
HRESULT WINAPI glDirectDrawPalette_Initialize(glDirectDrawPalette *This, LPDIRECTDRAW lpDD, DWORD dwFlags, LPPALETTEENTRY lpDDColorTable)
{
	TRACE_ENTER(4,14,This,14,lpDD,9,dwFlags,14,lpDDColorTable);
	if (!IsReadablePointer(This, sizeof(glDirectDrawPalette))) TRACE_RET(HRESULT, 23, DDERR_INVALIDOBJECT);
	TRACE_EXIT(23,DDERR_ALREADYINITIALIZED);
	return DDERR_ALREADYINITIALIZED;
}
HRESULT WINAPI glDirectDrawPalette_SetEntries(glDirectDrawPalette *This, DWORD dwFlags, DWORD dwStartingEntry, DWORD dwCount, LPPALETTEENTRY lpEntries)
{
	DWORD allentries = 256;
	DWORD entrysize;
	DDSURFACEDESC2 ddsd;
	TRACE_ENTER(5, 14, This, 9, dwFlags, 8, dwStartingEntry, 8, dwCount, 14, lpEntries);
	if (!IsReadablePointer(This, sizeof(glDirectDrawPalette))) TRACE_RET(HRESULT,23,DDERR_INVALIDOBJECT);
	if(This->flags & DDPCAPS_1BIT) allentries=2;
	if(This->flags & DDPCAPS_2BIT) allentries=4;
	if(This->flags & DDPCAPS_4BIT) allentries=16;
	if(This->flags & DDPCAPS_8BIT) allentries=256;
	if(This->flags & DDPCAPS_8BITENTRIES) entrysize = 1;
	else entrysize = sizeof(PALETTEENTRY);
	if((dwStartingEntry + dwCount) > allentries) TRACE_RET(HRESULT,23,DDERR_INVALIDPARAMS);
	memcpy(((char *)This->palette)+(dwStartingEntry*entrysize),lpEntries,dwCount*entrysize);
	if (!(This->flags & DDPCAPS_ALLOW256))
	{
		memcpy(&This->palette[0], DefaultPalette, 4);
		memcpy(&This->palette[255], DefaultPalette + 1020, 4);
	}
	if (This->texture)
	{
		glTexture_Lock(This->texture, 0, NULL, &ddsd, 0, FALSE);
		memcpy(ddsd.lpSurface, This->palette, 1024);
		glTexture_Unlock(This->texture, 0, NULL, FALSE);
	}
	if ((This->primary) && (This->surface))
	{
		if (!dxglcfg.DebugNoPaletteRedraw)
		{
			if (This->timer)
			{
				if (DXGLTimer_CheckLastDraw(This->timer, dxglcfg.HackPaletteDelay))
					glDirectDrawSurface7_RenderScreen(This->surface, dxglcfg.HackPaletteVsync, FALSE);
			}
		}
	}
	TRACE_EXIT(23, DD_OK);
	return DD_OK;
}

LPPALETTEENTRY glDirectDrawPalette_GetPalette(glDirectDrawPalette *This, DWORD *flags)
{
	TRACE_ENTER(2,14,This,14,flags);
	if(flags)
	{
		*flags = This->flags;
		TRACE_VAR("*flags",9,*flags);
	}
	TRACE_EXIT(14,This->palette);
	return This->palette;
}

glDirectDrawPaletteVtbl glDirectDrawPalette_iface =
{
	glDirectDrawPalette_QueryInterface,
	glDirectDrawPalette_AddRef,
	glDirectDrawPalette_Release,
	glDirectDrawPalette_GetCaps,
	glDirectDrawPalette_GetEntries,
	glDirectDrawPalette_Initialize,
	glDirectDrawPalette_SetEntries
};

HRESULT glDirectDrawPalette_Create(DWORD dwFlags, LPPALETTEENTRY lpDDColorArray, LPDIRECTDRAWPALETTE FAR *lplpDDPalette)
{
	glDirectDrawPalette *newpal;
	TRACE_ENTER(3,9,dwFlags,14,lpDDColorArray,14,lplpDDPalette);
	if (!IsWritablePointer(lplpDDPalette, sizeof(LPDIRECTDRAWPALETTE), FALSE)) TRACE_RET(HRESULT, 23, DDERR_INVALIDPARAMS);
	if (lpDDColorArray && !IsReadablePointer(lpDDColorArray,256*sizeof(PALETTEENTRY))) TRACE_RET(HRESULT, 23, DDERR_INVALIDPARAMS);
	if (dwFlags & 0xFFFFF000) TRACE_RET(HRESULT, 23, DDERR_INVALIDPARAMS);
	if ((dwFlags & DDPCAPS_8BIT) && (dwFlags & DDPCAPS_8BITENTRIES)) TRACE_RET(HRESULT, 23, DDERR_INVALIDPARAMS);
	if (((dwFlags & DDPCAPS_1BIT) || (dwFlags & DDPCAPS_2BIT) || (dwFlags & DDPCAPS_4BIT)) && (dwFlags & DDPCAPS_ALLOW256))
		TRACE_RET(HRESULT, 23, DDERR_INVALIDPARAMS);
	newpal = (glDirectDrawPalette*)malloc(sizeof(glDirectDrawPalette));
	if (!newpal) TRACE_RET(HRESULT, 23, DDERR_OUTOFMEMORY);
	ZeroMemory(newpal, sizeof(glDirectDrawPalette));
	newpal->refcount = 1;
	newpal->flags = dwFlags;
	newpal->lpVtbl = &glDirectDrawPalette_iface;
	newpal->creator = NULL;
	newpal->texture = NULL;
	newpal->primary = FALSE;
	newpal->surface = NULL;
	newpal->timer = NULL;
	if (lpDDColorArray == NULL)
	{
		if (dwFlags & 0x800) memcpy(newpal->palette, DefaultPalette, 1024);
		else return DDERR_INVALIDPARAMS;
	}
	else
	{
		if(newpal->flags & DDPCAPS_1BIT)
			if(newpal->flags & DDPCAPS_8BITENTRIES) memcpy(newpal->palette,lpDDColorArray,2);
			else memcpy(newpal->palette,lpDDColorArray,2*sizeof(PALETTEENTRY));
		else if(newpal->flags & DDPCAPS_2BIT)
			if(newpal->flags & DDPCAPS_8BITENTRIES) memcpy(newpal->palette,lpDDColorArray,4);
			else memcpy(newpal->palette,lpDDColorArray,4*sizeof(PALETTEENTRY));
		else if(newpal->flags & DDPCAPS_4BIT)
			if(newpal->flags & DDPCAPS_8BITENTRIES) memcpy(newpal->palette,lpDDColorArray,16);
			else memcpy(newpal->palette,lpDDColorArray,16*sizeof(PALETTEENTRY));
		else
		{
			memcpy(newpal->palette, lpDDColorArray, 256 * sizeof(PALETTEENTRY));
			if (!(dwFlags & DDPCAPS_ALLOW256))
			{
				memcpy(&newpal->palette[0], DefaultPalette, 4);
				memcpy(&newpal->palette[255], DefaultPalette + 1020, 4);
			}
		}
	}
	if(lplpDDPalette) *lplpDDPalette = (LPDIRECTDRAWPALETTE)newpal;
	TRACE_EXIT(23,DD_OK);
	return DD_OK;
}

void glDirectDrawPalette_CreateTexture(glDirectDrawPalette *This, struct glRenderer *renderer)
{
	DDSURFACEDESC2 ddsd;
	ZeroMemory(&ddsd, sizeof(DDSURFACEDESC2));
	memcpy(&ddsd, &ddsd256pal, sizeof(DDSURFACEDESC2));
	glTexture_Create(&ddsd256pal, &This->texture, renderer, 256, 1, FALSE, FALSE);
	glTexture_Lock(This->texture, 0, NULL, &ddsd, 0, FALSE);
	memcpy(ddsd.lpSurface, This->palette, 1024);
	glTexture_Unlock(This->texture, 0, NULL, FALSE);
}