// File Name	: lcd.c

//=================================================================================
// LCD
// PortA is output for data, and PortD is output for control
// PortA: PA
// PD7 PD6 PD5 PD4 PD3 PD2 PD1 PD0
//  -   -  Cnt RS  R/W E    -   -
// PA7 PA6 PA5 PA4 PA3 PA2 PA1 PA0
//  D7  D6  D5  D4  D3  D2  D1  D0
//
// Reference at
//	http://www.stanford.edu/class/ee281/projects/aut2001/remotecontrol/code.htm
//
// By: Gang Xie
//		Courtesy of Pascal Stang: some segments/functions are directly from Pascal.
//=================================================================================

#ifndef WIN32
	#include 
	#include 
	#include 
	#include 
#endif

#include "lcd.h"
#include "timer.h"

// custom LCD characters
static unsigned char __attribute__ ((progmem)) LcdCustomChar[] =
{
	0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, // 0. 0/5 full progress block
	0x00, 0x1F, 0x10, 0x10, 0x10, 0x10, 0x1F, 0x00, // 1. 1/5 full progress block
	0x00, 0x1F, 0x18, 0x18, 0x18, 0x18, 0x1F, 0x00, // 2. 2/5 full progress block
	0x00, 0x1F, 0x1C, 0x1C, 0x1C, 0x1C, 0x1F, 0x00, // 3. 3/5 full progress block
	0x00, 0x1F, 0x1E, 0x1E, 0x1E, 0x1E, 0x1F, 0x00, // 4. 4/5 full progress block
	0x00, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, // 5. 5/5 full progress block
	0x03, 0x07, 0x0F, 0x1F, 0x0F, 0x07, 0x03, 0x00, // 6. rewind arrow
	0x00, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, // 7. stop block
	0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x00, // 8. pause bars
	0x18, 0x1C, 0x1E, 0x1F, 0x1E, 0x1C, 0x18, 0x00, // 9. fast-forward arrow
	0x00, 0x04, 0x04, 0x0E, 0x0E, 0x1F, 0x1F, 0x00, // 10. scroll up arrow
	0x00, 0x1F, 0x1F, 0x0E, 0x0E, 0x04, 0x04, 0x00, // 11. scroll down arrow
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 12. blank character
	0x00, 0x0E, 0x19, 0x15, 0x13, 0x0E, 0x00, 0x00,	// 13. animated play icon frame 0
	0x00, 0x0E, 0x15, 0x15, 0x15, 0x0E, 0x00, 0x00,	// 14. animated play icon frame 1
	0x00, 0x0E, 0x13, 0x15, 0x19, 0x0E, 0x00, 0x00,	// 15. animated play icon frame 2
	0x00, 0x0E, 0x11, 0x1F, 0x11, 0x0E, 0x00, 0x00,	// 16. animated play icon frame 3
};

#define LCD_DELAY		asm volatile ("nop"); asm volatile ("nop")

extern RtcTimeType	gRtcTime;
extern PumpInfoType	gPumpInfo;
extern SysStateType	gSysState;
extern RtcTimeType	gAlarmTime;


//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void lcdInitHW(void)
{
	// initialize I/O portss
	// initialize LCD control lines
	cbi(LCD_CTRL_PORT, LCD_CTRL_RS);
	cbi(LCD_CTRL_PORT, LCD_CTRL_RW);
	cbi(LCD_CTRL_PORT, LCD_CTRL_E);

	//This line is necessary in my LCD. Maybe some bits should be initialized.
	outb(LCD_CTRL_DDR, 0xFF);
	// initialize LCD control lines to output
	sbi(LCD_CTRL_DDR, LCD_CTRL_RS);
	sbi(LCD_CTRL_DDR, LCD_CTRL_RW);
	sbi(LCD_CTRL_DDR, LCD_CTRL_E);
	// initialize LCD data port to input
	// initialize LCD data lines to pull-up
	outb(LCD_DATA_DDR, 0x00);						// set data I/O lines to input (8bit)
	outb(LCD_DATA_POUT, 0xFF);						// set pull-ups to on (8bit)
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void lcdBusyWait(void)
{
	// wait until LCD busy bit goes to zero
	// do a read from control register
	cbi(LCD_CTRL_PORT, LCD_CTRL_RS);				// set RS to "control"
	outb(LCD_DATA_DDR, 0x00);						// set data I/O lines to input (8bit)
	outb(LCD_DATA_POUT, 0xFF);						// set pull-ups to on (8bit)
	sbi(LCD_CTRL_PORT, LCD_CTRL_RW);				// set R/W to "read"
	sbi(LCD_CTRL_PORT, LCD_CTRL_E);					// set "E" line
	LCD_DELAY;										// wait
	while(inp(LCD_DATA_PIN) & 1<=0
// y >=0
//---------------------------------------------------------------------------------
void lcdGotoXY(u08 x, u08 y)
{
	register u08 DDRAMAddr;

	// remap lines into proper order
	switch(y)
	{
	case 0: DDRAMAddr = LCD_LINE0_DDRAMADDR+x; break;
	case 1: DDRAMAddr = LCD_LINE1_DDRAMADDR+x; break;
	case 2: DDRAMAddr = LCD_LINE2_DDRAMADDR+x; break;
	case 3: DDRAMAddr = LCD_LINE3_DDRAMADDR+x; break;
	default: DDRAMAddr = LCD_LINE0_DDRAMADDR+x;
	}

	// set data address
	lcdControlWrite(1< pixelprogress )
		{
			// this is a partial or empty block
			if( ((i*PROGRESSPIXELS_PER_CHAR)) > pixelprogress )
			{
				// this is an empty block
				// use space character?
				c = 0;
			}
			else
			{
				// this is a partial block
				c = pixelprogress % PROGRESSPIXELS_PER_CHAR;
			}
		}
		else
		{
			// this is a full block
			c = 5;
		}
		
		// write character to display
		lcdDataWrite(c);
	}
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void lcdInitDisp(void)
{
	//012345678901234567890123
	//??/??/????-???  ??:??:??
	lcdDispTwoDgtls(0, 0, gRtcTime.month);
	lcdDispChar(2, 0, 0x2F);					///
	lcdDispTwoDgtls(3, 0, gRtcTime.day);
	lcdDispChar(5, 0, 0x2F);					///
	lcdDispFourDgtls(6, 0, gRtcTime.year);
	lcdDispChar(10, 0, 0x2D);					//-
	lcdDispDay(11, 0, gRtcTime.sunsat);

	lcdDispTwoDgtls(16, 0, gRtcTime.hour);
	lcdDispChar(18, 0, 0x3A);					//:
	lcdDispTwoDgtls(19, 0, gRtcTime.minute);
	lcdDispChar(21, 0, 0x3A);					//:
	lcdDispTwoDgtls(22, 0, gRtcTime.second);

	lcdDispPumpInfo();
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void lcdUpdateDisp(void)
{
	u08 x, y;
	u08 all = 0;
	
	//whether to redraw a whole screen
	if (gSysState.displayPage != gSysState.displayPrevPage)
	{
		all = 1;
		lcdClear();
		gSysState.displayPrevPage = gSysState.displayPage;
	}

	//display on LCD
	if (gSysState.displayPage == kPage0_Norm)
	{
		//if previously another page was showed
		if (all)
			lcdInitDisp();
		else
		{
			// first line
			//012345678901234567890123
			//mm/dd/yyyy-sun  hh:mm:ss
			if (gSysState.update_rtc_month)
			{
				lcdDispTwoDgtls(0, 0, gRtcTime.month);
				gSysState.update_rtc_month = 0;
			}
			if (gSysState.update_rtc_day)
			{
				lcdDispTwoDgtls(3, 0, gRtcTime.day);
				lcdDispDay(11, 0, gRtcTime.sunsat);
				gSysState.update_rtc_day = 0;
			}
			if (gSysState.update_rtc_year)
			{
				lcdDispFourDgtls(6, 0, gRtcTime.year);
				gSysState.update_rtc_year = 0;
			}
			if (gSysState.update_rtc_sunsat)
			{
				lcdDispDay(11, 0, gRtcTime.sunsat);
				gSysState.update_rtc_sunsat = 0;
			}
			if (gSysState.update_rtc_hour)
			{
				lcdDispTwoDgtls(16, 0, gRtcTime.hour);
				gSysState.update_rtc_hour = 0;
			}
			if (gSysState.update_rtc_minute)
			{
				lcdDispTwoDgtls(19, 0, gRtcTime.minute);
				gSysState.update_rtc_minute = 0;
			}
			if (gSysState.update_rtc_second)
			{
				lcdDispTwoDgtls(22, 0, gRtcTime.second);
				gSysState.update_rtc_second = 0;
			}
			if (gSysState.update_info)
			{
				lcdDispPumpInfo();
				gSysState.update_info = 0;
			}
		}
	}
	else
	{
		switch (gSysState.displayPage)
		{
		case kPage1_Alarm:			lcdDispAlarm(all);			break;
		case kPage2_PumpActive:		lcdDispPumpActive(all);		break;
		case kPage3_PumpDuration:	lcdDispPumpDuration(all);	break;
		case kPage4_PumpFreq:		lcdDispPumpFrequency(all);	break;
		case kPage5_PumpNext:		lcdDispPumpNext(all);		break;
		}
	}

	//blink the cursor
	if (gSysState.displayState == kState_Edit)
	{
		switch (gSysState.displayPage)
		{
		case kPage0_Norm:
			//012345678901234567890123
			//mm/dd/yyyy-sun  hh:mm:ss
			y = 0;
			switch (gSysState.fieldAdjusted)
			{
			case 0: x = 1;	break;
			case 1: x = 4;	break;
			case 2: x = 9;	break;
			case 3: x = 13;	break;
			case 4: x = 17;	break;
			case 5: x = 20;	break;
			default:x = 23;	break;
			}
			break;
		case kPage1_Alarm:
			//012345678901234567890123
			//Alarm at: ??:??:??
			//Sound: o??         *----
			switch (gSysState.fieldAdjusted)
			{
			case 0: x = 11; y = 0;	break;
			case 1: x = 14;	y = 0;	break;
			case 2: x = 17; y = 0;	break;
			default:x = 7;	y = 1;	break;
			}
			break;
		case kPage2_PumpActive:
			//012345678901234567890123
			//Pump: active/inactive
			//Sound: o??         -*---
			switch (gSysState.fieldAdjusted)
			{
			case 0: x = 6; y = 0;	break;
			default:x = 7; y = 1;	break;
			}
			break;
		case kPage3_PumpDuration:
			//012345678901234567890123
			//Pump for ?? minutes and
			//?? seconds         --*--
			switch (gSysState.fieldAdjusted)
			{
			case 0: x = 10; y = 0;	break;
			default:x = 1; y = 1;	break;
			}
			break;
		case kPage4_PumpFreq:
			//012345678901234567890123
			//every ?? days ?? hours
			//and ?? minutes     ---*-
			switch (gSysState.fieldAdjusted)
			{
			case 0: x = 7; y = 0;	break;
			case 1: x = 15;	y = 0;	break;
			default:x = 5;	y = 1;	break;
			}
			break;
		default:	// kPage5_PumpNext:
			switch (gSysState.fieldAdjusted)
			{
			case 0: x = 17; y = 0;	break;
			case 1: x = 20;	y = 0;	break;
			case 2: x = 23; y = 0;	break;
			case 3: x = 4;	y = 1;	break;
			case 4: x = 7; y = 1;	break;
			default:x = 12;	y = 1;	break;
			}
			break;
		}
		lcdGotoXY(x, y);
		lcdBlinkCursor();
	}
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void lcdDispDay(u08 x, u08 y, u08 sunsat)
{
	switch (sunsat)
	{
	case 0: lcdDispStr(x, y, "Sun"); break;
	case 1: lcdDispStr(x, y, "Mon"); break;
	case 2: lcdDispStr(x, y, "Tue"); break;
	case 3: lcdDispStr(x, y, "Wed"); break;
	case 4: lcdDispStr(x, y, "Thu"); break;
	case 5: lcdDispStr(x, y, "Fri"); break;
	default: lcdDispStr(x, y, "Sat"); break;
	}
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void lcdDispPumpInfo(void)
{
	u16 tmp;

	if ((gSysState.pumpActivated == 1) && (gPumpInfo.duration.year > 0))
	{
		tmp = gPumpInfo.totDurInSeconds - gPumpInfo.duration.year;
		lcdGotoXY(0,1);
		lcdProgressBar(tmp, gPumpInfo.totDurInSeconds, 24);
		return;
	}

	                    //012345678901234567890123
	switch (gSysState.update_info_num)
	{
	case 0: lcdDispStr(0, 1, "               Welcome !"); break;
	case 1: lcdDispStr(0, 1, "              Welcome ! "); break;
	case 2: lcdDispStr(0, 1, "             Welcome !  "); break;
	case 3: lcdDispStr(0, 1, "            Welcome !   "); break;
	case 4: lcdDispStr(0, 1, "           Welcome !    "); break;
	case 5: lcdDispStr(0, 1, "          Welcome !     "); break;
	case 6: lcdDispStr(0, 1, "         Welcome !      "); break;
	case 7: lcdDispStr(0, 1, "        Welcome !       "); break;
	case 8: lcdDispStr(0, 1, "       Welcome !        "); break;
	case 9: lcdDispStr(0, 1, "      Welcome !         "); break;
	case 10: lcdDispStr(0, 1, "     Welcome !          "); break;
	case 11: lcdDispStr(0, 1, "    Welcome !           "); break;
	case 12: lcdDispStr(0, 1, "   Welcome !            "); break;
	case 13: lcdDispStr(0, 1, "  Welcome !             "); break;
	case 14: lcdDispStr(0, 1, " Welcome !              "); break;
	case 15: lcdDispStr(0, 1, "Welcome !               "); break;

	case 16:
	case 17:
	case 18:
		if (gSysState.pumpActivated)
		{
							//012345678901234567890123
							//Next pump on mm/dd hh:mm
			lcdDispStr(0, 1, "Next pump on   /     :  ");
			lcdDispTwoDgtls(13, 1, gPumpInfo.next.month);
			lcdDispTwoDgtls(16, 1, gPumpInfo.next.day);
			lcdDispTwoDgtls(19, 1, gPumpInfo.next.hour);
			lcdDispTwoDgtls(22, 1, gPumpInfo.next.minute);
		}
		else
			lcdDispStr(0, 1, "The pump is inactive.   ");
		break;

	case 19:
	case 20:
	case 21:
							//012345678901234567890123
			//lcdDispStr(0, 1, " Stanford EE281 Project ");
			lcdDispStr(0, 1, "     Gang loves Yue .     ");
		break;
	}
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void lcdDispAlarm(u08 all)
{
	// first line
	//012345678901234567890123
	//Alarm at: ??:??:??
	if (all)
		lcdDispStr(0, 0, "Alarm at:");
	if (all || gSysState.update_alm_hour)
	{
		gSysState.update_alm_hour = 0;
		lcdDispTwoDgtls(10, 0, gAlarmTime.hour);
	}
	if (all)
		lcdDispChar(12, 0, 0x3A);					//:
	if (all || gSysState.update_alm_minute)
	{
		gSysState.update_alm_minute = 0;
		lcdDispTwoDgtls(13, 0, gAlarmTime.minute);
	}
	if (all)
		lcdDispChar(15, 0, 0x3A);					//:
	if (all || gSysState.update_alm_second)
	{
		gSysState.update_alm_second = 0;
		lcdDispTwoDgtls(16, 0, gAlarmTime.second);
	}

	// second line
	//012345678901234567890123
	//Sound: o??         *----
	if (all)
		lcdDispStr(0, 1, "Sound:");
	if (all || gSysState.update_alm_sound)
	{
		gSysState.update_alm_sound = 0;
		if (gSysState.alarmOn)
			lcdDispStr(7, 1, "on ");
		else
			lcdDispStr(7, 1, "off");
	}
	if (all)
		lcdDispStr(19, 1, "*----");

	all = 0;
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void lcdDispPumpActive(u08 all)
{
	// first line
	//012345678901234567890123
	//Pump: active/inactive
	if (all)
		lcdDispStr(0, 0, "Pump:");
	if (all || gSysState.update_pa_active)
	{
		gSysState.update_pa_active = 0;
		if (gSysState.pumpActivated)
			lcdDispStr(6, 0, "active  ");
		else
			lcdDispStr(6, 0, "inactive");
	}

	// second line
	//012345678901234567890123
	//Sound: o??         -*---
	if (all)
		lcdDispStr(0, 1, "Sound:");
	if (all || gSysState.update_pa_sound)
	{
		gSysState.update_pa_sound = 0;
		if (gSysState.pumpSoundOn)
			lcdDispStr(7, 1, "on ");
		else
			lcdDispStr(7, 1, "off");
	}
	if (all)
		lcdDispStr(19, 1, "-*---");

	all = 0;
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void lcdDispPumpDuration(u08 all)
{
	// first line
	//012345678901234567890123
	//Pump for ?? minutes and
	if (all)
		lcdDispStr(0, 0, "Pump for");
	if (all || gSysState.update_pd_minute)
	{
		gSysState.update_pd_minute = 0;
		lcdDispTwoDgtls(9, 0, gPumpInfo.duration.minute);
	}
	if (all)
		lcdDispStr(12, 0, "minutes and");

	//012345678901234567890123
	//?? seconds         --*--
	if (all || gSysState.update_pd_second)
	{
		gSysState.update_pd_second = 0;
		lcdDispTwoDgtls(0, 1, gPumpInfo.duration.second);
	}
	if (all)
		lcdDispStr(3, 1, "seconds");
	if (all)
		lcdDispStr(19, 1, "--*--");
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void lcdDispPumpFrequency(u08 all)
{
	// first line
	//012345678901234567890123
	//every ?? days ?? hours
	if (all)
		lcdDispStr(0, 0, "every");
	if (all || gSysState.update_pf_day)
	{
		gSysState.update_pf_day = 0;
		lcdDispTwoDgtls(6, 0, gPumpInfo.frequency.day);
	}
	if (all)
		lcdDispStr(9, 0, "days");
	if (all || gSysState.update_pf_hour)
	{
		gSysState.update_pf_hour = 0;
		lcdDispTwoDgtls(14, 0, gPumpInfo.frequency.hour);
	}
	if (all)
		lcdDispStr(17, 0, "hours");

	//012345678901234567890123
	//and ?? minutes.    ---*-
	if (all)
		lcdDispStr(0, 1, "and");
	if (all || gSysState.update_pf_minute)
	{
		gSysState.update_pf_minute = 0;
		lcdDispTwoDgtls(4, 1, gPumpInfo.frequency.minute);
	}
	if (all)
		lcdDispStr(7, 1, "minutes.");
	if (all)
		lcdDispStr(19, 1, "---*-");
}

//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void lcdDispPumpNext(u08 all)
{
	// first line
	//012345678901234567890123
	//Next pump is at ??:??:??
	if (all)
		lcdDispStr(0, 0, "Next pump is at");
	if (all || gSysState.update_pn_hour)
	{
		gSysState.update_pn_hour = 0;
		lcdDispTwoDgtls(16, 0, gPumpInfo.next.hour);
	}
	if (all)
		lcdDispChar(18, 0, 0x3A);					//:
	if (all || gSysState.update_pn_minute)
	{
		gSysState.update_pn_minute = 0;
		lcdDispTwoDgtls(19, 0, gPumpInfo.next.minute);
	}
	if (all)
		lcdDispChar(21, 0, 0x3A);					//:
	if (all || gSysState.update_pn_second)
	{
		gSysState.update_pn_second = 0;
		lcdDispTwoDgtls(22, 0, gPumpInfo.next.second);
	}

	//012345678901234567890123
	//on ??/??/????.     ----*
	if (all)
		lcdDispStr(0, 1, "on");
	if (all || gSysState.update_pn_month)
	{
		gSysState.update_pn_month = 0;
		lcdDispTwoDgtls(3, 1, gPumpInfo.next.month);
	}
	if (all)
		lcdDispChar(5, 1, 0x2F);					///
	if (all || gSysState.update_pn_day)
	{
		gSysState.update_pn_day = 0;
		lcdDispTwoDgtls(6, 1, gPumpInfo.next.day);
	}
	if (all)
		lcdDispChar(8, 1, 0x2F);					///
	if (all || gSysState.update_pn_year)
	{
		gSysState.update_pn_year = 0;
		lcdDispFourDgtls(9, 1, gPumpInfo.next.year);
	}
	if (all)
		lcdDispChar(13, 1, 0x2E);
	if (all)
		lcdDispStr(19, 1, "----*");
}