#include "main.h"
#include "gpio.h"
#include "stm32f4xx_ll_gpio.h"
#include <string.h>
#include "lcd.h"

static char display[32];
int display_putchar(char c);

void lcd_clock(void)
{
	HAL_GPIO_WritePin(LCD_E_GPIO_Port,LCD_E_Pin,1);
	HAL_Delay(1);
	HAL_GPIO_WritePin(LCD_E_GPIO_Port,LCD_E_Pin,0);
	HAL_Delay(1);
	return;
}

void lcd_dwrite(uint8_t d)
{
	HAL_GPIO_WritePin(LCD_RS_GPIO_Port,LCD_RS_Pin,1);
	GPIOA->BSRR=(d>>4)<<(6+0);	// Bit sets
	GPIOA->BSRR=((~d)>>4)<<(6+16);	// Bit clears
	lcd_clock();
	GPIOA->BSRR=(d&0x0F)<<(6+0);	// Bit sets
	GPIOA->BSRR=((~d)&0x0F)<<(6+16);	// Bit clears
	lcd_clock();

	return;
}

void lcd_iwrite(uint8_t d, uint8_t half)
{
	HAL_GPIO_WritePin(LCD_RS_GPIO_Port,LCD_RS_Pin,0);
	GPIOA->BSRR=(d>>4)<<(6+0);	// Bit sets
	GPIOA->BSRR=((~d)>>4)<<(6+16);	// Bit clears
	lcd_clock();
	if (half==0) {
		GPIOA->BSRR=(d&0x0F)<<(6+0);	// Bit sets
		GPIOA->BSRR=((~d)&0x0F)<<(6+16);	// Bit clears
		lcd_clock();
	}

	return;
}

void lcd_init(void)
{
	memset(display,' ',32);

	HAL_Delay(16);
	lcd_iwrite(0x30,1);	// Function set 8 bits
	HAL_Delay(5);
	lcd_iwrite(0x30,1);	// Function set 8 bits
	HAL_Delay(5);
	lcd_iwrite(0x30,1);	// Function set 8 bits
	HAL_Delay(1);
	lcd_iwrite(0x20,1);	// Function set 4 bits
	HAL_Delay(1);
	lcd_iwrite(0x28,0);	// Font and Lines
	HAL_Delay(1);
	lcd_iwrite(0x08,0);	// Display off
	HAL_Delay(1);
	lcd_iwrite(0x01,0);	// Clear
	HAL_Delay(16);
	lcd_iwrite(0x0C,0);	// Entry Mode set
	HAL_Delay(1);
	lcd_iwrite(0x0F,0);	// Display on
	HAL_Delay(1);

	return;
}

// Put a character at a specific location
void lcd_putchar(uint32_t row, uint32_t col, char c)
{
	// Remember that the LCD has 2 logical rows but on physical
	// phy row 1 cols 0-7 are at address 0x00 to 0x07
	// phy row 2 cols 8-15 are at address 0x40 to 0x47

	uint32_t phy_addr;
	uint32_t log_row;
	uint32_t log_col;

	if (row>LCD_PHY_ROWS) {
		return;
	}
	if (col>LCD_PHY_COLS) {
		return;
	}
	phy_addr=row*LCD_PHY_COLS+col;
	log_row=phy_addr/LCD_LOG_COLS;
	log_col=phy_addr%LCD_LOG_COLS;

	lcd_iwrite(0x80 | (log_row*0x40+log_col),0);
	lcd_dwrite(c);

	return;
}

int _write(int file, char *ptr, int len)
{
	static uint32_t i=0;
	int cnt;


	cnt=len;
	if (cnt>16) {
		cnt=16;
	}
	for (i=0;i<cnt;i++) {
		display_putchar(ptr[i]);
	}

	// Copy out data to LCD
	for (i=0;i<16;i++) {
		lcd_putchar(0,i,display[i]);
	}
	for (i=0;i<16;i++) {
		lcd_putchar(1,i,display[16+i]);
	}

	return cnt;
}

int display_putchar(char c)
{
	static uint32_t cr=0;
	static uint32_t j=0;
	uint32_t i=0;

	if (c=='\n') {
		cr=1;
		return 1;
	}

	if (cr) {
		for (i=0;i<16;i++) {
			display[i]=display[16+i];
		}
		memset(&display[16], ' ', 16);
		cr=0;
		j=0;
	}
	display[16+j]=c;
	j++;
	if (j>=16) {
		j=15;
	}
	return 1;
}
