#include "stdio.h"
#include <mbed.h>
#include <rtos.h>
#include "display.h"


static DSP table[] =
{   // cmd,  fg, bg,  x0, y0,       fmt,  maxsize, width, font
	{ 0x00, 0xF, 0x0,  0,  0, FMT_ASCII, MAX_BUFF, 255, Mono19x27}, // main display
	{ 0x0C, 0xF, 0x0,194, 38, FMT_ASCII, 3,        45,  Mono15x22}, // channels display
	{ 0x0A, 0xF, 0x0,  0, 57, FMT_FLAGS, 4,        0, Terminal6x8}, // flags + bits
};

// 9x10
const unsigned char icon_alarm[] __attribute__((aligned (2))) =
{
		0x1c, 0x0,
		0x3e, 0x0,
		0x7f, 0x0,
		0x7f, 0x0,
		0x7f, 0x0,
		0x7f, 0x0,
		0x7f, 0x0,
		0x7f, 0x0,
		0xff, 0x80,
		0x10, 0x0
};

const unsigned char icon_curve[] __attribute__((aligned (2))) =
{
		0x80, 0x0,
		0x80, 0x0,
		0x80, 0x80,
		0x81, 0x0,
		0x9e, 0x0,
		0xa0, 0x0,
		0xc0, 0x0,
		0x80, 0x0,
		0x80, 0x0,
		0xff, 0x80
};

static const flags_map flags = {
	// id    x0   y0  rev.   msg   icon
    { 0x00, {246, 27, 0, 0, false, NULL, icon_alarm}},  // F1.0  01 00 00 00
	{ 0x01, {246, 38, 0, 0, false, NULL, icon_curve}},  // F1.1  02 00 00 00
    { 0x02, {192, 32, 240, 60, false, NULL, NULL}}, // F1.2  04 00 00 00 - Channel frame
    { 0x03, {195, 29, 0, 0, false, "CHANNEL", NULL}},         // F1.3  08 00 00 00
    { 0x1c, {0,  38,  0, 0, false, "MON", NULL}},             // F4.4  00 00 00 10
    { 0x1b, {0,  47,  0, 0, false, "VIEW", NULL}},            // F4.3  00 00 00 08
    { 0x1d, {0,  29,  0, 0, true,  "SCAN", NULL}},            // F4.5  00 00 00 20
    { 0x1e, {0,  56,  0, 0, true,  "CONFIG", NULL}},          // F4.6  00 00 00 40
    { 0x1a, {40,  56, 0, 0, false, "*", NULL}},               // F4.2  00 00 00 04
    { 0x19, {50,  56, 0, 0, false, "ADRS", NULL}},            // F4.1  00 00 00 02
    { 0x18, {80,  56, 0, 0, false, "RMT", NULL}},             // F4.0  00 00 00 01
    { 0x17, {104, 56, 0, 0, true,  "ERROR", NULL}},            // F3.7  00 00 80 00
    { 0x16, {86, 38, 0, 0, false, "EXT", NULL}},             // F3.6  00 00 40 00
    { 0x15, {60, 38, 0, 0, false, "ONCE", NULL}},            // F3.5  00 00 20 00
    { 0x13, {40, 38, 0, 0, false, "MEM", NULL}},             // F3.3  00 00 08 00
    { 0x0c, {244, 47, 0, 0, false, "4W", NULL}},              // F2.4  00 10 00 00
	{ 0x0d, {244, 56, 0, 0, false, "OC", NULL}},              // F2.5  00 20 00 00
    { 0x12, {40,  47, 0, 0, false, "LAST", NULL}},            // F3.2  00 00 04 00
    { 0x11, {66,  47, 0, 0, false, "MIN", NULL}},             // F3.1  00 00 02 00
    { 0x10, {86,  47, 0, 0, false, "MAX", NULL}},             // F3.0  00 00 01 00
    { 0x0f, {106, 47, 0, 0, false, "AVG", NULL}},             // F2.7  00 80 00 00
    { 0x05, {152, 29, 0, 0, false, "Alarm", NULL}},           // F1.5  20 00 00 00
    { 0x08, {152, 39, 0, 0, false, "H", NULL}},               // F1.6  40 00 00 00
    { 0x07, {158, 39, 0, 0, false, "1", NULL}},               // F2.3  00 08 00 00
    { 0x06, {164, 39, 0, 0, false, "2", NULL}},               // F2.0  00 01 00 00
    { 0x04, {170, 39, 0, 0, false, "3", NULL}},               // F2.2  00 04 00 00
    { 0x09, {176, 39, 0, 0, false, "4", NULL}},               // F2.1  00 02 00 00
    { 0x0a, {182, 39, 0, 0, false, "L", NULL}},               // F1.4  00 10 00 00
    { 0x0b, {149, 32, 190, 50, false, NULL, NULL}}, // F1.7 - alarm frame

	{ 0x0e, {152, 56, 0, 0, true, "SHIFT", NULL}},   // not an actual command, managed by the front panel

	{ 0x14, {0xFF, 0xFF, 0xFF, 0xFF, false, NULL, NULL}},   // not an actual command, for the sake of completeness
	{ 0x1f, {0xFF, 0xFF, 0xFF, 0xFF, false, NULL, NULL}},   // not an actual command, for the sake of completeness

};


Display::Display(int Hz, PinName mosi, PinName miso, PinName sclk, PinName CS, PinName reset, PinName DC,
				 const char* name)
		: SSD1322(Hz, mosi, miso, sclk, CS, reset, DC, name)
{
	must_refresh = 0;
	for (flags_map::const_iterator it=flags.cbegin(); it!=flags.cend(); ++it)
	{
		flags_status[it->first] = false;
		flags_dim[it->first] = false;
	}
}

Display::~Display()
{
}

void Display::show_splashscreen()
{
	locate(30, 10);
	set_font((unsigned char*)Mono19x27);
	this->printf("HP34970A");
	set_font((unsigned char*)Terminal6x8);
	locate(90, 40);
	this->printf("David Douard");
	copy_to_lcd();
}

void Display::show_byescreen()
{
	cls();
	background(0x00);
	foreground(0xFF);
	locate(30, 10);
	set_font((unsigned char*)Mono19x27);
	this->printf("Bye...");
	copy_to_lcd();
}

void Display::dim_char(uint8_t n)
{
	// dim the char number of the currently displayed string of the main area;
	// do this by printing a modifed version of the last displayed string
	// but only alphanumeric chars should be counted (not the punctuation)
	char c;
	uint8_t len, i, j;
	// for which we need to look for the entry in the table
	for (i=0; i<sizeof(table)/sizeof(table[0]); ++i) {
		if (table[i].cmd == 0x00) {

			background(table[i].bgcolor);
			foreground(table[i].color / 2);  // dimmed
			set_font((unsigned char*) table[i].font);
			locate(table[i].x0, table[i].y0);

			len = strlen(table[i].buffer);
			if (n >= len)
				break;  // nothing to do

			for (j=0; (j<len); j++)
			{
				// advae the location up to the char to dimm
				c = table[i].buffer[j];
				if ((c == ',') || (c == '.') || (c == ';') || (c == ':'))
					// it's a "zero" width char, skip it
					n++;
				if (n == 0)
				{
					// found it
					character(c);
					break;
				}
				// otherwise, just advance the char_x
				char_width(c, true);
				n--;
			}
		}
	}
}

void Display::show(uint8_t cmd, const char *intxt, uint8_t nchar=0)
{
	uint8_t i;
	// uint8_t len;
	uint16_t bgcolor, fgcolor;
	char *oldv;
	// char *txt;
	char *txtp;
	static char txt[64];


	if (cmd == 0xFF) // cls
	{
		clrbuff();
	}
	else if (cmd == 0x0D)
	{ // dimm a character of the main area
		dim_char(intxt[0]);
	}
	else
	{
		pixel_buffer_mutex.lock();
		for (i=0; i<sizeof(table)/sizeof(table[0]); ++i) {
			if (table[i].cmd == cmd) {
				strcpy(txt, intxt);
				txtp = txt;

				bgcolor = table[i].bgcolor;
				fgcolor = table[i].color;
				background(bgcolor);
				foreground(fgcolor);
				set_font((unsigned char*) table[i].font);
				oldv = table[i].buffer;

				locate(table[i].x0, table[i].y0);

				if (table[i].fmt & FMT_ASCII)  // ASCII text
				{
					// check if the string has changed since last time
					if (true) //(strncmp(oldv, txt, table[i].maxsize) != 0)
					{
						// clear the text area
						if (table[i].width > 0)
							fillrect(table[i].x0,
									 table[i].y0,
									 table[i].x0 + table[i].width,
									 table[i].y0 + table[i].font[2] - 1,
									 bgcolor);
						// draw chars by portions of string separated by \t (highlight)
						for (uint8_t k=0; ;k++)
						{
							if (txtp[k] == 0x00)
							{ // end of string, display it
								this->printf(txtp);
								break;
							}
							if (txtp[k] == 0x09)
							{ // \t is a special char for 'unselected' display value
								// first disply the beginning of the string
								txtp[k] = 0x00;
								this->printf(txtp);

								// swap the fg color (dimm/bright)
								if (fgcolor == table[i].color)
									fgcolor /= 2;
								else
									fgcolor = table[i].color;
								foreground(fgcolor);
								// continue on the next with part of the string
								txtp = &(txtp[k+1]);
								k = 0;
							}
						}
						must_refresh = 1;
					}
				}

				if (table[i].fmt & FMT_FLAGS )  // flag indicators
				{
					uint8_t id;
					uint8_t c;
					id = 0;
					for (uint8_t nc=0; nc<4; nc++)
					{
						c = intxt[nc];
						for (uint8_t nb=0; nb<8; nb++)
						{
							if (c & (0x01 << nb))
								set_flag_status(id, true);
							else
								set_flag_status(id, false);
							draw_flag(id);
							id++;
						}
					}
				}

				for(uint8_t j=0; j<table[i].maxsize; j++)
					oldv[j] = txt[j];
				break;
			}
		}
		pixel_buffer_mutex.unlock();
	}
}


void Display::set_flag_status(uint8_t flagid, bool show)
{
	flags_status[flagid] = show;
}

void Display::set_flag_dim(uint8_t flagid, bool dim)
{
	flags_dim[flagid] = dim;
}

void Display::draw_flag(uint8_t flagid)
{
	uint8_t i;
	uint8_t bgcolor = 0x00, fgcolor = 0x0F;

	::printf("  FLAG %X:", flagid);
	const FLAG flag = flags.at(flagid);  // can't use the [] operator on a const map...
	if (flag.msg != NULL)
		::printf("%s ", flag.msg);
	else if (flag.icon != NULL)
		::printf("ICN ");
	else if (flag.x0 > 0)
		::printf("FR[%dx%d] ", flag.x0, flag.y0);
	else
		::printf("  WTF? ");

	if (flags_status[flagid])
	{
		if (flags_dim[flagid])
			::printf("  DIM\n");
		else
			::printf("  ON\n");
	}
	else
		::printf("  OFF\n");

	if ((flag.msg == NULL) && (flag.icon == NULL) && (flag.x0 == 0xFF) && (flag.y0 == 0xFF))
		// noop command
		return;

	if (flags_dim[flagid])
		fgcolor = fgcolor / 4;
	else if ((flag.msg == NULL) && (flag.icon == NULL))
		fgcolor = fgcolor / 6; // it's a frame

	if (flags_status[flagid])
	{
		foreground(flag.reverse ? bgcolor : fgcolor);
		background(flag.reverse ? fgcolor : bgcolor);
	}
	else
	{
		foreground(bgcolor);
		background(bgcolor);
	}

	if (flag.msg != NULL)
	{
		set_font((unsigned char*)Terminal6x8);
		locate(flag.x0, flag.y0);
		this->printf(flag.msg);
	}
	else if (flag.icon != NULL)
	{ // flag is an icon
		Bitmap_s pic = {9, 10, 2, (char*) flag.icon};
		Bitmap_BW(pic, flag.x0, flag.y0);
	}
	else
	{ // draw frames (Alarm and Channel)
		hline(flag.x0+1, flag.x0+3, flag.y0, foreground());
		hline(flag.x1-3, flag.x1-1, flag.y0, foreground());
		hline(flag.x0+1, flag.x1-1, flag.y1, foreground());

		vline(flag.x0, flag.y0+1, flag.y1-1, foreground());
		vline(flag.x1, flag.y0+1, flag.y1-1, foreground());
	}

	must_refresh = 1;
}
