//
//  s95160_2.c	Layer 2 (Primitive figure elements)
//  Graphic LCD module "SUCCESS ELECTRONIC S95160" I/O library
//  Copyright(C)2013-2018 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: SUCCESS ELECTRONIC S95160-32. (Controller chip is SOLOMON SYSTECH SSD1289Z)
//		Interface: 16bits 8080 parallel by software control.
//			D0-15: 16bit 1 port or split 2 port
//			CS   : Any output port   See "a95160_1.c"
//			D/C  : Any output port
//			WR   : Any output port
//			RD   : Any output port
//			Y-   : Any analog port   See "a95160_t1.c"
//			X-   : Any analog port
//			Y+   : Any analog port
//			X+   : Any analog port
//		Screen direction:
//			X: 0=Left .. 239=Right
//			Y: 0=Top .. 319=Bottom
//		Color mode: 64k
//
//
//    Update history
//    ---------- ----- -------------------------------------------
//    2013.01.22 v0.0  First cording
//    2014.02.07 v0.1  Supported a LCD_LANDSCAPE_MODE
//    2014.03.09 v0.1  If Line width > 1 then center
//    2014.03.15 v0.2  Source file: s95160_2.c -> s95160_2.c + lcd_font.c + lcd_font.h
//    2014.03.16 v0.2  Add lcd_drawChar2(),lcd_drawStr2() and lcd_drawArc()
//    2014.03.16 v0.2  S95160_ARG_CHECK option
//    2017.04.15 v0.3  Supported a STM32 NUCLEO F446RE
//    2018.01.19 v0.3  Supported a LCD_UPSIDE_DOWN_MODE
//

#include "lcd_draw.h"					// API
#include "lcd_font.h"					// Pattern data
#include "s95160_1.h"					// Port I/O
#include "s95160_2.h"					// My def

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

void lcd_drawInit(void) {
//	long w;

//	for(w = 1000000; w > 0; --w);					// Wait  DEL 2017.04.15
#ifdef LCD_UPSIDE_DOWN_MODE										// ADD 2018.01.19
	lcd_portWriteCmd(LCD_COMMAND_DriverOutputControl, 0x793f);	// ADD 2018.01.19
#else															// ADD 2018.01.19
	lcd_portWriteCmd(LCD_COMMAND_DriverOutputControl, 0x3b3f);
#endif															// ADD 2018.01.19
	lcd_portWriteCmd(LCD_COMMAND_DisplayControl,      0x0021);
	lcd_portWriteCmd(LCD_COMMAND_Oscillator,          0x0001);
	lcd_portWriteCmd(LCD_COMMAND_DisplayControl,      0x0023);
	lcd_portWriteCmd(LCD_COMMAND_SleepMode,           0x0000);
//	for(w = 10000; w > 0; --w);						// Wait  DEL 2017.04.15
	lcd_portWait(30);								// Wait  ADD 2017.04.15
	lcd_portWriteCmd(LCD_COMMAND_DisplayControl,      0x0033);
#ifdef LCD_LANDSCAPE_MODE
	lcd_portWriteCmd(LCD_COMMAND_EntryMode,           0x6838);
#else
	lcd_portWriteCmd(LCD_COMMAND_EntryMode,           0x6830);
#endif

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

	return;
}

#ifdef LCD_LANDSCAPE_MODE
#define lcd_portSetAddr(x,y)	lcd_portWriteCmd(LCD_COMMAND_RAMaddressSetX, LCD_SCREEN_HEIGHT-(y)-1);lcd_portWriteCmd(LCD_COMMAND_RAMaddressSetY, (x))
#else
#define lcd_portSetAddr(x,y)	lcd_portWriteCmd(LCD_COMMAND_RAMaddressSetX, (x));lcd_portWriteCmd(LCD_COMMAND_RAMaddressSetY, (y))
#endif

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

#ifdef S95160_ARG_CHECK
	if (x < 0) {
		w += x;
		x = 0;
	}
	if (y < 0) {
		h += y;
		y = 0;
	}
#endif //S95160_ARG_CHECK
	for(; h > 0; --h) {
		lcd_portSetAddr(x, y);
		lcd_portIndex(LCD_COMMAND_WriteDataToGRAM);
		for(i = w; i > 0; --i) {
			lcd_portWriteData(color);
		}
		y++;
	}
	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;

#ifdef S95160_ARG_CHECK
	if (x < 0) {
		w += x;
		x = 0;
	}
	if (y < 0) {
		h += y;
		y = 0;
	}
#endif //S95160_ARG_CHECK
	for(; h > 0; --h) {
		lcd_portSetAddr(x, y);
		lcd_portIndex(LCD_COMMAND_WriteDataToGRAM);
		for(i = w; i > 0; --i) {
			lcd_portWriteData(*image++);
		}
		y++;
	}
	return;
}


//
//	Text
//
void lcd_drawChar(
	short			x,
	short			y,
	unsigned short	fcolor,
	unsigned short	bcolor,
	char			c)
{
	int	i;
	int	j;
	const unsigned char	*p;

#ifdef S95160_ARG_CHECK2
	if (x < 0) {
		x = 0;
	}
	if (y < 0) {
		y = 0;
	}
#endif //S95160_ARG_CHECK2
	p = &lcd_font5x7[((c & 0x7f) - ' ')][0];

	for(i = 0; i < LCD_FONT_HEIGHT; i++) {
		int	b;

		lcd_portSetAddr(x, y);
		lcd_portIndex(LCD_COMMAND_WriteDataToGRAM);

		if (i < 7) {				// CHG 2014.03.15
			b = (*p++) << (8 - LCD_FONT_WIDTH);
		} else {
			b = 0;
		}
		for(j = 0; j < LCD_FONT_WIDTH; j++) {
			if (b & 0x80) {
				lcd_portWriteData(fcolor);
			} else {
				lcd_portWriteData(bcolor);
			}
			b <<= 1;
		}

		y++;
	}
	return;
}

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

	x  = sx;
	y  = sy;

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

		c &= 0b01111111;

		// Visual char
		if (c >= ' ') {
			lcd_drawChar(x, y, fcolor, bcolor, c);
			x += LCD_FONT_WIDTH;

		// CLF
		} else if (c == '\n') {
			x = sx;
			y += LCD_FONT_HEIGHT;

		// CR
		} else if (c == '\r') {
			y += LCD_FONT_HEIGHT;

		}

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

	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;

#ifdef S95160_ARG_CHECK2
	if (x < 0) {
		x = 0;
	}
	if (y < 0) {
		y = 0;
	}
#endif //S95160_ARG_CHECK2
	pattern = lcd_fontGetPattern(font, c);
	for(i = 0; i < font->height; i++) {
		int	b;

		lcd_portSetAddr(x, y);
		lcd_portIndex(LCD_COMMAND_WriteDataToGRAM);

		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++;
	}
	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 S95160_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;
}
