#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>

#include "ls020.h"
#include "../pins.h"

#define LS020_ROTATE_180


const uint8_t *_font;
static uint8_t fp[FONT_PARAM_COUNT];
static uint8_t _x, _y;
static uint8_t _br;



static void ls020WriteCommand(uint8_t command)
{
    PORT(LS020_DPORT) &= ~LS020_RS_LINE; // 0
    SPDR = command;
	while(!(SPSR & (1<<SPIF)));
    
    return;
}

static void ls020WriteData(uint8_t data)
{
    PORT(LS020_DPORT) |= LS020_RS_LINE; //1
    SPDR = data;
    while(!(SPSR & (1<<SPIF)));


	return;
}

static void ls020SetWindow(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1)
{
	// Set command mode
	PORT(LS020_DPORT) &= ~LS020_CS_LINE;

	ls020WriteCommand(0x36); //Memory Access Control (Направление заполнения области дисплея (памяти): 0bVHRXXXXX, V - заполнение по вертикали (0 - сверху-вниз, 1 - снизу-вверх),
                            //H - заполнение по горизонтали (0 - слева-направо, 1 - справа-налево), R - меняются местами строки и столбцы (при этом заполнение остается сверху-вниз, слева-направо))
    ls020WriteData(0x80);
    
#ifdef LS020_ROTATE_180
    ls020WriteCommand(0x2a);
    ls020WriteData(LS020_WIDTH - 1 - y1);
    ls020WriteData(LS020_WIDTH - 1 - y0);
    ls020WriteCommand(0x2b);
    ls020WriteData(LS020_HEIGHT - 1 - x1);
    ls020WriteData(LS020_HEIGHT - 1 - x0);
#else
    ls020WriteCommand(0x2a);
    ls020WriteData(LS020_HEIGHT - 1 - x1);
    ls020WriteData(LS020_HEIGHT - 1 - x0);
    ls020WriteCommand(0x2b);
    ls020WriteData(y0);
    ls020WriteData(y1);
#endif
    ls020WriteCommand(0x2c);
	// Stop command sequence
	PORT(LS020_DPORT) |= LS020_CS_LINE;

	return;
}

void ls020DrawRect(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t color)
{
	uint8_t x, y;

	/* Swap X */
	if (x1 > x2) {
		x = x1;
		x1 = x2;
		x2 = x;
	}

	/* Swap Y */
	if (y1 > y2) {
		y = y1;
		y1 = y2;
		y2 = y;
	}

	if (x2 >= LS020_HEIGHT)
		return;
	if (y2 >= LS020_WIDTH)
		return;

	ls020SetWindow(x1, y1, x2, y2);

	
	// Start data sequence
	PORT(LS020_DPORT) &= ~LS020_CS_LINE;

	for (y = y1; y <= y2; y++)
		for (x = x1; x <= x2; x++)
			ls020WriteData(color);

	// Stop data sequence
	while(!(SPSR & (1<<SPIF)));
	PORT(LS020_DPORT) |= LS020_CS_LINE;

	return;
}

void ls020DrawFrame(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t color)
{
	ls020DrawHorizLine(x1, y1, x2, color);
	ls020DrawHorizLine(x1, y2, x2, color);
	ls020DrawVertLine(x1, y1, y2, color);
	ls020DrawVertLine(x2, y1, y2, color);

	return;
}

void ls020Clear(void)
{
	ls020DrawRect(0, 0, 175, 131, COLOR_BCKG);

	return;
}

ISR (TIMER0_OVF_vect)
{
	/* 2MHz / (256 - 156) = 20000Hz */
	TCNT0 = 156;

	static uint8_t run = 1;
	if (run)
		ADCSRA |= 1<<ADSC;							/* Start ADC every second interrupt */
	run = !run;

	static uint8_t br;

	if (++br >= LS020_MAX_BRIGHTNESS)				/* Loop brightness */
		br = LS020_MIN_BRIGHTNESS;

	if (br == _br) {
		PORT(LS020_BCKL) &= ~LS020_BCKL_LINE;		/* Turn backlight off */
	} else if (br == 0)
		PORT(LS020_BCKL) |= LS020_BCKL_LINE;		/* Turn backlight on */
}


void ls020SetBrightness(uint8_t br)
{
	_br = br;

	return;
}

void ls020Init(void)
{

	DDR(LS020_DPORT) = -LS020_RS_LINE | LS020_RES_LINE | LS020_DAT_LINE | LS020_CLK_LINE | LS020_CS_LINE;
	DDR(LS020_BCKL) |= LS020_BCKL_LINE;

	/* Configure Hardware SPI */
	SPCR = (1<<SPE) | (1<<MSTR);
	SPSR = (1<<SPI2X);
	
	PORT(LS020_DPORT) &= ~LS020_CS_LINE;  //0
	PORT(LS020_DPORT) &= ~LS020_RES_LINE; //0
	_delay_ms(5);
	PORT(LS020_DPORT) |= LS020_RES_LINE;
	_delay_ms(5);
    // Start command sequence
	
    ls020WriteCommand(0x01); //Программный сброс
    ls020WriteCommand(0x36); //Memory Access Control (Направление заполнения области дисплея (памяти): 0bVHRXXXXX, V - заполнение по вертикали (0 - сверху-вниз, 1 - снизу-вверх),
                            //H - заполнение по горизонтали (0 - слева-направо, 1 - справа-налево), R - меняются местами строки и столбцы (при этом заполнение остается сверху-вниз, слева-направо))
    ls020WriteData(0xe0);
    ls020WriteCommand(0x11); //Выход из спящего режима

    _delay_ms(20);

    ls020WriteCommand(0x3a); //Установка цветовой палитры
    ls020WriteData(0x02); //Байт на пиксель 256 цветов

    _delay_ms(20);

    ls020WriteCommand(0x29); //Включение дисплея

	// Stop command sequence
	PORT(LS020_DPORT) |= LS020_CS_LINE;

	ls020Clear();

	return;
}

void ls020PowerOff(void)
{

}

void ls020SetXY(uint8_t x, uint8_t y)
{
	_x = x;
	_y = y;
}

uint8_t ls020GetX(void)
{
	return _x;
}

void ls020LoadFont(const uint8_t *font, uint8_t color, uint8_t mult)
{
	uint8_t i;

	_font = font + 5;
	for (i = 0; i < FONT_PARAM_COUNT - 2; i++)
		fp[i] = pgm_read_byte(font + i);
	fp[FONT_COLOR] = color;
	fp[FONT_MULT] = mult;
}

void ls020WriteChar(uint8_t code)
{
	/* Store current position before writing to display */
	uint8_t x = _x;
	uint8_t y = _y;

	uint8_t i, j, k;
	uint8_t mx, my;

	uint8_t spos = code - ((code >= 128) ? fp[FONT_OFTNA] : fp[FONT_OFTA]);

	uint16_t oft = 0;	/* Current symbol offset in array*/
	uint8_t swd = 0;	/* Current symbol width */
	uint8_t pgmData;

	for (i = 0; i < spos; i++) {
		swd = pgm_read_byte(_font + i);
		oft += swd;
	}
	swd = pgm_read_byte(_font + spos);

	oft *= fp[FONT_HEIGHT];
	oft += fp[FONT_CCNT];

	ls020SetWindow(x, y, x + swd * fp[FONT_MULT] - 1, y + fp[FONT_HEIGHT] * fp[FONT_MULT] * 8 - 1);

	// Set data mode
	PORT(LS020_DPORT) &= ~LS020_RS_LINE;

	// Start data sequence
	PORT(LS020_DPORT) &= ~LS020_CS_LINE;




	for (j = 0; j < swd; j++) {







		for (my = 0; my < fp[FONT_MULT]; my++) {
			for (k = 0; k < fp[FONT_HEIGHT]; k++) {
				for(i = 0; i < 8; i++) {
#ifdef LS020_ROTATE_180
					pgmData = pgm_read_word(_font + oft + (fp[FONT_HEIGHT] - k) * swd - 1 - j);
					if (pgmData & (128>>i)) {
#else
					pgmData = pgm_read_word(_font + oft + k * swd + j);
					if (pgmData & (1<<i)) {
#endif
						for (mx = 0; mx < fp[FONT_MULT]; mx++)
							ls020WriteData(fp[FONT_COLOR]);

					} else {
						for (mx = 0; mx < fp[FONT_MULT]; mx++)
							ls020WriteData(COLOR_BCKG);
					}
				}
			}
		}
	}


	// Stop data sequence
	while(!(SPSR & (1<<SPIF)));
	PORT(LS020_DPORT) |= LS020_CS_LINE;

	ls020SetXY(x + swd * fp[FONT_MULT], y);

	return;
}

void ls020WriteString(char *string)
{
	if (*string)
		ls020WriteChar(*string++);
	while(*string) {
		ls020WriteChar(fp[FONT_LTSPPOS]);
		ls020WriteChar(*string++);
	}

	return;
}

void ls020WriteIcon24(uint8_t iconNum)
{
	uint8_t j, k, i;


	const uint8_t *icon;
	uint8_t pgmData;

	icon = &icons_24[24 * 24 / 8 * iconNum];

	if (icon) {
		ls020SetWindow(_x, _y, _x + 24 - 1, _y + 24 - 1);
		// Set data mode
		PORT(LS020_DPORT) &= ~LS020_RS_LINE;
		// Start data sequence
		PORT(LS020_DPORT) &= ~LS020_CS_LINE;

		for (j = 0; j < 24; j++) {
			for (k = 0; k < 24 / 8; k++) {
				for (i = 0; i < 8; i++) {
#ifdef LS020_ROTATE_180
					pgmData = pgm_read_byte(icon + 24 * (24 / 8 - k) - j - 1);
					ls020WriteData(pgmData & (128>>i) ? COLOR_WHITE : COLOR_BLACK);
#else
					pgmData = pgm_read_byte(icon + 24 * k + j);
					ls020WriteData(pgmData & (1<<i) ? COLOR_WHITE : COLOR_BLACK);
#endif
				}
			}
		}

		// Stop data sequence
		while(!(SPSR & (1<<SPIF)));
		PORT(LS020_DPORT) |= LS020_CS_LINE;
	}

	return;
}

void ls020WriteIcon32(uint8_t iconNum)
{
	uint8_t j, k, i;


	const uint8_t *icon;
	uint8_t pgmData;

	icon = &icons_32[32 * 32 / 8 * iconNum];

	if (icon) {
		ls020SetWindow(_x, _y, _x + 32 - 1, _y + 32 - 1);
		// Set data mode
		PORT(LS020_DPORT) &= ~LS020_RS_LINE;
		// Start data sequence
		PORT(LS020_DPORT) &= ~LS020_CS_LINE;

		for (j = 0; j < 32; j++) {
			for (k = 0; k < 32 / 8; k++) {
				for (i = 0; i < 8; i++) {
#ifdef LS020_ROTATE_180
					pgmData = pgm_read_byte(icon + 32 * (32 / 8 - 1) - j - 1);
					ls020WriteData(pgmData & (128>>i) ? COLOR_WHITE : COLOR_BLACK);
#else
					pgmData = pgm_read_byte(icon + 32 * k + j);
					ls020WriteData(pgmData & (1<<i) ? COLOR_WHITE : COLOR_BLACK);
#endif
				}
			}
		}

		// Stop data sequence
		while(!(SPSR & (1<<SPIF)));
		PORT(LS020_DPORT) |= LS020_CS_LINE;
	}

	return;
}
