// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

/*
 * lcd_emulator.c
 *
 *  Created on: Apr 1, 2019
 */

#include <stdio.h>
#include <stdint.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>
#include "lcd.h"

#define CR 13 // \r
#define TAB 9 // \n

static uint8_t lcd_row, lcd_col;
static char lcd_buf[MAX_ROWS][MAX_CHARS_PER_LINE];

static char enable_checks = 1;

const char* get_lcd_buf(void){
	return &lcd_buf[0][0];
}

void print_lcd(void){
	printf("(row,col)=(%d,%d)\n", lcd_row, lcd_col);
	printf("|---|---|---|---|\n");
	for (int i = 0; i < MAX_ROWS; i++){
		printf("|");
		for (int j = 0; j < MAX_CHARS_PER_LINE; j++){
			printf("%c", lcd_buf[i][j]);
		}
		printf("\n");
	}
	printf("|---|---|---|---|\n");
}

void LCD_Open(void){
	LCD_Clear();
}

void LCD_Clear(void){
	for (int i = 0; i < MAX_ROWS; i++){
		for (int j = 0; j < MAX_CHARS_PER_LINE; j++){
			lcd_buf[i][j] = ' ';
		}
	}
	lcd_row=0;
	lcd_col=0;
}

void LCD_GoTo(uint8_t row, uint8_t col){
	if (row < MAX_ROWS && col < MAX_CHARS_PER_LINE){
		lcd_row = row;
		lcd_col = col;
	} else {
		printf("LCD_GoTo(%u, %u) out of range\n", lcd_row, lcd_col);
	}
}

static void to_row(unsigned char row_to){
	if (row_to == 0){
		lcd_row = 0;
	} else {
		lcd_row = 1;
	}
	lcd_col = 0;
}

void LCD_OutString(const char *string, uint8_t max_chars) {
	const char *s;
	for (s = string; *s && max_chars > 0; s++, max_chars--) {
		TERMIO_PutChar(*s);
	}
}

void LCD_OutString_Initial(const char *string) {
	enable_checks = 0;
	assert(strlen(string) <= 32);
	LCD_OutString(string, 32);
	enable_checks = 1;
}

static int is_valid_character(char letter){
	if (isdigit(letter)){
		return 1;
	} else if(letter == CGRAM_EXP || letter == CGRAM_EXP_NEG){
		return 1;
	} else if(letter == '.' || letter == ' ' || letter == '-'){
		return 1;
	} else if(letter == '^'){
		return 1;
	} else if(letter == 'E' || letter == 'r' || letter == 'o'){
		return 1;
	}

	return 0;
}

short TERMIO_PutChar(unsigned char letter) {
	if (letter == CR || letter == '\n') {
		LCD_Clear();
	} else if (letter == TAB || letter == '\t') {
		if (lcd_row == 0) {
			to_row(1);
		} else {
			to_row(0);
		}
	}
	//warn if unknown character
	if (!is_valid_character(letter) && enable_checks) {
		printf("\nerror @%d,%d, invalid character %d\n",
				lcd_row, lcd_col, letter);
	}
	//add character to buf
	if (letter == CGRAM_EXP){
		lcd_buf[lcd_row][lcd_col] = 'E';
	} else if (letter == CGRAM_EXP_NEG) {
		lcd_buf[lcd_row][lcd_col] = '-';
	} else if (letter == CGRAM_DOWN) {
		lcd_buf[lcd_row][lcd_col] = 'V';
	} else {
		lcd_buf[lcd_row][lcd_col] = letter;
	}
	lcd_col++;
	//check if new line
	if (lcd_col >= MAX_CHARS_PER_LINE) {
		if (lcd_row == 0) {
			to_row(1);
		} else {
			to_row(0);
		}
	}

	return 1;
}

void LCD_ClearToEnd(uint8_t curr_row){
	while (lcd_col != 0 && lcd_row == curr_row){
		TERMIO_PutChar(' ');
	}
}

void LCD_OutNibble(uint8_t x){
	x &= 0xf; //ensure only bottom nibble
	if (x <= 9){
		TERMIO_PutChar(x + '0');
	} else {
		TERMIO_PutChar(x - 10 + 'a');
	}
}
