/*
 *   display.c
 *
 *   This file is part of Emu48
 *
 *   Copyright (C) 1995 Sebastien Carlier
 *
 */
#include "pch.h"
#include "resource.h"
#include "Emu48.h"
#include "kml.h"

#define LCD1_ROW    144
#define LCD2_ROW    288

UINT    nBackgroundX = 0;
UINT    nBackgroundY = 0;
UINT    nBackgroundW = 0;
UINT    nBackgroundH = 0;
UINT    nLcdX = 0;
UINT    nLcdY = 0;
BOOL    bLcdDoubled = FALSE;
LPBYTE	pbyLcd;
HDC     hLcdDC;
HDC     hMainDC;
static HBITMAP hLcdBitmap;
static HBITMAP hMainBitmap;
static HBITMAP hOldLcdBitmap;
static HBITMAP hOldMainBitmap;

#define B 0xFFFFFF
#define W 0x000000
static struct
{
	BITMAPINFOHEADER Lcd_bmih;
	DWORD dwColor[32];
} bmiLcd =
{
	{0x28,0/*x*/,0/*y*/,1,8,BI_RGB,0,0,0,32,0},
	{
		B,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,
		W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W
	}
};
#undef B
#undef W

static DWORD Pattern4[16];
static DWORD Pattern2[4];

VOID UpdateContrast(BYTE byContrast)
{
	if (bLcdDoubled)
	{
		BYTE c = byContrast;
		Pattern2[0] = 0;
		Pattern2[1] = (c<<8)|(c);
		Pattern2[2] = (c<<24)|(c<<16);
		Pattern2[3] = (c<<24)|(c<<16)|(c<<8)|(c);
	}
	else
	{
		DWORD c = (DWORD)byContrast;
		FillMemory(Pattern4, 16*sizeof(DWORD), 0);
		Pattern4[0x1] = c;
		Pattern4[0x3] = c;
		Pattern4[0x5] = c;
		Pattern4[0x7] = c;
		Pattern4[0x9] = c;
		Pattern4[0xB] = c;
		Pattern4[0xD] = c;
		Pattern4[0xF] = c;
		c<<=8;
		Pattern4[0x2] |= c;
		Pattern4[0x3] |= c;
		Pattern4[0x6] |= c;
		Pattern4[0x7] |= c;
		Pattern4[0xA] |= c;
		Pattern4[0xB] |= c;
		Pattern4[0xE] |= c;
		Pattern4[0xF] |= c;
		c<<=8;
		Pattern4[0x4] |= c;
		Pattern4[0x5] |= c;
		Pattern4[0x6] |= c;
		Pattern4[0x7] |= c;
		Pattern4[0xC] |= c;
		Pattern4[0xD] |= c;
		Pattern4[0xE] |= c;
		Pattern4[0xF] |= c;
		c<<=8;
		Pattern4[0x8] |= c;
		Pattern4[0x9] |= c;
		Pattern4[0xA] |= c;
		Pattern4[0xB] |= c;
		Pattern4[0xC] |= c;
		Pattern4[0xD] |= c;
		Pattern4[0xE] |= c;
		Pattern4[0xF] |= c;
	}
	return;
}

VOID SetLcdColor(UINT nId, UINT nRed, UINT nGreen, UINT nBlue)
{
	bmiLcd.dwColor[nId&0x1F] = (nRed&0xFF)|((nGreen&0xFF)<<8)|((nBlue&0xFF)<<16);
	return;
}

VOID CreateLcdBitmap()
{
	// create LCD bitmap
	if (bLcdDoubled)
	{
		bmiLcd.Lcd_bmih.biWidth = LCD2_ROW;
		bmiLcd.Lcd_bmih.biHeight = -128;
	}
	else
	{
		bmiLcd.Lcd_bmih.biWidth = LCD1_ROW;
		bmiLcd.Lcd_bmih.biHeight = -64;
	}
	hLcdDC = CreateCompatibleDC(hWindowDC);
	hLcdBitmap = CreateDIBSection(hLcdDC, (BITMAPINFO*)&bmiLcd,
		DIB_RGB_COLORS, (LPVOID*)&pbyLcd, NULL, 0);
	hOldLcdBitmap = SelectObject(hLcdDC, hLcdBitmap);
	UpdateContrast(0x10);
}

VOID DestroyLcdBitmap()
{
	if (hLcdDC != NULL)
	{
		// destroy LCD bitmap
		SelectObject(hLcdDC, hOldLcdBitmap);
		DeleteObject(hLcdBitmap);
		DeleteDC(hLcdDC);
		hLcdDC = NULL;
		hLcdBitmap = NULL;
		hOldLcdBitmap = NULL;
	}
	return;
}

BOOL CreateMainBitmap(LPSTR szFilename)
{
	hMainDC = CreateCompatibleDC(hWindowDC);
	hMainBitmap = LoadBitmapFile(szFilename);
	if (hMainBitmap == NULL)
	{
		DeleteDC(hMainDC);
		return FALSE;
	}
	hOldMainBitmap = SelectObject(hMainDC, hMainBitmap);
	SelectPalette(hMainDC, hPalette, FALSE);
	return TRUE;
}

VOID DestroyMainBitmap()
{
	if (hMainDC != NULL)
	{
		// destroy Main bitmap
		SelectObject(hMainDC, hOldMainBitmap);
		DeleteObject(hMainBitmap);
		DeleteDC(hMainDC);
		hMainDC = NULL;
		hMainBitmap = NULL;
		hOldMainBitmap = NULL;
	}
	return;
}

//****************
//*
//* LCD functions
//*
//****************

VOID UpdateDisplayPointers()
{
	if (Chipset.boffset&4)
	{
		Chipset.width = Chipset.loffset + 36;
	}
	else
	{
		Chipset.width = Chipset.loffset + 34;
	}
	Chipset.end1 = Chipset.start1 + (Chipset.lcounter+1)*Chipset.width;
	if (Chipset.end1 < Chipset.start1)
	{
		Chipset.start12 = Chipset.end1;
		Chipset.end1 = Chipset.start1;
	}
	else
	{
		Chipset.start12 = Chipset.start1;
	}
	Chipset.end2 = Chipset.start2 + (63 - Chipset.lcounter)*34;
}

static BYTE Buf[36];
static BOOL bScreenIsClean = FALSE;

VOID UpdateMainDisplay()
{
	UINT x, y;
	DWORD d = Chipset.start1;
	BYTE *p = pbyLcd;

	if (!Chipset.dispon)
	{
		if (!bScreenIsClean)
		{
			bScreenIsClean = TRUE;
			if (bLcdDoubled)
				FillMemory(pbyLcd, LCD2_ROW*64, 0);
			else
				FillMemory(pbyLcd, LCD1_ROW*64, 0);
		}
		return;
	}
	bScreenIsClean = FALSE;
	if (bLcdDoubled)
	{
		for (y=0; y<=Chipset.lcounter; y++)
		{
			Npeek(Buf,d,36);
			for (x=0; x<36; x++,p+=8)
			{
				*((DWORD*)(p+0))=Pattern2[Buf[x]&3];
				*((DWORD*)(p+4))=Pattern2[Buf[x]>>2];
			}
			CopyMemory(p, p-LCD2_ROW, LCD2_ROW);
			p+=LCD2_ROW;
			d+=Chipset.width;
		}
		BitBlt(hWindowDC, nLcdX, nLcdY, 262, (Chipset.lcounter+1)*2, hLcdDC, Chipset.boffset*2, 0, SRCCOPY);
	}
	else
	{
		for (y=0; y<=Chipset.lcounter; y++)
		{
			Npeek(Buf,d,36);
			for (x=0; x<36; x++,p+=4) *((DWORD*)p)=Pattern4[Buf[x]];
			d+=Chipset.width;
		}
		BitBlt(hWindowDC, nLcdX, nLcdY, 131, Chipset.lcounter+1, hLcdDC, Chipset.boffset, 0, SRCCOPY);
	}
}

VOID UpdateMenuDisplay()
{
	UINT x, y;
	DWORD d = Chipset.start2;

	if (!Chipset.dispon) return;
	if (Chipset.lcounter==0x3F) return; // menu disabled
	if (bLcdDoubled)
	{
		BYTE *p = pbyLcd + ((Chipset.lcounter+1)*2*LCD2_ROW);
		for (y=Chipset.lcounter+1; y<64; y++)
		{
			Npeek(Buf,d,36);
			for (x=0; x<36; x++,p+=8)
			{
				*((DWORD*)(p+0))=Pattern2[Buf[x]&3];
				*((DWORD*)(p+4))=Pattern2[Buf[x]>>2];
			}
			CopyMemory(p, p-LCD2_ROW, LCD2_ROW);
			p+=LCD2_ROW;
			d+=34;
		}
		BitBlt(hWindowDC, nLcdX, nLcdY+(Chipset.lcounter+1)*2, 262, (63-Chipset.lcounter)*2, hLcdDC, 0, (Chipset.lcounter+1)*2, SRCCOPY);
	}
	else
	{
		BYTE *p = pbyLcd + (Chipset.lcounter+1)*LCD1_ROW;
		for (y=Chipset.lcounter+1; y<64; y++)
		{
			Npeek(Buf,d,36);
			for (x=0; x<36; x++,p+=4) *((DWORD*)p)=Pattern4[Buf[x]];
			d+=34;
		}
		BitBlt(hWindowDC, nLcdX, nLcdY+(Chipset.lcounter+1), 131, 63-Chipset.lcounter, hLcdDC, 0, Chipset.lcounter+1, SRCCOPY);
	}
	return;
}

VOID WriteToMainDisplay(LPBYTE a, DWORD d, UINT s)
{
	UINT x0, x;
	UINT y0, y;
	DWORD *p;

	if (Chipset.width<0) return;
	d -= Chipset.start1;
	y0 = y = d / Chipset.width;
	x0 = x = d % Chipset.width;
	if ((x0*4+Chipset.boffset)>=131) return;
	if (bLcdDoubled)
	{
		p  = (DWORD*)(pbyLcd + y0*LCD2_ROW*2 + x0*8);
		while (s--)
		{
			if (x<36)
			{
				p[72] = p[0] = Pattern2[(*a)&3];
				p[73] = p[1] = Pattern2[(*a)>>2];
			}
			a++;
			x++;
			if ((x==(UINT)Chipset.width)&&s)
			{
				x=0;
				y++;
				if (y==(Chipset.lcounter+1)) break;
				p=(DWORD*)(pbyLcd+y*LCD2_ROW*2);
			} else p+=2;
		}
		if (y0!=y)
		{
			y++;
			y0<<=1; y<<=1;
			BitBlt(hWindowDC, nLcdX, nLcdY+y0, 262, y-y0, hLcdDC, Chipset.boffset*2, y0, SRCCOPY);
		}
		else
		{
			x0<<=3; x<<=3;
			if (x>262) x=262;
			y0<<=1; y<<=1;
			BitBlt(hWindowDC, nLcdX+x0, nLcdY+y0, x-x0, 2, hLcdDC, x0+Chipset.boffset*2, y0, SRCCOPY);
		}
	}
	else
	{
		p  = (DWORD*)(pbyLcd + y0*LCD1_ROW + x0*4);
		while (s--)
		{
			if (x<36) *p = Pattern4[*a];
			a++;
			x++;
			if ((x==(UINT)Chipset.width)&&s)
			{
				x=0;
				y++;
				if (y==(Chipset.lcounter+1)) break;
				p=(DWORD*)(pbyLcd+y*LCD1_ROW);
			} else p++;
		}
		if (y0!=y)
		{
			BitBlt(hWindowDC, nLcdX, nLcdY+y0, 131, y-y0+1, hLcdDC, Chipset.boffset, y0, SRCCOPY);
		}
		else
		{
			x0<<=2; x<<=2;
			if (x>131) x=131;
			BitBlt(hWindowDC, nLcdX+x0, nLcdY+y0, x-x0, 1, hLcdDC, x0+Chipset.boffset, y0, SRCCOPY);
		}
	}
	return;
}

VOID WriteToMenuDisplay(LPBYTE a, DWORD d, UINT s)
{
	UINT x0, x;
	UINT y0, y;
	DWORD *p;

	if (Chipset.width<0) return;
	if (Chipset.lcounter==0x3F) return; // menu disabled
	d -= Chipset.start2;
	y0 = y = (d / 34) + (Chipset.lcounter+1);
	x0 = x = d % 34;
	if ((x0*4)>=131) return;
	if (bLcdDoubled)
	{
		p  = (DWORD*)(pbyLcd + y0*LCD2_ROW*2 + x0*8);
		while (s--)
		{
			if (x<34)
			{
				p[72] = p[0] = Pattern2[(*a)&3];
				p[73] = p[1] = Pattern2[(*a)>>2];
			}
			a++;
			x++;
			if ((x==34)&&s)
			{
				x=0;
				y++;
				if (y==64) break;
				p=(DWORD*)(pbyLcd+y*LCD2_ROW*2);
			} else p+=2;
		}
		if (y0!=y)
		{
			y0<<=1; y<<=1;
			BitBlt(hWindowDC, nLcdX, nLcdY+y0, 262, y-y0+2, hLcdDC, 0, y0, SRCCOPY);
		}
		else
		{
			x0<<=3; x<<=3;
			y0<<=1; y<<=1;
			if (x>262) x=262;
			BitBlt(hWindowDC, nLcdX+x0, nLcdY+y0, x-x0, y-y0+2, hLcdDC, x0, y0, SRCCOPY);
		}
	}
	else
	{
		p  = (DWORD*)(pbyLcd + y0*LCD1_ROW + x0*4);
		while (s--)
		{
			if (x<34) *p = Pattern4[*a];
			a++;
			x++;
			if ((x==34)&&s)
			{
				x=0;
				y++;
				if (y==64) break;
				p=(DWORD*)(pbyLcd+y*LCD1_ROW);
			} else p++;
		}
		if (y0!=y)
		{
			BitBlt(hWindowDC, nLcdX, nLcdY+y0, 131, y-y0+1, hLcdDC, 0, y0, SRCCOPY);
		}
		else
		{
			x0<<=2; x<<=2;
			if (x>131) x=131;
			BitBlt(hWindowDC, nLcdX+x0, nLcdY+y0, x-x0, y-y0+1, hLcdDC, x0, y0, SRCCOPY);
		}
	}
	return;
}

VOID UpdateAnnunciators()
{
	BYTE c;
	
	c = (BYTE)(Chipset.IORam[0xB] | (Chipset.IORam[0xC]<<4));
	if (!(c&0x80)) c=0;
	DrawAnnunciator(1,c&0x01);
	DrawAnnunciator(2,c&0x02);
	DrawAnnunciator(3,c&0x04);
	DrawAnnunciator(4,c&0x08);
	DrawAnnunciator(5,c&0x10);
	DrawAnnunciator(6,c&0x20);
	return;
}

VOID ResizeWindow()
{
	RECT rectWindow;
	RECT rectClient;

	rectWindow.left   = 0;
	rectWindow.top    = 0;
	rectWindow.right  = nBackgroundW;
	rectWindow.bottom = nBackgroundH;
	AdjustWindowRect(&rectWindow, WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_OVERLAPPED, TRUE);
	SetWindowPos (hWnd, (HWND)NULL, 0, 0,
		rectWindow.right  - rectWindow.left,
		rectWindow.bottom - rectWindow.top,
		SWP_NOMOVE | SWP_NOZORDER);
	GetClientRect(hWnd, &rectClient);
	AdjustWindowRect(&rectClient, WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_OVERLAPPED, TRUE);
	if (rectClient.bottom < rectWindow.bottom)
	{
		rectWindow.bottom += (rectWindow.bottom - rectClient.bottom);
		SetWindowPos (hWnd, (HWND)NULL, 0, 0,
			rectWindow.right  - rectWindow.left,
			rectWindow.bottom - rectWindow.top,
			SWP_NOMOVE | SWP_NOZORDER);
	}
	InvalidateRect(hWnd,NULL,TRUE);
	return;
}