#pragma once

#include "bitflags.h"
#include "miniwin/windows.h"

#define DDENUMRET_OK TRUE

#define MAKE_DDHRESULT(code) MAKE_HRESULT(1, 0x876, code)

#define DD_OK S_OK
#define DDERR_ALREADYINITIALIZED MAKE_DDHRESULT(5)
#define DDERR_CANNOTATTACHSURFACE MAKE_DDHRESULT(10)
#define DDERR_CANNOTDETACHSURFACE MAKE_DDHRESULT(20)
#define DDERR_CURRENTLYNOTAVAIL MAKE_DDHRESULT(40)
#define DDERR_EXCEPTION MAKE_DDHRESULT(55)
#define DDERR_GENERIC ((HRESULT) 0x80004005)
#define DDERR_HEIGHTALIGN MAKE_DDHRESULT(90)
#define DDERR_INCOMPATIBLEPRIMARY MAKE_DDHRESULT(95)
#define DDERR_INVALIDCAPS MAKE_DDHRESULT(100)
#define DDERR_INVALIDCLIPLIST MAKE_DDHRESULT(110)
#define DDERR_INVALIDMODE MAKE_DDHRESULT(120)
#define DDERR_INVALIDOBJECT MAKE_DDHRESULT(130)
#define DDERR_INVALIDPARAMS ((HRESULT) 0x80070057)
#define DDERR_INVALIDPIXELFORMAT MAKE_DDHRESULT(145)
#define DDERR_INVALIDRECT MAKE_DDHRESULT(150)
#define DDERR_LOCKEDSURFACES MAKE_DDHRESULT(160)
#define DDERR_NO3D MAKE_DDHRESULT(170)
#define DDERR_NOALPHAHW MAKE_DDHRESULT(180)
#define DDERR_NOCLIPLIST MAKE_DDHRESULT(205)
#define DDERR_NOCOLORCONVHW MAKE_DDHRESULT(210)
#define DDERR_NOCOOPERATIVELEVELSET MAKE_DDHRESULT(212)
#define DDERR_NOCOLORKEY MAKE_DDHRESULT(215)
#define DDERR_NOCOLORKEYHW MAKE_DDHRESULT(220)
#define DDERR_NOEXCLUSIVEMODE MAKE_DDHRESULT(225)
#define DDERR_NOFLIPHW MAKE_DDHRESULT(230)
#define DDERR_NOGDI MAKE_DDHRESULT(240)
#define DDERR_NOMIRRORHW MAKE_DDHRESULT(250)
#define DDERR_NOTFOUND MAKE_DDHRESULT(255)
#define DDERR_NOOVERLAYHW MAKE_DDHRESULT(260)
#define DDERR_NORASTEROPHW MAKE_DDHRESULT(280)
#define DDERR_NOROTATIONHW MAKE_DDHRESULT(290)
#define DDERR_NOSTRETCHHW MAKE_DDHRESULT(310)
#define DDERR_NOT4BITCOLOR MAKE_DDHRESULT(316)
#define DDERR_NOT4BITCOLORINDEX MAKE_DDHRESULT(317)
#define DDERR_NOT8BITCOLOR MAKE_DDHRESULT(320)
#define DDERR_NOTEXTUREHW MAKE_DDHRESULT(330)
#define DDERR_NOVSYNCHW MAKE_DDHRESULT(335)
#define DDERR_NOZBUFFERHW MAKE_DDHRESULT(340)
#define DDERR_NOZOVERLAYHW MAKE_DDHRESULT(350)
#define DDERR_OUTOFCAPS MAKE_DDHRESULT(360)
#define DDERR_OUTOFMEMORY ((HRESULT) 0x8007000E)
#define DDERR_OUTOFVIDEOMEMORY MAKE_DDHRESULT(380)
#define DDERR_OVERLAYCANTCLIP MAKE_DDHRESULT(382)
#define DDERR_OVERLAYCOLORKEYONLYONEACTIVE MAKE_DDHRESULT(384)
#define DDERR_PALETTEBUSY MAKE_DDHRESULT(387)
#define DDERR_COLORKEYNOTSET MAKE_DDHRESULT(400)
#define DDERR_SURFACEALREADYATTACHED MAKE_DDHRESULT(410)
#define DDERR_SURFACEALREADYDEPENDENT MAKE_DDHRESULT(420)
#define DDERR_SURFACEBUSY MAKE_DDHRESULT(430)
#define DDERR_SURFACEISOBSCURED MAKE_DDHRESULT(440)
#define DDERR_SURFACELOST MAKE_DDHRESULT(450)
#define DDERR_SURFACENOTATTACHED MAKE_DDHRESULT(460)
#define DDERR_TOOBIGHEIGHT MAKE_DDHRESULT(470)
#define DDERR_TOOBIGSIZE MAKE_DDHRESULT(480)
#define DDERR_TOOBIGWIDTH MAKE_DDHRESULT(490)
#define DDERR_UNSUPPORTED ((HRESULT) 0x80004001)
#define DDERR_UNSUPPORTEDFORMAT MAKE_DDHRESULT(510)
#define DDERR_UNSUPPORTEDMASK MAKE_DDHRESULT(520)
#define DDERR_VERTICALBLANKINPROGRESS MAKE_DDHRESULT(537)
#define DDERR_WASSTILLDRAWING MAKE_DDHRESULT(540)
#define DDERR_XALIGN MAKE_DDHRESULT(560)
#define DDERR_INVALIDDIRECTDRAWGUID MAKE_DDHRESULT(561)
#define DDERR_DIRECTDRAWALREADYCREATED MAKE_DDHRESULT(562)
#define DDERR_NODIRECTDRAWHW MAKE_DDHRESULT(563)
#define DDERR_PRIMARYSURFACEALREADYEXISTS MAKE_DDHRESULT(564)
#define DDERR_NOEMULATION MAKE_DDHRESULT(565)
#define DDERR_REGIONTOOSMALL MAKE_DDHRESULT(566)
#define DDERR_CLIPPERISUSINGHWND MAKE_DDHRESULT(567)
#define DDERR_NOCLIPPERATTACHED MAKE_DDHRESULT(568)
#define DDERR_NOHWND MAKE_DDHRESULT(569)
#define DDERR_HWNDSUBCLASSED MAKE_DDHRESULT(570)
#define DDERR_HWNDALREADYSET MAKE_DDHRESULT(571)
#define DDERR_NOPALETTEATTACHED MAKE_DDHRESULT(572)
#define DDERR_NOPALETTEHW MAKE_DDHRESULT(573)
#define DDERR_BLTFASTCANTCLIP MAKE_DDHRESULT(574)
#define DDERR_NOBLTHW MAKE_DDHRESULT(575)
#define DDERR_NODDROPSHW MAKE_DDHRESULT(576)
#define DDERR_OVERLAYNOTVISIBLE MAKE_DDHRESULT(577)
#define DDERR_NOOVERLAYDEST MAKE_DDHRESULT(578)
#define DDERR_INVALIDPOSITION MAKE_DDHRESULT(579)
#define DDERR_NOTAOVERLAYSURFACE MAKE_DDHRESULT(580)
#define DDERR_EXCLUSIVEMODEALREADYSET MAKE_DDHRESULT(581)
#define DDERR_NOTFLIPPABLE MAKE_DDHRESULT(582)
#define DDERR_CANTDUPLICATE MAKE_DDHRESULT(583)
#define DDERR_NOTLOCKED MAKE_DDHRESULT(584)
#define DDERR_CANTCREATEDC MAKE_DDHRESULT(585)
#define DDERR_NODC MAKE_DDHRESULT(586)
#define DDERR_WRONGMODE MAKE_DDHRESULT(587)
#define DDERR_IMPLICITLYCREATED MAKE_DDHRESULT(588)
#define DDERR_NOTPALETTIZED MAKE_DDHRESULT(589)

// --- GUIDs ---
DEFINE_GUID(IID_IDirectDraw2, 0xB3A6F3E0, 0x2B43, 0x11CF, 0xA2, 0xDE, 0x00, 0xAA, 0x00, 0xB9, 0x33, 0x56);
DEFINE_GUID(IID_IDirectDrawSurface3, 0xDA044E00, 0x69B2, 0x11D0, 0xA1, 0xD5, 0x00, 0xAA, 0x00, 0xB8, 0xDF, 0xBB);

// --- Enums ---
#define DDCKEY_SRCBLT DDColorKeyFlags::SRCBLT
enum class DDColorKeyFlags : uint32_t {
	SRCBLT = 1 << 3,
};

#define DDFLIP_WAIT DDFlipFlags::WAIT
enum class DDFlipFlags : uint32_t {
	WAIT = 1,
};

#define DDCAPS2_CERTIFIED DDCaps2Flags::CERTIFIED
enum class DDCaps2Flags : uint32_t {
	CERTIFIED = 1,
};

#define DDPCAPS_8BIT DDPixelCaps::_8BIT
#define DDPCAPS_INITIALIZE DDPixelCaps::INITIALIZE
#define DDPCAPS_ALLOW256 DDPixelCaps::ALLOW256
enum class DDPixelCaps : uint32_t {
	_8BIT = 1 << 2,
	INITIALIZE = 1 << 3,
	ALLOW256 = 1 << 6,
};
ENABLE_BITMASK_OPERATORS(DDPixelCaps)

#define DDBLT_NONE DDBltFlags::NONE
#define DDBLT_KEYSRC DDBltFlags::KEYSRC
#define DDBLT_ROP DDBltFlags::ROP
#define DDBLT_COLORFILL DDBltFlags::COLORFILL
#define DDBLT_WAIT DDBltFlags::WAIT
enum class DDBltFlags : uint32_t {
	NONE = 0,
	COLORFILL = 1 << 10,
	KEYSRC = 1 << 15,
	ROP = 1 << 17,
	WAIT = 1 << 24,
};
ENABLE_BITMASK_OPERATORS(DDBltFlags)

#define DDPF_ALPHAPIXELS DDPixelFormatFlags::ALPHAPIXELS
#define DDPF_PALETTEINDEXED8 DDPixelFormatFlags::PALETTEINDEXED8
#define DDPF_RGB DDPixelFormatFlags::RGB
#define DDPF_ALPHAPIXELS DDPixelFormatFlags::ALPHAPIXELS
enum class DDPixelFormatFlags : uint32_t {
	ALPHAPIXELS = 1 << 0,     // dwRGBAlphaBitMask is valid
	PALETTEINDEXED8 = 1 << 5, // The texture uses an 8 bit palette
	RGB = 1 << 6,             // dwRGBBitCount, dwRBitMask, dwGBitMask, and dwBBitMask is valid
};
ENABLE_BITMASK_OPERATORS(DDPixelFormatFlags)

#define DDBLTFAST_SRCCOLORKEY DDBltFastFlags::SRCCOLORKEY
#define DDBLTFAST_WAIT DDBltFastFlags::WAIT
enum class DDBltFastFlags : uint32_t {
	SRCCOLORKEY = 1 << 0,
	WAIT = 1 << 4,
};
ENABLE_BITMASK_OPERATORS(DDBltFastFlags)

#define DDLOCK_SURFACEMEMORYPTR DDLockFlags::SURFACEMEMORYPTR
#define DDLOCK_WAIT DDLockFlags::WAIT
#define DDLOCK_WRITEONLY DDLockFlags::WRITEONLY
#define DDLOCK_READONLY DDLockFlags::READONLY
enum class DDLockFlags : uint32_t {
	SURFACEMEMORYPTR = 0,
	WAIT = 1 << 0,
	READONLY = 1 << 4,
	WRITEONLY = 1 << 5,
};
ENABLE_BITMASK_OPERATORS(DDLockFlags)

#define DDSCL_FULLSCREEN DDSCLFlags::FULLSCREEN
#define DDSCL_ALLOWREBOOT DDSCLFlags::ALLOWREBOOT
#define DDSCL_NORMAL DDSCLFlags::NORMAL
#define DDSCL_EXCLUSIVE DDSCLFlags::EXCLUSIVE
enum class DDSCLFlags : uint32_t {
	FULLSCREEN = 1 << 0,
	ALLOWREBOOT = 1 << 1,
	NORMAL = 1 << 3,
	EXCLUSIVE = 1 << 4,
};
ENABLE_BITMASK_OPERATORS(DDSCLFlags)

#define DDSD_CAPS DDSurfaceDescFlags::CAPS
#define DDSD_HEIGHT DDSurfaceDescFlags::HEIGHT
#define DDSD_WIDTH DDSurfaceDescFlags::WIDTH
#define DDSD_BACKBUFFERCOUNT DDSurfaceDescFlags::BACKBUFFERCOUNT
#define DDSD_ZBUFFERBITDEPTH DDSurfaceDescFlags::ZBUFFERBITDEPTH
#define DDSD_PIXELFORMAT DDSurfaceDescFlags::PIXELFORMAT
enum class DDSurfaceDescFlags : uint32_t {
	CAPS = 1 << 0,            // ddsCaps is valid
	HEIGHT = 1 << 1,          // dwHeight is valid
	WIDTH = 1 << 2,           // dwWidth is valid
	BACKBUFFERCOUNT = 1 << 5, // dwBackBufferCount is valid
	ZBUFFERBITDEPTH = 1 << 6, // dwZBufferBitDepth is valid
	PIXELFORMAT = 1 << 12,    // ddpfPixelFormat is valid
};
ENABLE_BITMASK_OPERATORS(DDSurfaceDescFlags)

#define DDSCAPS_BACKBUFFER DDSCapsFlags::BACKBUFFER
#define DDSCAPS_COMPLEX DDSCapsFlags::COMPLEX
#define DDSCAPS_FLIP DDSCapsFlags::FLIP
#define DDSCAPS_OFFSCREENPLAIN DDSCapsFlags::OFFSCREENPLAIN
#define DDSCAPS_PRIMARYSURFACE DDSCapsFlags::PRIMARYSURFACE
#define DDSCAPS_SYSTEMMEMORY DDSCapsFlags::SYSTEMMEMORY
#define DDSCAPS_TEXTURE DDSCapsFlags::TEXTURE
#define DDSCAPS_3DDEVICE DDSCapsFlags::D3DDEVICE
#define DDSCAPS_VIDEOMEMORY DDSCapsFlags::VIDEOMEMORY
#define DDSCAPS_ZBUFFER DDSCapsFlags::ZBUFFER
enum class DDSCapsFlags : uint32_t {
	BACKBUFFER = 1 << 2,
	COMPLEX = 1 << 3,
	FLIP = 1 << 4,
	OFFSCREENPLAIN = 1 << 6,
	PRIMARYSURFACE = 1 << 9,
	SYSTEMMEMORY = 1 << 11,
	TEXTURE = 1 << 12,
	D3DDEVICE = 1 << 13,
	VIDEOMEMORY = 1 << 14,
	ZBUFFER = 1 << 17,
};
ENABLE_BITMASK_OPERATORS(DDSCapsFlags)

#define PC_NONE PCFlags::NONE
#define PC_NOCOLLAPSE PCFlags::NOCOLLAPSE
#define D3DPAL_READONLY PCFlags::D3DREADONLY
#define D3DPAL_RESERVED PCFlags::D3DRESERVED
enum class PCFlags : uint8_t {
	NONE = 0,
	NOCOLLAPSE = 1 << 2,
	D3DREADONLY = 1 << 6,
	D3DRESERVED = 1 << 7,
};
ENABLE_BITMASK_OPERATORS(PCFlags)

// --- Structs ---
struct DDCAPS {
	DWORD dwSize;
	DDCaps2Flags dwCaps2;
	DWORD dwSVBRops[8];
};
typedef DDCAPS* LPDDCAPS;

struct DDSCAPS {
	DDSCapsFlags dwCaps;
};
typedef struct DDSCAPS* LPDDSCAPS;

#define DDBLTFX_NOTEARING DDBLTFXFlags::NOTEARING
enum class DDBLTFXFlags : uint8_t {
	NOTEARING = 1 << 3,
};
struct DDBLTFX {
	DWORD dwSize;
	DDBLTFXFlags dwDDFX;
	DWORD dwROP;
	DWORD dwFillColor;
};
typedef DDBLTFX *LPDDBLTFX, *LPDDENUMSURFACESCALLBACK;

struct DDPIXELFORMAT {
	DWORD dwSize;
	DDPixelFormatFlags dwFlags;
	DWORD dwRGBBitCount; // Bit count, Lego Island only handles 8 or 16
	DWORD dwRBitMask;    // Red bit mask (0xF800)
	DWORD dwGBitMask;    // Green bit mask (0x07E0)
	DWORD dwBBitMask;    // Blue bit mask (0x001F)
	DWORD dwRGBAlphaBitMask;
};
typedef struct DDPIXELFORMAT* LPDDPIXELFORMAT;

struct DDCOLORKEY {
	DWORD dwColorSpaceLowValue;
	DWORD dwColorSpaceHighValue;
};
typedef DDCOLORKEY* LPDDCOLORKEY;

struct DDSURFACEDESC {
	DWORD dwSize;
	DDSurfaceDescFlags dwFlags;
	DWORD dwHeight;
	DWORD dwWidth;
	LONG lPitch;
	DWORD dwBackBufferCount;
	DWORD dwZBufferBitDepth;
	LPVOID lpSurface;
	DDPIXELFORMAT ddpfPixelFormat;
	DDSCAPS ddsCaps;
};
typedef struct DDSURFACEDESC* LPDDSURFACEDESC;

struct PALETTEENTRY {
	BYTE peRed;
	BYTE peGreen;
	BYTE peBlue;
	PCFlags peFlags;
};
typedef PALETTEENTRY* LPPALETTEENTRY;

struct LOGPALETTE {
	WORD palVersion;
	WORD palNumEntries;
	PALETTEENTRY palPalEntry[1];
};
typedef LOGPALETTE* LPLOGPALETTE;

typedef struct IDirectDraw* LPDIRECTDRAW;
struct IDirectDrawPalette : virtual public IUnknown {
	virtual HRESULT GetEntries(DWORD dwFlags, DWORD dwBase, DWORD dwNumEntries, LPPALETTEENTRY lpEntries) = 0;
	virtual HRESULT SetEntries(DWORD dwFlags, DWORD dwStartingEntry, DWORD dwCount, LPPALETTEENTRY lpEntries) = 0;
};
typedef struct IDirectDrawPalette* LPDIRECTDRAWPALETTE;

struct IDirectDrawClipper : virtual public IUnknown {
	virtual HRESULT SetHWnd(DWORD unnamedParam1, HWND hWnd);
};
typedef IDirectDrawClipper* LPDIRECTDRAWCLIPPER;

typedef struct IDirectDrawSurface* LPDIRECTDRAWSURFACE;
struct IDirectDrawSurface : virtual public IUnknown {
	virtual HRESULT AddAttachedSurface(LPDIRECTDRAWSURFACE lpDDSAttachedSurface) = 0;
	virtual HRESULT Blt(
		LPRECT lpDestRect,
		LPDIRECTDRAWSURFACE lpDDSrcSurface,
		LPRECT lpSrcRect,
		DDBltFlags dwFlags,
		LPDDBLTFX lpDDBltFx
	) = 0;
	virtual HRESULT BltFast(
		DWORD dwX,
		DWORD dwY,
		LPDIRECTDRAWSURFACE lpDDSrcSurface,
		LPRECT lpSrcRect,
		DDBltFastFlags dwTrans
	) = 0;
	virtual HRESULT Flip(LPDIRECTDRAWSURFACE lpDDSurfaceTargetOverride, DDFlipFlags dwFlags) = 0;
	virtual HRESULT GetAttachedSurface(LPDDSCAPS lpDDSCaps, LPDIRECTDRAWSURFACE* lplpDDAttachedSurface) = 0;
	virtual HRESULT GetDC(HDC* lphDC) = 0;
	virtual HRESULT GetPalette(LPDIRECTDRAWPALETTE* lplpDDPalette) = 0;
	virtual HRESULT GetPixelFormat(LPDDPIXELFORMAT lpDDPixelFormat) = 0;
	virtual HRESULT GetSurfaceDesc(LPDDSURFACEDESC lpDDSurfaceDesc) = 0;
	virtual HRESULT IsLost() = 0;
	virtual HRESULT Lock(LPRECT lpDestRect, LPDDSURFACEDESC lpDDSurfaceDesc, DDLockFlags dwFlags, HANDLE hEvent) = 0;
	virtual HRESULT ReleaseDC(HDC hDC) = 0;
	virtual HRESULT Restore() = 0;
	virtual HRESULT SetClipper(LPDIRECTDRAWCLIPPER lpDDClipper) = 0;
	virtual HRESULT SetColorKey(DDColorKeyFlags dwFlags, LPDDCOLORKEY lpDDColorKey) = 0;
	virtual HRESULT SetPalette(LPDIRECTDRAWPALETTE lpDDPalette) = 0;
	virtual HRESULT Unlock(LPVOID lpSurfaceData) = 0;
};

struct IDirectDrawSurface3 : public IDirectDrawSurface {};
typedef IDirectDrawSurface3* LPDIRECTDRAWSURFACE3;

typedef HRESULT (*LPDDENUMMODESCALLBACK)(LPDDSURFACEDESC, LPVOID);
struct IDirectDraw : virtual public IUnknown {
	virtual HRESULT CreateClipper(DWORD dwFlags, LPDIRECTDRAWCLIPPER* lplpDDClipper, IUnknown* pUnkOuter) = 0;
	virtual HRESULT CreatePalette(
		DDPixelCaps dwFlags,
		LPPALETTEENTRY lpColorTable,
		LPDIRECTDRAWPALETTE* lplpDDPalette,
		IUnknown* pUnkOuter
	) = 0;
	virtual HRESULT CreateSurface(
		LPDDSURFACEDESC lpDDSurfaceDesc,
		LPDIRECTDRAWSURFACE* lplpDDSurface,
		IUnknown* pUnkOuter
	) = 0;
	virtual HRESULT EnumDisplayModes(
		DWORD dwFlags,
		LPDDSURFACEDESC lpDDSurfaceDesc,
		LPVOID lpContext,
		LPDDENUMMODESCALLBACK lpEnumModesCallback
	) = 0;
	virtual HRESULT FlipToGDISurface() = 0;
	virtual HRESULT GetCaps(LPDDCAPS lpDDDriverCaps, LPDDCAPS lpDDHELCaps) = 0;
	virtual HRESULT GetDisplayMode(LPDDSURFACEDESC lpDDSurfaceDesc) = 0;
	virtual HRESULT RestoreDisplayMode() = 0;
	virtual HRESULT SetCooperativeLevel(HWND hWnd, DDSCLFlags dwFlags) = 0;
	virtual HRESULT SetDisplayMode(DWORD dwWidth, DWORD dwHeight, DWORD dwBPP) = 0;
};

struct IDirectDraw2 : public IDirectDraw {};
typedef IDirectDraw2* LPDIRECTDRAW2;

// --- Functions ---

HRESULT DirectDrawCreate(LPGUID lpGuid, LPDIRECTDRAW* lplpDD, IUnknown* pUnkOuter);

typedef BOOL (*LPDDENUMCALLBACKA)(GUID*, LPSTR, LPSTR, LPVOID);
HRESULT DirectDrawEnumerate(LPDDENUMCALLBACKA cb, void* context);

UINT WINAPI GetSystemPaletteEntries(HDC hdc, UINT iStart, UINT cEntries, LPPALETTEENTRY pPalEntries);

HPALETTE CreatePalette(LPLOGPALETTE lpLogPalette);

int SelectPalette(HDC hdc, HPALETTE hpal, BOOL bForceBackground);

int RealizePalette(HDC hdc);
