/*
imfplay
Copyright (c) 2014 Tom Grus

This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the
use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:

    1. The origin of this software must not be misrepresented; you must not
       claim that you wrote the original software. If you use this software
       in a product, an acknowledgment in the product documentation would be
       appreciated but is not required.

    2. Altered source versions must be plainly marked as such, and must not
       be misrepresented as being the original software.

    3. This notice may not be removed or altered from any source distribution.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <conio.h>
#include <dos.h>

#include "global.h"
#include "screen.h"

#define CHAN_Y	2		//channel data table Y offset
#define ICL			-1	//invalid channel line

#define XY(x, y) (2 * ((x) + 80 * (y)))

extern unsigned char shadow_opl[256];

unsigned char *VGA = (unsigned char *)MK_FP(0xB800, 0);
unsigned char video_mode[11];

char hex[] = "0123456789ABCDEF";
char dec[16][3] = {" 0", " 1", " 2", " 3", " 4", " 5", " 6", " 7",
	" 8", " 9", "10", "11", "12", "13", "14", "15"};

char logo[] =
"          XX         X\n"
"          X          X\n"
"X         X          X\n"
"          X          X\n"
"X XXXXXXX XX XXXXXXX X XXXXXXX  X     X\n"
"X X  X  X X  X     X X X     X  X     X\n"
"X X  X  X X  XXXXXXX X XXXXXXXX XXXXXXX\n"
"             X                        X\n"
"             X                        X\n"
"             X                        X\n"
"             X                        X";

void puts_xy(char *text, unsigned char x, unsigned char y, unsigned char color)
{
	int vgaaddr = XY(x, y);
	while (*text)
	{
		VGA[vgaaddr++] = *text;
		VGA[vgaaddr++] = color;
		text++;
	}
}

void print_mute(int channel, int muted)
{
	unsigned char color = muted ? C_NACT : C_LOW;
	puts_xy("-", 53, CHAN_Y + 1 + 2 * channel, color);
	puts_xy("----", 56, CHAN_Y + 1 + 2 * channel, color);
	puts_xy("---", 62, CHAN_Y + 1 + 2 * channel, color);
	puts_xy(dec[channel + 1], 51, CHAN_Y + 2 * channel,
		muted ? C_NACT : C_ACT);
}

void print_cmd(unsigned char reg, unsigned char data)
{
	int channel_xlat[32] = {0, 2, 4, 1, 3, 5, ICL, ICL,
		6, 8, 10, 7, 9, 11, ICL, ICL,
		12, 14, 16, 13, 15, 17, ICL, ICL,
		ICL, ICL, ICL, ICL, ICL, ICL, ICL, ICL};
	char mult[16][4] = {"0.5", " 1 ", " 2 ", " 3 ", " 4 ", " 5 ", " 6 ", " 7 ",
		" 8 ", " 9 ", " 10", " 10", " 12", " 12", " 15", " 15"};
	char ksl[4][4] = {" - ", "1.5", " 3 ", " 6 "};
	char wave[4][5] = {" ^v ", " ^- ", " ^^ ", " // "};
	char buffer[16];

	int ch_line = channel_xlat[reg & 0x1F];
	int mch_line = reg & 0x0F;


	//raw data
	VGA[XY(3 * (reg & 0x0F) + 0, 22 + (reg >> 4))] = hex[data >> 4];
	VGA[XY(3 * (reg & 0x0F) + 1, 22 + (reg >> 4))] = hex[data & 0x0F];


	//decoded data
	switch (reg & 0xF0)
	{
		case 0x20: case 0x30:
			if (ch_line == ICL)
				break;

			puts_xy("AM", 2, ch_line + CHAN_Y, data & 0x80 ? C_HI : C_LOW);
			puts_xy("VIB", 5, ch_line + CHAN_Y, data & 0x40 ? C_HI : C_LOW);
			puts_xy("EGT", 9, ch_line + CHAN_Y, data & 0x20 ? C_HI : C_LOW);
			puts_xy("KSR", 13, ch_line + CHAN_Y, data & 0x10 ? C_HI : C_LOW);
			puts_xy(mult[data & 0x0F], 18, ch_line + CHAN_Y, C_NORM);
			break;

		case 0x40: case 0x50:	//total level
			if (ch_line == ICL)
				break;

			puts_xy(ksl[data >> 6], 24, ch_line + CHAN_Y, C_NORM);
			sprintf(buffer, "%5.2f",
				24.0f * ((data & 0x20) >> 5) +
				12.0f * ((data & 0x10) >> 4) +
				6.0f * ((data & 0x08) >> 3) +
				3.0f * ((data & 0x04) >> 2) +
				1.5f * ((data & 0x02) >> 1) +
				0.75f * (data & 0x01));
			puts_xy(buffer, 30, ch_line + CHAN_Y, C_NORM);
			break;

		case 0x60: case 0x70:	//AD
			if (ch_line == ICL)
				break;

			puts_xy(dec[data >> 4], 38, ch_line + CHAN_Y, C_NORM);
			puts_xy(dec[data & 0x0F], 41, ch_line + CHAN_Y, C_NORM);
			break;

		case 0x80: case 0x90:	//SR
			if (ch_line == ICL)
				break;

			puts_xy(dec[data >> 4], 44, ch_line + CHAN_Y, C_NORM);
			puts_xy(dec[data & 0x0F], 47, ch_line + CHAN_Y, C_NORM);
			break;

		case 0xA0:	//F-number
			if (mch_line > 8)
				break;

			sprintf(buffer, "%4d", ((shadow_opl[reg + 0x10] & 0x03) << 8) | data);
			puts_xy(buffer, 56, 2 * mch_line + 1 + CHAN_Y, C_NORM);
			break;

		case 0xB0:	//block, F-number
			if (mch_line > 8)
				break;

			puts_xy("KEY", 62, 2 * mch_line + 1 + CHAN_Y,
				data & 0x20 ? C_HI : C_LOW);
			sprintf(buffer, "%1d", (data >> 2) & 0x07);
			puts_xy(buffer, 53, 2 * mch_line + 1 + CHAN_Y, C_NORM);
			sprintf(buffer, "%4d", ((data & 0x03) << 8) | shadow_opl[reg - 0x10]);
			puts_xy(buffer, 56, 2 * mch_line + 1 + CHAN_Y, C_NORM);
			break;

		case 0xC0:	//feedback
			if (mch_line > 8)
				break;

			sprintf(buffer, "%1d", (data >> 1) & 0x07);
			puts_xy(buffer, 67, 2 * mch_line + 1 + CHAN_Y, C_NORM);
			puts_xy("CNT", 70, 2 * mch_line + 1 + CHAN_Y,
				data & 0x01 ? C_HI : C_LOW);
			break;

		case 0xE0: case 0xF0:
			if (ch_line == ICL)
				break;

			puts_xy(wave[data & 0x03], 75, ch_line + CHAN_Y, C_NORM);
			break;
	}
}

void print_delay(unsigned short delay)
{
	VGA[XY(16, 39)] = hex[(delay >> 12) & 0x0F];
	VGA[XY(17, 39)] = hex[(delay >> 8) & 0x0F];
	VGA[XY(18, 39)] = hex[(delay >> 4) & 0x0F];
	VGA[XY(19, 39)] = hex[delay & 0x0F];
}

void print_rem_delay(unsigned short rem)
{
	VGA[XY(16, 40)] = hex[(rem >> 12) & 0x0F];
	VGA[XY(17, 40)] = hex[(rem >> 8) & 0x0F];
	VGA[XY(18, 40)] = hex[(rem >> 4) & 0x0F];
	VGA[XY(19, 40)] = hex[rem & 0x0F];
}

void draw_logo(int x, int y)
{
	int r = 0, c = 0;
	char *text = logo;
	while (*text)
	{
		if (*text == '\n')
		{
			r++;
			c = 0;
		}
		else
		{
			VGA[XY(x + c, y + r)] = *text != ' ' ? 219 : ' ';
			VGA[XY(x + c++, y + r) + 1] = 15;
		}

		text++;
	}

	puts_xy("> imfplay "VERSION" by kvee", x + 15, y + 8, 10);
}

void screen_init()
{
	int i;

	//init video mode
	gettextinfo((struct text_info*)video_mode);
	textmode(C4350);
	clrscr();


	//prepare screen
	puts_xy("OPL2 register map", 0, 21, C_LOW);
	puts_xy("20+", 2, 0, C_LOW);
	puts_xy("40+", 24, 0, C_LOW);
	puts_xy("60+", 38, 0, C_LOW);
	puts_xy("80+", 44, 0, C_LOW);
	puts_xy("B0:A0+", 53, 0, C_LOW);
	puts_xy("C0+", 67, 0, C_LOW);
	puts_xy("E0+", 75, 0, C_LOW);
	puts_xy("multi scale  level   A  D  S  R     o F-num      fbk      wave",
	17, 1,
	C_LOW);
	draw_logo(41, 39);
	{
		int reg;
		for (reg = 0; reg < 256; reg++)
		{
			VGA[XY(3 * (reg & 0x0F) + 0, 22 + (reg >> 4))] = 249;
			VGA[XY(3 * (reg & 0x0F) + 1, 22 + (reg >> 4))] = 249;
		}
	}


	for (i = 0; i < 18; i++)
	{
		puts_xy(i & 1 ? "2" : "1",
			0, i + CHAN_Y,
			C_LOW);
		puts_xy("AM VIB EGT KSR [0.5] [ - ] [ 0.00]"
			" [ 0  0  0  0]",
			2, i + CHAN_Y,
			C_LOW);
		if (i & 1)
			puts_xy(" [ ][    ] KEY [ ] CNT [    ]", 51, i + CHAN_Y, C_LOW);
		else
		{
			puts_xy(dec[i / 2 + 1], 51, i + CHAN_Y, C_ACT);
			puts_xy("[    ]", 74, i + CHAN_Y, C_LOW);
		}
	}
}

void screen_restore()
{
	textmode(video_mode[6]);
}