#include <avr/interrupt.h>
#include <avr/io.h>
#include <string.h>
#include <util/delay.h>
#include "spi_master.h"
#include "func.h"
#include <stdlib.h>

#define MAX_DIGITS 12
#define F_CPU 16000000UL
#define DELAY_MS 2000

void output_on_display(struct calculator *calc)
{
	char buffer[32];
	switch (calc->state)
	{
		case NUMBER_FIRST:
		{
			size_t b_size = display_all_clear(0, buffer);
			SPI_MasterTransmit(buffer, b_size);
			
			b_size = display_set_page(0x01, buffer);
			SPI_MasterTransmit(buffer, b_size);
			
			transmit_num(calc->num1, buffer);	
			break;
		}
		case NUMBER_SECOND:
		{
			size_t b_size = display_all_clear(0, buffer);
			SPI_MasterTransmit(buffer, b_size);
			
			b_size = display_set_page(0x01, buffer);
			SPI_MasterTransmit(buffer, b_size);
			
			transmit_num(calc->num1, buffer);
			
			char symbol[] = {0x01};
			// Îïåðàöèÿ
			switch (calc->operation)
			{
				case NO_OP:
				break;
				case ADD:
				symbol[0] = 0x04;
				break;
				case SUB:
				symbol[0] = 0x06;
				break;
				case DIV:
				symbol[0] = 0x08;
				break;
				case MUL:
				symbol[0] = 0x03;
				break;
			}
			
			b_size = display_add_simbol(symbol, 1, buffer);
			SPI_MasterTransmit(buffer, b_size);
			
			b_size = display_set_page(0x02, buffer);
			SPI_MasterTransmit(buffer, b_size);
				
			transmit_num(calc->num2, buffer);
			break;
		}
		case RESULT:
		{
			size_t b_size = display_all_clear(0, buffer);
			SPI_MasterTransmit(buffer, b_size);
			
			b_size = display_set_page(0x01, buffer);
			SPI_MasterTransmit(buffer, b_size);
			
			transmit_num(calc->num1, buffer);
			
			char symbol[] = {0x01};
			// Îïåðàöèÿ
			switch (calc->operation)
			{
				case NO_OP:
				break;
				case ADD:
				symbol[0] = 0x04;
				break;
				case SUB:
				symbol[0] = 0x06;
				break;
				case DIV:
				symbol[0] = 0x08;
				break;
				case MUL:
				symbol[0] = 0x03;
				break;
			}
			
			b_size = display_add_simbol(symbol, 1, buffer);
			SPI_MasterTransmit(buffer, b_size);
			
			b_size = display_set_page(0x02, buffer);
			SPI_MasterTransmit(buffer, b_size);
			
			transmit_num(calc->num2, buffer);
			
			// ðåçóëüòàò
			b_size = display_set_page(0x03, buffer);
			SPI_MasterTransmit(buffer, b_size);
			
			symbol[0] = 0x15;
			b_size = display_add_simbol(symbol, 1, buffer);
			SPI_MasterTransmit(buffer, b_size);
			
			transmit_num(calc->result, buffer);
			
			break;
		}
		default:
		display_all_clear(0, buffer);
	}
}

void transmit_num(long number, char *buffer) {
	char number_buffer[MAX_DIGITS];
	char mapped_digits[MAX_DIGITS];
	ltoa(number, number_buffer, 10);
	map_digits(mapped_digits, number_buffer, strlen(number_buffer));
	size_t b_size = display_add_simbol(mapped_digits, strlen(number_buffer), buffer);
	SPI_MasterTransmit(buffer, b_size);
}

void SPI_MasterInit(void)
{
	/* Íàñòðîéêà MOSI è SCK êàê âûõîä,
	âñå îñòàëüíûå ñèãíàëû êàê âõîä: */
	SPI_DDRX = (1 << SPI_MOSI) | (1 << SPI_SCK) | (1 << SPI_SS) | (0 << SPI_MISO);

	/* Ðàçðåøèòü ðàáîòó SPI, ðåæèì Master,
	óñòàíîâèòü ñêîðîñòü òàêòîâ fck/16: */
	SPCR = (1 << SPE) | (1 << MSTR) | (0 << CPOL) | (0 << CPHA) | (1 << SPR1);

	SPI_PORTX |= (1 << SPI_SS);
}

void SPI_MasterTransmit(char *buffer, size_t length)
{
	SPI_PORTX &= ~(1 << SPI_SS);
	for (int i = 0; i < length; i++)
	{
		SPDR = buffer[i];
		while (!(SPSR & (1 << SPIF)))
		{
			// îæèäàíèå çàâåðøåíèÿ ïåðåäà÷è
		}
	}
	SPI_PORTX |= (1 << SPI_SS);
	_delay_ms(DELAY_MS);
}

char crc8(char *data, int len) {
	char crc = 0x00;
	while (len--) {
		crc ^= *data++;
		for (int i = 0; i < 8; i++) {
			if (crc & 0x80) {
				crc = (crc << 1) ^ 0x07;
				} else {
				crc <<= 1;
			}
		}
	}
	return crc;
}

//î÷èùåíèå ýêðàía
size_t display_all_clear(char color, char *buffer)
{
	buffer[0] = 0x01;
	buffer[1] = color;
	buffer[2] = crc8(buffer, 2);
	return 3;
}

//âûáîð òî÷êè
size_t display_set_page(char point, char *buffer)
{
	buffer[0] = 0x02;
	buffer[1] = point;
	buffer[2] = crc8(buffer, 2);
	return 3;
}

//äîáàâëåíèå íîâîãî ñèìâîëà è åãî ïàðàìåòðîâ
size_t display_add_simbol(char *str, size_t str_len, char *buffer)
{
	size_t idx = 0;
	buffer[idx] = 0x03;
	idx++;
	for (int i = 0; i < str_len; i++)
	{
		buffer[idx] = str[i];
		idx++;
	}
	buffer[idx] = crc8(buffer, idx);
	idx++;
	return idx;
}

//óäàëåíèå ñèìâîëà è åãî ïàðàìåòðîâ
size_t display_del_simbol(char number, char *buffer)
{
	buffer[0] = 0x04;
	buffer[1] = number;
	buffer[2] = crc8(buffer, 2);
	return 3;
}

//çàêðàøèâàíèå ïèêñåëÿ
size_t display_draw_pixel(char x, char y, char color, uint8_t *buffer)
{
	buffer[0] = 0x05;
	buffer[1] = x;
	buffer[2] = y;
	buffer[3] = color;
	buffer[4] = crc8(buffer, 4);
	return 5;
}

//ðèñóåò ëèíèþ
size_t display_draw_line(char x1, char y1, char x2, char y2, char color, char *buffer)
{
	buffer[0] = 0x06;
	buffer[1] = x1;
	buffer[2] = y1;
	buffer[3] = x2;
	buffer[4] = y2;
	buffer[5] = color;
	buffer[6] = crc8(buffer, 6);
	return 7;
}

//ðèñóåò êðóã
size_t display_draw_circle(char x, char y, char r, char color, uint8_t *buffer)
{
	buffer[0] = 0x07;
	buffer[1] = x;
	buffer[2] = y;
	buffer[3] = r;
	buffer[4] = color;
	buffer[5] = crc8(buffer, 5);
	return 6;
}

//ðèñóåò ïðÿìîóãîëüíèê
size_t display_draw_rectangle(char x, char y, char height, char width, char color, uint8_t *buffer)
{
	buffer[0] = 0x08;
	buffer[1] = x;
	buffer[2] = y;
	buffer[3] = height;
	buffer[4] = width;
	buffer[5] = color;
	buffer[6] = crc8(buffer, 6);
	return 7;
}