//
//  z180sn009_2.c	Drawing Layer (Primitive figure elements)
//  Graphic LCD module "Z180SN009" I/O library
//  Copyright(C)2019 Toyohiko Togashi tog001@nifty.com
//
//
//  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 <http://www.gnu.org/licenses/>
//
//
//	Description
//		I/O control for Graphic LCD module.
//		LCD module: Z180SN009. (Controller chip is Sitronix Technology ST7735S)
//		Screen direction:
//			X: 0=Left .. 127=Right
//			Y: 0=Top .. 159=Bottom
//		Color mode: 64k (RGB 5-6-5bit)
//
//
//    Update history
//    ---------- ----- -------------------------------------------
//    2019.01.24 v0.0  First cording
//

#include "lcd_draw.h"					// API
#include "lcd_font.h"					// Pattern data
#include "lcd_color.h"					// Color name
#include "z180sn009_1.h"				// Port I/O
#include "z180sn009_2.h"				// My def

//
// Command code ST7735S
//
#define LCD_COMMAND_NOP			0x00	// No operation

#define LCD_COMMAND_SWRESET		0x01	// Software reset

#define LCD_COMMAND_SLPIN		0x10	// Sleep in & booster off

#define LCD_COMMAND_SLPOUT		0x11	// Sleep out & booster on

#define LCD_COMMAND_PTLON		0x12	// Partial mode on

#define LCD_COMMAND_NORON		0x13	// Partial off (normal)

#define LCD_COMMAND_INVOFF		0x20	// Display inversion off (normal)

#define LCD_COMMAND_INVON		0x21	// Display inversion on

#define LCD_COMMAND_GAMSET		0x26	// Gamma curve select
#define LCD_DATA_GC(n)			(n)		// Gamma

#define LCD_COMMAND_DISPOFF		0x28	// Display off

#define LCD_COMMAND_DISPON		0x29	// Display on

#define LCD_COMMAND_CASET		0x2A	// Column address set
#define LCD_DATA_X(s,e)			((s)>>8),((s)&0xff),((e)>>8),((e)&0xff)

#define LCD_COMMAND_RASET		0x2B	// Row address set
#define LCD_DATA_Y(s,e)			((s)>>8),((s)&0xff),((e)>>8),((e)&0xff)

#define LCD_COMMAND_RAMWR		0x2C	// Memory write

#define LCD_COMMAND_RGBSET		0x2D	// LUT for indexed color

#define LCD_COMMAND_PTLAR		0x30	// Partial start/end address set
#define LCD_DATA_PL(s,e)		((s)>>8),((s)&0xff),((e)>>8),((e)&0xff)

#define LCD_COMMAND_SCRLAR		0x33	// Scroll area set
#define LCD_DATA_TFA(n)			((n)>>8),((n)&0xff)
#define LCD_DATA_VSA(n)			((n)>>8),((n)&0xff)
#define LCD_DATA_BFA(n)			((n)>>8),((n)&0xff)

#define LCD_COMMAND_TEOFF		0x34	// Tearing effect line off

#define LCD_COMMAND_TEON		0x35	// Tearing effect mode set & on
#define LCD_DATA_TEM(n)			(n)

#define LCD_COMMAND_MADCTL		0x36	// Memory data access control
#define LCD_DATA_MY				(1<<7)	// Row Address Order
#define LCD_DATA_MX				(1<<6)	// Column Address Order
#define LCD_DATA_MV				(1<<5)	// Row/Column Exchange
#define LCD_DATA_ML				(0<<4)	// Vertical Refresh Order
#define LCD_DATA_ML_Top_Bottom	(0<<4)	//
#define LCD_DATA_ML_Bottom_Top	(1<<4)	//
#define LCD_DATA_RGB			(0<<3)	// RGB-BGR ORDER
#define LCD_DATA_BGR			(1<<3)	// RGB-BGR ORDER
#define LCD_DATA_MH				(1<<2)	// Horizontal Refresh Order
#define LCD_DATA_MH_Left_Right	(0<<2)	//
#define LCD_DATA_MH_Right_Left	(1<<2)	//

#define LCD_COMMAND_VSCSAD		0x37	// Scroll start address of RAM
#define LCD_DATA_SSA(n)			((n)>>8),((n)&0xff)

#define LCD_COMMAND_IDMOFF		0x38	// Idle mode off

#define LCD_COMMAND_IDMON		0x39	// Idle mode on

#define LCD_COMMAND_COLMOD		0x3A	// Interface pixel format
#define LCD_DATA_IFPF(n)		(n)		//
#define LCD_DATA_IFPF_12BIT		3		// 4k
#define LCD_DATA_IFPF_16BIT		5		// 65k
#define LCD_DATA_IFPF_18BIT		6		// 262k


//
// Inline function
//
#define ABS(a)		((a) < 0 ? -(a) : (a))


//
// API
//
void lcd_drawInit(void) {
	lcd_portWriteCmd(LCD_COMMAND_NOP);
	lcd_portWriteCmd(LCD_COMMAND_SWRESET);
	lcd_portWait(120);
	lcd_portWriteCmd(LCD_COMMAND_SLPOUT);
	lcd_portWait(120);
#if  defined(LCD_LANDSCAPE_MODE) && defined(LCD_UPSIDE_DOWN_MODE)
	lcd_portWriteCmdPrm(LCD_COMMAND_MADCTL, LCD_DATA_MV | LCD_DATA_MY);
#elif defined(LCD_LANDSCAPE_MODE)
	lcd_portWriteCmdPrm(LCD_COMMAND_MADCTL, LCD_DATA_MV | LCD_DATA_MX);
#elif defined(LCD_PORTRATE_MODE) && defined(LCD_UPSIDE_DOWN_MODE)
	lcd_portWriteCmdPrm(LCD_COMMAND_MADCTL, LCD_DATA_MX | LCD_DATA_MY);
#elif defined(LCD_PORTRATE_MODE)
	lcd_portWriteCmdPrm(LCD_COMMAND_MADCTL, 0);
#else
#error "Not define LCD_PORTRATE_MODE, LCD_LANDSCAPE_MODE"
#endif
	lcd_portWriteCmdPrm(LCD_COMMAND_COLMOD, LCD_DATA_IFPF_16BIT);
	lcd_portWriteCmd(LCD_COMMAND_DISPON);
	lcd_portWritePeriod();

	lcd_drawRect(0,0, LCD_SCREEN_WIDTH,LCD_SCREEN_HEIGHT, LCD_COLOR_BLACK);
	return;
}

//
//	Rectangle
//
void lcd_drawRect(
	short			x,					// Upper left position
	short			y,					//
	short			w,					// width
	short			h,					// height
	unsigned short	color				//
) {
	short	i;
	short	x2,y2;

#ifdef ARG_CHECK
	if (x < 0) {
		w += x;
		x = 0;
	}
	if (y < 0) {
		h += y;
		y = 0;
	}
#endif //ARG_CHECK
	if (color != LCD_COLOR_TRANSPARENT) {
		x2 = x + w - 1;
		y2 = y + h - 1;
		lcd_portWriteCmd(LCD_COMMAND_CASET);
		lcd_portWriteData(x);
		lcd_portWriteData(x2);
		lcd_portWriteCmd(LCD_COMMAND_RASET);
		lcd_portWriteData(y);
		lcd_portWriteData(y2);
		lcd_portWriteCmd(LCD_COMMAND_RAMWR);
		for(i = w * h; i > 0; --i) {
			lcd_portWriteData(color);
		}
		lcd_portWritePeriod();
	}
	return;
}

//
//	Line
//
void lcd_drawLine(
	short			x1,					// Start point
	short			y1,					//
	short			x2,					// End point
	short			y2,					//
	short			s,					// Line size
	unsigned short	color				//
) {
	short			w;
	short			h;
	short			i;

	w = (short)x2 - (short)x1 + 1;
	h = (short)y2 - (short)y1 + 1;
	x1 -= (s / 2);						// ADD 2014.03.09
	y1 -= (s / 2);						// ADD 2014.03.09

	if (ABS(w) < ABS(h)) {				// 45..90deg
		if (h < 0) {
			for(i = 0; i > h; i--) {
				short	x,y;

				x = x1 + (i * w / h);
				y = y1 + i;
				lcd_drawRect(x, y, s, s, color);
			}
		} else {
			for(i = 0; i < h; i++) {
				short	x,y;

				x = x1 + (i * w / h);
				y = y1 + i;
				lcd_drawRect(x, y, s, s, color);
			}
		}
	} else {							// 0..45deg
		if (w < 0) {
			for(i = 0; i > w; i--) {
				short	x,y;

				x = x1 + i;
				y = y1 + (i * h / w);
				lcd_drawRect(x, y, s, s, color);
			}
		} else {
			for(i = 0; i < w; i++) {
				short	x,y;

				x = x1 + i;
				y = y1 + (i * h / w);
				lcd_drawRect(x, y, s, s, color);
			}
		}
	}

	return;
}


//
//	Image
//
void lcd_drawImage(
	short			x,					// Upper left position
	short			y,					//
	short			w,					// width
	short			h,					// height
	unsigned short	*image				// 16bit color / pixel left->right top->down scan
) {
	short	i;
	short	x2,y2;

#ifdef ARG_CHECK
	if (x < 0) {
		w += x;
		x = 0;
	}
	if (y < 0) {
		h += y;
		y = 0;
	}
#endif //ARG_CHECK
	x2 = x + w - 1;
	y2 = y + h - 1;
	lcd_portWriteCmd(LCD_COMMAND_CASET);
	lcd_portWriteData(x);
	lcd_portWriteData(x2);
	lcd_portWriteCmd(LCD_COMMAND_RASET);
	lcd_portWriteData(y);
	lcd_portWriteData(y2);
	lcd_portWriteCmd(LCD_COMMAND_RAMWR);
	for(i = w * h; i > 0; --i) {
		lcd_portWriteData(*image++);
	}
	lcd_portWritePeriod();
	return;
}


//
//	Text
//
void lcd_drawChar(
	short			x,
	short			y,
	unsigned short	fcolor,
	unsigned short	bcolor,
	char			c)
{
	lcd_drawChar2(x, y, fcolor, bcolor, c, LCD_FONT_6x8);
	return;
}

void lcd_drawStr(
	short			x,					// Upper left position
	short			y,					//
	unsigned short	fcolor,				// Text color
	unsigned short	bcolor,				// Background color
	char			*str				// '\0' terminated text
) {
	lcd_drawStr2(x, y, fcolor, bcolor, str, LCD_FONT_6x8);
	return;
}

										// ADD START 2014.03.16
//
//	Text with font
//
void lcd_drawChar2(
	short			x,
	short			y,
	unsigned short	fcolor,
	unsigned short	bcolor,
	char			c,
	const struct lcd_font *font)
{
	int	i;
	int	j;
	unsigned int	*pattern;
	short			x2,y2;

#ifdef ARG_CHECK2
	if (x < 0) {
		x = 0;
	}
	if (y < 0) {
		y = 0;
	}
#endif //ARG_CHECK2
	x2 = x + font->width - 1;
	y2 = y + font->height - 1;
	lcd_portWriteCmd(LCD_COMMAND_CASET);
	lcd_portWriteData(x);
	lcd_portWriteData(x2);
	lcd_portWriteCmd(LCD_COMMAND_RASET);
	lcd_portWriteData(y);
	lcd_portWriteData(y2);
	lcd_portWriteCmd(LCD_COMMAND_RAMWR);
	pattern = lcd_fontGetPattern(font, c);
	for(i = 0; i < font->height; i++) {
		int	b;

		b = 1 << (font->width-1);
		for(j = 0; j < font->width; j++) {
			if (b & *pattern) {
				lcd_portWriteData(fcolor);
			} else {
				lcd_portWriteData(bcolor);
			}
			b >>= 1;
		}

		y++;
		pattern++;
	}
	lcd_portWritePeriod();
	return;
}

void lcd_drawStr2(
	short			sx,					// Upper left position
	short			sy,					//
	unsigned short	fcolor,				// Text color
	unsigned short	bcolor,				// Background color
	char			*str,				// '\0' terminated text
	const struct lcd_font *font
) {
	short	x;
	short	y;
	char	c;

	x  = sx;
	y  = sy;

	while((c = *str++) != '\0') {

		c &= 0b01111111;

		// Visual char
		if (c >= ' ') {
			lcd_drawChar2(x, y, fcolor, bcolor, c, font);
			x += font->width;

		// CLF
		} else if (c == '\n') {
			x = sx;
			y += font->height;

		// CR
		} else if (c == '\r') {
			y += font->height;

		}

		// Screen range out
		if (x >= (LCD_SCREEN_WIDTH - font->width + 1)) {
			x = sx;
			y += font->height;
		}
		if (y >= (LCD_SCREEN_HEIGHT - font->height + 1)) {
			y = 0;
		}
	}

	return;
}

#ifdef ARC_ENABLE
#include <math.h>
//
//	Arc
//
void lcd_drawArc(
	short			x,					// Center point
	short			y,					//
	short			r,					//
	float			start,				//
	float			end,				//
	short			s,					// Line size
	unsigned short	color				//
) {
	short			x1;
	short			y1;
	short			x2;
	short			y2;
	float			a;

#define ARC_STEP (M_PI / 16.0)
	start += M_PI;
	end   += M_PI;
	x1 = cosf(start) * r + x;
	y1 = sinf(start) * r + y;
	for(a = start + ARC_STEP; a < end; a += ARC_STEP) {
		x2 = cosf(a) * r + x;
		y2 = sinf(a) * r + y;
		lcd_drawLine(x1, y1, x2, y2, s, color);
		x1 = x2;
		y1 = y2;
	}
	x2 = cosf(end) * r + x;
	y2 = sinf(end) * r + y;
	lcd_drawLine(x1, y1, x2, y2, s, color);
	return;
}
#endif //LCD_DRAW_ARC_ENABLE
										// ADD END 2014.03.16

//
//	Open
//
void lcd_drawOpen(void)	{
	lcd_portStart();
	lcd_drawInit();
	return;
}
