#include "PlatformPrecomp.h"
#include "video_gl.h"
#include "dink/misc_util.h"
#include <cassert>
#include "Renderer/SoftSurface.h"
#include "App.h"

LPDIRECTDRAWSURFACE lpDDSBack = NULL;      // DirectDraw back surface
SoftSurface g_palette;

extern LPDIRECTDRAWSURFACE lpDDSBackGround;

bool InitializeVideoSystem()
{
	assert(!lpDDSBack);
	lpDDSBack = InitOffscreenSurface(640, 480, IDirectDrawSurface::MODE_PRIMARY_GL);
	
	g_palette.Init(8,8, SoftSurface::SURFACE_PALETTE_8BIT);
	g_palette.SetPaletteFromBMP("dink/tiles/palette.bmp", SoftSurface::COLOR_KEY_NONE);

	return true;
}

void KillVideoEngine()
{
	SAFE_DELETE(lpDDSBack);
}

void dderror(int hErr){};

IDirectDrawSurface * LoadBitmapIntoSurface(const char *pName, eTransparencyType trans, IDirectDrawSurface::eMode mode, byte *pMem)
{

	IDirectDrawSurface * pSurf;
	pSurf = new IDirectDrawSurface;
	pSurf->m_mode = mode;
	pSurf->m_pSurf = new SoftSurface;
	
	if (pMem)
	{
#ifdef _DEBUG
		//LogMsg("loading DDRAW bmp from mem");
#endif
		//if this is set, ignore the filename
		pSurf->m_pSurf->LoadFileFromMemory(pMem, SoftSurface::eColorKeyType(trans));
	} else
	{
#ifdef _DEBUG
		//LogMsg("loading DDRAW bmp from file");
#endif
		pSurf->m_pSurf->LoadFile(pName, SoftSurface::eColorKeyType(trans), false);
	}
	
	//LogMsg("loaded bitmap");

	switch( mode)
	{
	case IDirectDrawSurface::MODE_SHADOW_GL:
	
	
		break;
	}
	
	return pSurf;
}

void GetSizeOfSurface(IDirectDrawSurface *pdds, int *pX, int *pY)
{
	
	if (pdds->m_pSurf)
	{
		*pX = pdds->m_pSurf->GetWidth();
		*pY = pdds->m_pSurf->GetHeight();
		return;
	} else
	{
		assert(!"Bad surface");
		return;
	}
}

IDirectDrawSurface * InitOffscreenSurface(int x, int y, IDirectDrawSurface::eMode mode, bool bHiColor, SoftSurface *pSurfaceToCopyFrom)
{
	IDirectDrawSurface *pdds;
	pdds = new IDirectDrawSurface;
	pdds->m_mode = mode;

	switch (mode)
	{
		case IDirectDrawSurface::MODE_NORMAL:
		case IDirectDrawSurface::MODE_SHADOW_GL:
			
	
			pdds->m_pSurf = new SoftSurface;
			if (bHiColor)
			{
				pdds->m_pSurf->Init(x,y, SoftSurface::SURFACE_RGBA);
				pdds->m_pSurf->SetHasPremultipliedAlpha(true);
			} else
			{
				pdds->m_pSurf->Init(x,y, SoftSurface::SURFACE_PALETTE_8BIT);
				pdds->m_pSurf->SetPaletteFromBMP("dink/tiles/palette.bmp", SoftSurface::COLOR_KEY_NONE);
			}
			
			if (pSurfaceToCopyFrom)
			{
				pdds->m_pSurf->Blit(0,0,pSurfaceToCopyFrom);

			}
			if (mode == IDirectDrawSurface::MODE_SHADOW_GL)
			{
				pdds->m_pGLSurf = new Surface;
				
				if (GetApp()->GetVar("smoothing")->GetUINT32())
				{
					pdds->m_pGLSurf->SetTextureType(Surface::TYPE_GUI);

				} else
				{
					pdds->m_pGLSurf->SetTextureType(Surface::TYPE_NO_SMOOTHING);
				}

				pdds->m_pGLSurf->InitBlankSurface(x,y);
			}
			
			break;

		case IDirectDrawSurface::MODE_PRIMARY_GL:

		break;
	}

	return pdds;
}


IDirectDrawSurface::IDirectDrawSurface()
{
	m_pSurf = NULL;
	m_mode = MODE_NORMAL;
	m_pGLSurf = NULL;
	m_gameTickOfLastUse = 0;
}

IDirectDrawSurface::~IDirectDrawSurface()
{
	SAFE_DELETE(m_pSurf);
	SAFE_DELETE(m_pGLSurf);
}

glColorBytes RGBA_TO_GLCOLOR(const unsigned int color)
{
	return glColorBytes(GET_RED(color), GET_GREEN(color), GET_BLUE(color), GET_ALPHA(color));
}

unsigned int GLCOLOR_TO_RGBA(const glColorBytes glColor)
{
	return MAKE_RGBA(glColor.r, glColor.g, glColor.b, glColor.a);
}

Surface * IDirectDrawSurface::GetGLSuface()
{
	assert(m_mode == MODE_SHADOW_GL && "We're only using this for item icons, and they always would be gl shadowed!");

	UpdateShadowSurface();
	return m_pGLSurf;
}

int IDirectDrawSurface::Blt( rtRect32 *pDestRect, IDirectDrawSurface * pSrcSurf, rtRect32 *pSrcRect, uint32 flags, DDBLTFX *pFX )
{
	if (pSrcSurf)
	{
		pSrcSurf->UpdateLastUsedTime();
	}

	switch (m_mode)
	{
	
	case MODE_SHADOW_GL:
	case MODE_NORMAL:
		if (flags & DDBLT_COLORFILL)
		{
			assert(pFX);
			assert(pDestRect == NULL && "Well, we only support modifying the entire screen");
			m_pSurf->FillColor(g_palette.GetPalette()[pFX->dwFillColor]);
			return DD_OK;
		}
		if (pSrcSurf && pSrcSurf->m_pSurf && pSrcSurf->m_pSurf->GetSurfaceType() != SoftSurface::SURFACE_NONE)
		{
			//assert(!"Dumbass alert");
			m_pSurf->Blit(pDestRect->left, pDestRect->top, pSrcSurf->m_pSurf, pSrcRect->left, pSrcRect->top, pSrcRect->right-pSrcRect->left, pSrcRect->bottom-pSrcRect->top);
		}
		
	break;

	case MODE_PRIMARY_GL:
		{
			if (flags & DDBLT_COLORFILL)
			{
	
				assert(pFX);
				DrawRect(*pDestRect, GLCOLOR_TO_RGBA(g_palette.GetPalette()[pFX->dwFillColor]));
				return DD_OK;
			}

			if (!pSrcSurf)
			{
				assert(!"huh?!");
				return DD_OK;
			}
			if (pSrcSurf->m_mode == MODE_SHADOW_GL)
			{
				//blit from a GL surface instead
				pSrcSurf->UpdateShadowSurface();
				pSrcSurf->m_pGLSurf->BlitEx(rtRectf(*pDestRect) + rtRectf(0,0, 0.5f, 0.5f), rtRectf(*pSrcRect));
				break;
			}
			
			SoftSurface s;
			s.Init(pSrcRect->GetWidth(), pSrcRect->GetHeight(), SoftSurface::SURFACE_RGBA);
			
			//s.FillColor(glColorBytes(0,0,0,0));

			s.Blit(0,0, pSrcSurf->m_pSurf, pSrcRect->left,pSrcRect->top, pSrcRect->GetWidth(), pSrcRect->GetHeight());
				if (pSrcSurf->m_pSurf->GetUsesAlpha())
				{
					s.SetUsesAlpha(true);
				}
				
				g_globalBatcher.BlitRawImage(pDestRect->left,pDestRect->top, s);
		}

		break;
	
	}
	return DD_OK;
}
 
void IDirectDrawSurface::UpdateShadowSurface()
{
	assert(m_mode == MODE_SHADOW_GL && "Don't call this on other modes!");
	assert(m_pSurf);

	if (m_pSurf->GetModified())
	{
		//init the surface if needed
		if (!m_pGLSurf)
		{
			m_pGLSurf = new Surface;
			if (GetApp()->GetVar("smoothing")->GetUINT32())
			{
				m_pGLSurf->SetTextureType(Surface::TYPE_GUI);			
			} else
			{
				m_pGLSurf->SetTextureType(Surface::TYPE_NO_SMOOTHING);
			}


			m_pGLSurf->InitBlankSurface(m_pSurf->GetWidth(),m_pSurf->GetHeight());
		}


		if (m_pSurf->GetSurfaceType() == SoftSurface::SURFACE_PALETTE_8BIT)
		{
			SoftSurface s;
			s.Init(m_pSurf->GetWidth(), m_pSurf->GetHeight(), SoftSurface::SURFACE_RGBA);
			s.FillColor(glColorBytes(0,0,0,0));
			s.Blit(0,0, m_pSurf);
			s.FlipY();
		
			
			
			//put it on the GL surface
			m_pGLSurf->UpdateSurfaceRect(rtRect(0,0, s.GetWidth(), s.GetHeight()), s.GetPixelData());
			m_pGLSurf->SetUsesAlpha(m_pSurf->GetUsesAlpha());
	
			if (s.GetHasPremultipliedAlpha()) 
			{
				m_pGLSurf->SetBlendingMode(Surface::BLENDING_PREMULTIPLIED_ALPHA);
			}


		//	if (m_pSurf) m_pSurf->SetModified(false); //WARNING: Seth changed on 8/21/2017, seems like this fixes issue with constantly re-initting surfaces


			//		SAFE_FREE(m_pSurf);
		} 
	else
		{
			assert(m_pSurf);
			assert(m_pGLSurf);
			assert(m_pSurf->GetSurfaceType() == SoftSurface::SURFACE_RGBA);
			//m_pSurf->FillColor(glColorBytes(0,0,0,0));
			if (m_pSurf->GetPixelData())
			{
				m_pSurf->FlipY();
				m_pGLSurf->UpdateSurfaceRect(rtRect(0,0, m_pSurf->GetWidth(), m_pSurf->GetHeight()), m_pSurf->GetPixelData(), true);
				m_pSurf->FlipY();
				m_pGLSurf->SetUsesAlpha(m_pSurf->GetUsesAlpha());
			
				if (m_pSurf->GetHasPremultipliedAlpha()) 
				{
					m_pGLSurf->SetBlendingMode(Surface::BLENDING_PREMULTIPLIED_ALPHA);
				}

			}

		}

		if (m_pSurf) m_pSurf->SetModified(false);
		
		//assert(m_pGLSurf->GetBlendingMode() == Surface::BLENDING_PREMULTIPLIED_ALPHA);
	}
}

int IDirectDrawSurface::BltFast( int x, int y, IDirectDrawSurface *pSrcSurf, rtRect32 *pSrcRect, uint32 dwTrans )
{

	if (pSrcSurf)
	{
		pSrcSurf->UpdateLastUsedTime();
	}

	switch (m_mode)
	{
	case MODE_SHADOW_GL:
	case MODE_NORMAL:
		
		if (pSrcSurf->m_mode == MODE_PRIMARY_GL)
		{
			//we need to copy from what is already on the screen
			m_pSurf->BlitFromScreen(x, y, pSrcRect->left, pSrcRect->top, pSrcRect->GetWidth(), pSrcRect->GetHeight());
			//m_pSurf->Blit(x, y, lpDDSBackGround->m_pSurf, pSrcRect->left, pSrcRect->top, pSrcRect->GetWidth(), pSrcRect->GetHeight());
			m_pSurf->SetUsesAlpha(false);
		} else
		{
			m_pSurf->Blit(x, y, pSrcSurf->m_pSurf, pSrcRect->left, pSrcRect->top, pSrcRect->GetWidth(), pSrcRect->GetHeight());
		}
		break;

	case MODE_PRIMARY_GL:
		
		if (!pSrcSurf) 
		{
			assert(!"Shit!");
			return DD_OK;
		}
 		
		if (pSrcSurf->m_mode == MODE_SHADOW_GL)
		{
			//blit from a GL surface instead
			pSrcSurf->UpdateShadowSurface();
			pSrcSurf->m_pGLSurf->BlitEx(rtRectf(x, y, x+pSrcRect->GetWidth(), y +pSrcRect->GetHeight())+ rtRectf(0,0, 0.5f, 0.5f), rtRectf(*pSrcRect));
			break;
		}
		
		SoftSurface s;
		s.Init(pSrcRect->GetWidth(), pSrcRect->GetHeight(), SoftSurface::SURFACE_RGBA);
		s.Blit(0,0, pSrcSurf->m_pSurf, pSrcRect->left,pSrcRect->top, pSrcRect->GetWidth(), pSrcRect->GetHeight());
		
		if (pSrcSurf->m_pSurf->GetUsesAlpha())
		{
			s.SetUsesAlpha(true);
		}
		g_globalBatcher.BlitRawImage(x,y, s);
		break;

	}
	return DD_OK;
}

void IDirectDrawSurface::UpdateLastUsedTime()
{
	m_gameTickOfLastUse = GetBaseApp()->GetGameTick();
}