#include "st7789.h"
#include "ost_hal.h"
#include "debug.h"

/**
 * @brief Write command to ST7789 controller
 * @param cmd -> command to write
 * @return none
 */
static void ST7789_WriteCommand(uint8_t cmd)
{
	ost_display_dc_low();
	ost_display_ss_low();
	ost_display_transfer_byte(cmd);
	// HAL_SPI_Transmit(&ST7789_SPI_PORT, &cmd, sizeof(cmd), HAL_MAX_DELAY);
	ost_display_ss_high();
}

/**
 * @brief Write data to ST7789 controller
 * @param buff -> pointer of data buffer
 * @param buff_size -> size of the data buffer
 * @return none
 */
static void ST7789_WriteData(uint8_t *buff, uint32_t buff_size)
{
	ost_display_dc_high();
	ost_display_ss_low();

	// split data in small chunks because HAL can't send more than 64K at once

	while (buff_size > 0)
	{
		uint16_t chunk_size = buff_size > 65535 ? 65535 : buff_size;
		ost_display_transfer_multi(buff, chunk_size);
		// HAL_SPI_Transmit(&ST7789_SPI_PORT, buff, chunk_size, HAL_MAX_DELAY);

		buff += chunk_size;
		buff_size -= chunk_size;
	}

	ost_display_ss_high();
}
/**
 * @brief Write data to ST7789 controller, simplify for 8bit data.
 * data -> data to write
 * @return none
 */
static void ST7789_WriteSmallData(uint8_t data)
{
	ost_display_dc_high();
	ost_display_ss_low();

	// HAL_SPI_Transmit(&ST7789_SPI_PORT, &data, sizeof(data), HAL_MAX_DELAY);
	ost_display_transfer_byte(data);
	ost_display_ss_high();
}

/**
 * @brief Set the rotation direction of the display
 * @param m -> rotation parameter(please refer it in st7789.h)
 * @return none
 */
void ST7789_SetRotation(uint8_t m)
{
	ST7789_WriteCommand(ST7789_MADCTL); // MADCTL
	switch (m)
	{
	case 0:
		ST7789_WriteSmallData(ST7789_MADCTL_MX | ST7789_MADCTL_MY | ST7789_MADCTL_RGB);
		break;
	case 1:
		ST7789_WriteSmallData(ST7789_MADCTL_MY | ST7789_MADCTL_MV | ST7789_MADCTL_RGB);
		break;
	case 2:
		ST7789_WriteSmallData(ST7789_MADCTL_RGB);
		break;
	case 3:
		ST7789_WriteSmallData(ST7789_MADCTL_MX | ST7789_MADCTL_MV | ST7789_MADCTL_RGB);
		break;
	default:
		break;
	}
}

/**
 * @brief Set address of DisplayWindow
 * @param xi&yi -> coordinates of window
 * @return none
 */
static void ST7789_SetAddressWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
{
	ost_display_ss_low();
	uint16_t x_start = x0 + X_SHIFT, x_end = x1 + X_SHIFT;
	uint16_t y_start = y0 + Y_SHIFT, y_end = y1 + Y_SHIFT;

	/* Column Address set */
	ST7789_WriteCommand(ST7789_CASET);
	{
		uint8_t data[] = {x_start >> 8, x_start & 0xFF, x_end >> 8, x_end & 0xFF};
		ST7789_WriteData(data, sizeof(data));
	}

	/* Row Address set */
	ST7789_WriteCommand(ST7789_RASET);
	{
		uint8_t data[] = {y_start >> 8, y_start & 0xFF, y_end >> 8, y_end & 0xFF};
		ST7789_WriteData(data, sizeof(data));
	}
	/* Write to RAM */
	ST7789_WriteCommand(ST7789_RAMWR);
	ost_display_ss_high();
}

/**
 * @brief Initialize ST7789 controller
 * @param none
 * @return none
 */
void ST7789_Init(void)
{
#ifdef USE_DMA
	memset(disp_buf, 0, sizeof(disp_buf));
#endif
	// ost_system_delay_ms(25);
	// ST7789_RST_Clr();
	// ost_system_delay_ms(25);
	// ST7789_RST_Set();
	// ost_system_delay_ms(50);

	ST7789_WriteCommand(0x28); // Turn off display
	ST7789_WriteCommand(0x11); // Exit sleep mode
	ost_system_delay_ms(100);
	ST7789_WriteCommand(0x01); // Software Reset
	ost_system_delay_ms(150);

	// Does not word, read 00 42 C2 FF ?? (should be 00 85 85 52 )
	ost_display_dc_low();
	ost_display_ss_low();
	ost_display_transfer_byte(ST7789_RDDID);
	uint8_t buff[4] = {0xFF, 0xFF, 0xFF, 0xFF};
	ost_display_transfer_multi(buff, sizeof(buff));

	for (int i = 0; i < sizeof(buff); i++)
	{
		debug_printf("%X ", buff[i]);
	}
	ost_display_ss_high();

	ST7789_SetRotation(ST7789_ROTATION); //	MADCTL (Display Rotation)

	// ST7789_WriteCommand(ST7789_MADCTL);	// MADCTL
	// ST7789_WriteSmallData(0x88);

	ST7789_WriteCommand(ST7789_COLMOD); //	Set color mode
	ST7789_WriteSmallData(ST7789_COLOR_MODE_16bit);
	ST7789_WriteCommand(0xB2); //	Porch control
	{
		uint8_t data[] = {0x0C, 0x0C, 0x00, 0x33, 0x33};
		ST7789_WriteData(data, sizeof(data));
	}

	/* Internal LCD Voltage generator settings */
	ST7789_WriteCommand(0XB7);	 //	Gate Control
	ST7789_WriteSmallData(0x35); //	Default value
	ST7789_WriteCommand(0xBB);	 //	VCOM setting
	ST7789_WriteSmallData(0x2B); //	0.725v (default 0.75v for 0x20)
	ST7789_WriteCommand(0xC0);	 //	LCMCTRL
	ST7789_WriteSmallData(0x2C); //	Default value

	ST7789_WriteCommand(0xC2); //	VDV and VRH command Enable
	{
		uint8_t data[] = {0x01, 0xFF};
		ST7789_WriteData(data, sizeof(data));
	}

	ST7789_WriteCommand(0xC3);	 //	VRH set
	ST7789_WriteSmallData(0x11); //	+-4.45v (defalut +-4.1v for 0x0B)

	ST7789_WriteCommand(0xC4);	 //	VDV set
	ST7789_WriteSmallData(0x20); //	Default value

	ST7789_WriteCommand(0xC6);	 //	Frame rate control in normal mode
	ST7789_WriteSmallData(0x0F); //	Default value (60HZ)

	ST7789_WriteCommand(0xD0);	 //	Power control
	ST7789_WriteSmallData(0xA4); //	Default value
	ST7789_WriteSmallData(0xA1); //	Default value
	/**************** Division line ****************/

	ST7789_WriteCommand(0xE0);
	{
		uint8_t data[] = {0xD0, 0x00, 0x05, 0x0E, 0x15, 0x0D, 0x37, 0x43, 0x47, 0x09, 0x15, 0x12, 0x16, 0x19};
		ST7789_WriteData(data, sizeof(data));
	}

	ST7789_WriteCommand(0xE1);
	{
		uint8_t data[] = {0xD0, 0x00, 0x05, 0x0D, 0x0C, 0x06, 0x2D, 0x44, 0x40, 0x0E, 0x1C, 0x18, 0x16, 0x19};
		ST7789_WriteData(data, sizeof(data));
	}
	ST7789_WriteCommand(ST7789_INVOFF); //	Inversion OFF
	ST7789_WriteCommand(ST7789_SLPOUT); //	Out of sleep mode
	ST7789_WriteCommand(ST7789_NORON);	//	Normal Display on
	ST7789_WriteCommand(ST7789_DISPON); //	Main screen turned on

	ost_system_delay_ms(50);
	//	ST7789_Fill_Color(BLACK);				//	Fill with Black.
}

// rrrrrggggggbbbbb
uint8_t color_get_high(const color_t *c)
{
	return ((c->r & 248) | (c->g >> 5));
}
// rrrrrggggggbbbbb
uint8_t color_get_low(const color_t *c)
{
	return ((c->g & 28) << 3 | (c->b >> 3));
}

uint16_t color565(uint8_t r, uint8_t g, uint8_t b)
{
	return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
}

void ST7789_Fill_Line(uint16_t y, const uint8_t *data, const uint8_t *palette)
{
	// ili9341_adressSet(0, y, 320, 240);
	ST7789_SetAddressWindow(0, y, ST7789_WIDTH - 1, ST7789_HEIGHT - 1);
	ost_display_dc_high();
	ost_display_ss_low();

	color_t color;

	for (uint16_t i = 0; i < 320; i++)
	{
		uint8_t val = data[i];

		if (val > 15)
		{
			val = 0;
			debug_printf("***");
		}

		const uint8_t *palettePtr = &palette[val * 4];
		color.r = palettePtr[0];
		color.g = palettePtr[1];
		color.b = palettePtr[2];
		//   uint16_t pixel = color565(color.r, color.g, color.b);

		ost_display_transfer_byte(color_get_high(&color));
		ost_display_transfer_byte(color_get_low(&color));

		// pixel = ILI9341_PURPLE;
		// xchg_spi0(pixel>>8);
		// xchg_spi0(pixel&0xFF);
	}

	ost_display_ss_high();
}

void ST7789_Fill_LineRgb888(uint16_t y, const color_t *data)
{
	ST7789_SetAddressWindow(0, y, ST7789_WIDTH - 1, ST7789_HEIGHT - 1);
	ost_display_dc_high();
	ost_display_ss_low();

	color_t color;

	for (uint16_t i = 0; i < ST7789_WIDTH; i++)
	{
		uint16_t pixel = color565(data[i].r, data[i].g, data[i].b);
		ost_display_transfer_byte(pixel >> 8);
		ost_display_transfer_byte(pixel & 0xFF);
	}
}

/**
 * @brief Fill the DisplayWindow with single color
 * @param color -> color to Fill with
 * @return none
 */
void ST7789_Fill_Color(uint16_t color)
{
	uint16_t i;
	ST7789_SetAddressWindow(0, 0, ST7789_WIDTH - 1, ST7789_HEIGHT - 1);
	ost_display_ss_low();

#ifdef USE_DMA
	for (i = 0; i < ST7789_HEIGHT / HOR_LEN; i++)
	{
		memset(disp_buf, color, sizeof(disp_buf));
		ST7789_WriteData(disp_buf, sizeof(disp_buf));
	}
#else
	uint16_t j;
	for (i = 0; i < ST7789_WIDTH; i++)
		for (j = 0; j < ST7789_HEIGHT; j++)
		{
			uint8_t data[] = {color >> 8, color & 0xFF};
			ST7789_WriteData(data, sizeof(data));
		}
#endif
	ost_display_ss_high();
}

/**
 * @brief Draw a Pixel
 * @param x&y -> coordinate to Draw
 * @param color -> color of the Pixel
 * @return none
 */
void ST7789_DrawPixel(uint16_t x, uint16_t y, uint16_t color)
{
	if ((x < 0) || (x >= ST7789_WIDTH) ||
		(y < 0) || (y >= ST7789_HEIGHT))
		return;

	ST7789_SetAddressWindow(x, y, x, y);
	uint8_t data[] = {color >> 8, color & 0xFF};
	ost_display_ss_low();
	ST7789_WriteData(data, sizeof(data));
	ost_display_ss_high();
}

/**
 * @brief Fill an Area with single color
 * @param xSta&ySta -> coordinate of the start point
 * @param xEnd&yEnd -> coordinate of the end point
 * @param color -> color to Fill with
 * @return none
 */
void ST7789_Fill(uint16_t xSta, uint16_t ySta, uint16_t xEnd, uint16_t yEnd, uint16_t color)
{
	if ((xEnd < 0) || (xEnd >= ST7789_WIDTH) ||
		(yEnd < 0) || (yEnd >= ST7789_HEIGHT))
		return;
	ost_display_ss_low();
	uint16_t i, j;
	ST7789_SetAddressWindow(xSta, ySta, xEnd, yEnd);
	for (i = ySta; i <= yEnd; i++)
		for (j = xSta; j <= xEnd; j++)
		{
			uint8_t data[] = {color >> 8, color & 0xFF};
			ST7789_WriteData(data, sizeof(data));
		}
	ost_display_ss_high();
}

/**
 * @brief Draw a big Pixel at a point
 * @param x&y -> coordinate of the point
 * @param color -> color of the Pixel
 * @return none
 */
void ST7789_DrawPixel_4px(uint16_t x, uint16_t y, uint16_t color)
{
	if ((x <= 0) || (x > ST7789_WIDTH) ||
		(y <= 0) || (y > ST7789_HEIGHT))
		return;
	ost_display_ss_low();
	ST7789_Fill(x - 1, y - 1, x + 1, y + 1, color);
	ost_display_ss_high();
}

/**
 * @brief Draw an Image on the screen
 * @param x&y -> start point of the Image
 * @param w&h -> width & height of the Image to Draw
 * @param data -> pointer of the Image array
 * @return none
 */
void ST7789_DrawImage(uint16_t x, uint16_t y, uint16_t w, uint16_t h, const uint16_t *data)
{
	if ((x >= ST7789_WIDTH) || (y >= ST7789_HEIGHT))
		return;
	if ((x + w - 1) >= ST7789_WIDTH)
		return;
	if ((y + h - 1) >= ST7789_HEIGHT)
		return;

	ost_display_ss_low();
	ST7789_SetAddressWindow(x, y, x + w - 1, y + h - 1);
	ST7789_WriteData((uint8_t *)data, sizeof(uint16_t) * w * h);
	ost_display_ss_high();
}

/**
 * @brief Invert Fullscreen color
 * @param invert -> Whether to invert
 * @return none
 */
void ST7789_InvertColors(uint8_t invert)
{
	ost_display_ss_low();
	ST7789_WriteCommand(invert ? 0x21 /* INVON */ : 0x20 /* INVOFF */);
	ost_display_ss_high();
}

/*
void ST7789_Test(void)
{
	ST7789_Fill_Color(WHITE);
	ost_system_delay_ms(1000);
	ST7789_WriteString(10, 20, "Speed Test", Font_11x18, RED, WHITE);
	ost_system_delay_ms(1000);
	ST7789_Fill_Color(CYAN);
	ost_system_delay_ms(500);
	ST7789_Fill_Color(RED);
	ost_system_delay_ms(500);
	ST7789_Fill_Color(BLUE);
	ost_system_delay_ms(500);
	ST7789_Fill_Color(GREEN);
	ost_system_delay_ms(500);
	ST7789_Fill_Color(YELLOW);
	ost_system_delay_ms(500);
	ST7789_Fill_Color(BROWN);
	ost_system_delay_ms(500);
	ST7789_Fill_Color(DARKBLUE);
	ost_system_delay_ms(500);
	ST7789_Fill_Color(MAGENTA);
	ost_system_delay_ms(500);
	ST7789_Fill_Color(LIGHTGREEN);
	ost_system_delay_ms(500);
	ST7789_Fill_Color(LGRAY);
	ost_system_delay_ms(500);
	ST7789_Fill_Color(LBBLUE);
	ost_system_delay_ms(500);
	ST7789_Fill_Color(WHITE);
	ost_system_delay_ms(500);

	ST7789_WriteString(10, 10, "Font test.", Font_16x26, GBLUE, WHITE);
	ST7789_WriteString(10, 50, "Hello Steve!", Font_7x10, RED, WHITE);
	ST7789_WriteString(10, 75, "Hello Steve!", Font_11x18, YELLOW, WHITE);
	ST7789_WriteString(10, 100, "Hello Steve!", Font_16x26, MAGENTA, WHITE);
	ost_system_delay_ms(1000);

	ST7789_Fill_Color(RED);
	ST7789_WriteString(10, 10, "Rect./Line.", Font_11x18, YELLOW, BLACK);
	ST7789_DrawRectangle(30, 30, 100, 100, WHITE);
	ost_system_delay_ms(1000);

	ST7789_Fill_Color(RED);
	ST7789_WriteString(10, 10, "Filled Rect.", Font_11x18, YELLOW, BLACK);
	ST7789_DrawFilledRectangle(30, 30, 50, 50, WHITE);
	ost_system_delay_ms(1000);

	ST7789_Fill_Color(RED);
	ST7789_WriteString(10, 10, "Circle.", Font_11x18, YELLOW, BLACK);
	ST7789_DrawCircle(60, 60, 25, WHITE);
	ost_system_delay_ms(1000);

	ST7789_Fill_Color(RED);
	ST7789_WriteString(10, 10, "Filled Cir.", Font_11x18, YELLOW, BLACK);
	ST7789_DrawFilledCircle(60, 60, 25, WHITE);
	ost_system_delay_ms(1000);

	ST7789_Fill_Color(RED);
	ST7789_WriteString(10, 10, "Triangle", Font_11x18, YELLOW, BLACK);
	ST7789_DrawTriangle(30, 30, 30, 70, 60, 40, WHITE);
	ost_system_delay_ms(1000);

	ST7789_Fill_Color(RED);
	ST7789_WriteString(10, 10, "Filled Tri", Font_11x18, YELLOW, BLACK);
	ST7789_DrawFilledTriangle(30, 30, 30, 70, 60, 40, WHITE);
	ost_system_delay_ms(1000);

	//	If FLASH cannot storage anymore datas, please delete codes below.
	ST7789_Fill_Color(WHITE);
	ST7789_DrawImage(0, 0, 128, 128, (uint16_t *)saber);
	ost_system_delay_ms(3000);
}
*/