#include <com32.h>
#include <stdio.h>
#include <bios.h>
#include <graphics.h>

static uint8_t TextAttribute;	/* Text attribute for message file */
static uint8_t DisplayMask;	/* Display modes mask */

/* Routine to interpret next print char */
static void (*NextCharJump)(uint8_t);

void msg_initvars(void);
static void msg_setfg(uint8_t data);
static void msg_putchar(uint8_t ch);

/*
 *
 * get_msg_file: Load a text file and write its contents to the screen,
 *               interpreting color codes.
 *
 * Returns 0 on success, -1 on failure.
 */
int get_msg_file(char *filename)
{
	FILE *f;
	char ch;

	f = fopen(filename, "r");
	if (!f)
		return -1;

	TextAttribute = 0x7;	/* Default grey on white */
	DisplayMask = 0x7;	/* Display text in all modes */
	msg_initvars();

	/*
	 * Read the text file a byte at a time and interpret that
	 * byte.
	 */
	while ((ch = getc(f)) != EOF) {
		/* DOS EOF? */
		if (ch == 0x1A)
			break;

		/*
		 * 01h = text mode
		 * 02h = graphics mode
		 */
		UsingVGA &= 0x1;
		UsingVGA += 1;

		NextCharJump(ch);	/* Do what shall be done */
	}

	fclose(f);
	return 0;
}

static void msg_setbg(uint8_t data)
{
	if (unhexchar(&data) == 0) {
		data <<= 4;
		if (DisplayMask & UsingVGA) {
			TextAttribute = data;
		}

		NextCharJump = msg_setfg;
	} else {
		TextAttribute = 0x7;	/* Default attribute */
		NextCharJump = msg_putchar;
	}
}

static void msg_setfg(uint8_t data)
{
	if (unhexchar(&data) == 0) {
		if (DisplayMask & UsingVGA) {
			/* setbg set foreground to 0 */
			TextAttribute |= data;
		}
	} else
		TextAttribute = 0x7;	/* Default attribute */

	NextCharJump = msg_putchar;
}

static inline void msg_ctrl_o(void)
{
	NextCharJump = msg_setbg;
}

static void msg_novga(void)
{
	syslinux_force_text_mode();
	msg_initvars();
}

static void msg_viewimage(void)
{
	FILE *f;

	*VGAFilePtr = '\0';	/* Zero-terminate filename */

	mangle_name(VGAFileMBuf, VGAFileBuf);
	f = fopen(VGAFileMBuf, "r");
	if (!f) {
		/* Not there */
		NextCharJump = msg_putchar;
		return;
	}

	vgadisplayfile(f);
	fclose(f);
	msg_initvars();
}

/*
 * Getting VGA filename
 */
static void msg_filename(uint8_t data)
{
	/* <LF> = end of filename */
	if (data == 0x0A) {
		msg_viewimage();
		return;
	}

	/* Ignore space/control char */
	if (data > ' ') {
		if ((char *)VGAFilePtr < (VGAFileBuf + sizeof(VGAFileBuf)))
			*VGAFilePtr++ = data;
	}
}

static void msg_vga(void)
{
	NextCharJump = msg_filename;
	VGAFilePtr = (uint16_t *)VGAFileBuf;
}

/* Convert ANSI colors to PC display attributes */
static int convert_to_pcdisplay[] = { 0, 4, 2, 6, 1, 5, 3, 7 };

static void msg_normal(uint8_t data)
{
	uint8_t bg, fg;

	/* Write to serial port */
	if (DisplayMask & 0x4)
		write_serial(data);

	if (!(DisplayMask & UsingVGA))
		return;		/* Not screen */

	if (!(DisplayCon & 0x01))
		return;

	fg = convert_to_pcdisplay[(TextAttribute & 0x7)];
	bg = convert_to_pcdisplay[((TextAttribute >> 4) & 0x7)];

	printf("\033[");
	if (TextAttribute & 0x40)
		printf("1;"); /* Foreground bright */

	printf("3%dm\033[", fg);

	if (TextAttribute & 0x80)
		printf("5;"); /* Foreground blink */

	printf("4%dm%c\033[0m", bg, data);
}

static void msg_modectl(uint8_t data)
{
	data &= 0x07;
	DisplayMask = data;
	NextCharJump = msg_putchar;
}

static void msg_putchar(uint8_t ch)
{
	/* 10h to 17h are mode controls */
	if (ch >= 0x10 && ch < 0x18) {
		msg_modectl(ch);
		return;
	}

	switch (ch) {
	case 0x0F:		/* ^O = color code follows */
		msg_ctrl_o();
		break;
	case 0x0D:		/* Ignore <CR> */
		break;
	case 0x19:		/* <EM> = return to text mode */
		msg_novga();
		break;
	case 0x18:		/* <CAN> = VGA filename follows */
		msg_vga();
		break;
	default:
		msg_normal(ch);
		break;
	}
}

/*
 * Subroutine to initialize variables, also needed after loading
 * graphics file.
 */
void msg_initvars(void)
{
	/* Initialize state machine */
	NextCharJump = msg_putchar;
}
