/*
 * This file is subject to the terms of the GFX License. If a copy of
 * the license was not distributed with this file, you can obtain one at:
 *
 *              http://ugfx.org/license.html
 */

/**
 * @file    drivers/tdisp/HD44780/tdisp_lld.c
 * @brief   TDISP driver subsystem low level driver source for the HD44780 display
 *
 * @addtogroup TDISP
 * @{
 */

#include "gfx.h"

#if GFX_USE_TDISP /*|| defined(__DOXYGEN__)*/

#include "tdisp_lld_board.h"

/* Controller Specific Properties */
#define CUSTOM_CHAR_COUNT		8
#define CUSTOM_CHAR_XBITS		5
#define CUSTOM_CHAR_YBITS		8

/* Define the properties of our controller */
tdispStruct	TDISP = {
	TDISP_COLUMNS, TDISP_ROWS,		/* cols, rows */
	CUSTOM_CHAR_XBITS, CUSTOM_CHAR_YBITS,	/* charBitsX, charBitsY */
	CUSTOM_CHAR_COUNT			/* maxCustomChars */
};

/* display control settings */
#define TDISP_DISPLAY_ON		0x04
#define TDISP_CURSOR_ON			0x02
#define TDISP_CURSOR_BLINK		0x01

/* cursor movement settings */
#define TDISP_SHIFT_ON			0x01
#define	TDISP_CURSOR_INC		0x02

/* display scroll settings */
#define TDISP_SCROLL_DISPLAY		0x08
#define TDISP_SCROLL_RIGHT		0x04

static uint8_t	displaycontrol;
static uint8_t	cursorcontrol;

bool_t tdisp_lld_init(void) { 
	/* initialise hardware */
	init_board();
	
	/* The first part is the initialing code.
	 * In this part only the lower nibble of the
	 * byte is written directly to the display, thus
	 * without write_cmd, which sends both high and
	 * low nibble.
	 */
	
	/* Give the LCD a little time to wake up */
	gfxSleepMilliseconds(15);
	
	/* clear the RS-line to the display */
	palClearPad(PORT_CTRL, PIN_RS);
// 	#if BUS_4BITS
		/* write three times 0x03 to display (4-bit mode only)
		* with RS = low.
		*/
		writeToLCD(0x03); // 1x
		gfxSleepMilliseconds(20);
		writeToLCD(0x03); // 2x
// 		gfxSleepMilliseconds(20);
		writeToLCD(0x03); // 3x
// 		gfxSleepMilliseconds(20);
		/* Put display in 4-bit mode by
		* write 0x02 to display.
		*/
		writeToLCD(0x02); // 4bit-modus
// 		gfxSleepMilliseconds(20);
// 	#else
		/* write three times 0x30 to display (8-bit mode only)
		* with RS = low.
		*/
// 		writeToLCD(0x30); // 1x
// 		writeToLCD(0x30); // 2x
// 		writeToLCD(0x30); // 3x
// 	#endif	

	/* From this point on, the LCD accepts
	 * bytes sent with highnibbel first and than
	 *the lownibble if working in 4-bit mode.
	 * In 8-bit mode bytes are written in 1 action.
	 */
	
	/* 4-bit modus, 2 lines en 5x7 characters */
	write_cmd(0x28);
// 	gfxSleepMilliseconds(20);
	
	/* set display on, cursor off and no blinking */
	write_cmd(0x0C);
// 	gfxSleepMilliseconds(20);
	/* set cursor increase and direction */
	write_cmd(0x06);
// 	gfxSleepMilliseconds(20);
	
	displaycontrol = TDISP_DISPLAY_ON;
	cursorcontrol = TDISP_CURSOR_INC;
	/* END OF INITIALISATION */

	return TRUE;
}

/* Clears the display. The display needs
 * a long time to process this command. So
 * an extra delay is insterted.
 */
void tdisp_lld_clear(void) {
	write_cmd(0x01);
}

/* Writes a character to the display */
void tdisp_lld_draw_char(char c) {
	write_data(c);
}

/* Set cursor position */
void tdisp_lld_set_cursor(coord_t col, coord_t row) {
	static const uint8_t row_offsets[] = { 0x00, 0x40, 0x14, 0x54 };

	/*
	 *  Short-cut:
	 *
	 *  If x and y = 0 then use the home command.
	 *
	 *  Note: There is probably no advantage as both commands are a single byte
	 */
//	if (col == 0 && row == 0) {
//		write_cmd(0x02);
//		return;
//	}

	write_cmd(0x80 | (col + row_offsets[row]));
// 	write_cmd(0x80 | (0x40 + col));
}

/* Create a custom character to the display */
void tdisp_lld_create_char(uint8_t address, uint8_t *charmap) {
	int i;

	write_cmd(0x40 | (address << 3));
	for(i = 0; i < CUSTOM_CHAR_YBITS; i++) {
		write_data(charmap[i]);
	}

}

/* Write display control commands to the display */
void tdisp_lld_control(uint16_t what, uint16_t value) {
	switch(what) {
		      case TDISP_CTRL_DISPLAY:
				switch (value) {
					case displayOff:
						displaycontrol &= ~TDISP_DISPLAY_ON;
						break;
					case displayOn:
						displaycontrol |= TDISP_DISPLAY_ON;
						break;
				}
				write_cmd(0x08 | displaycontrol);
				break;
		      case TDISP_CTRL_CURSOR:
				switch (value) {
					case cursorBlinkingBlock:
					case cursorBlinkingUnderline:
					case cursorBlinkingBar:
						displaycontrol |= TDISP_CURSOR_ON + TDISP_CURSOR_BLINK;
						break;
					case cursorBlock:
					case cursorUnderline:
					case cursorBar:
						displaycontrol = (displaycontrol | TDISP_CURSOR_ON) & ~TDISP_CURSOR_BLINK;
						break;
					case cursorOff:
					default:
						displaycontrol &= ~(TDISP_CURSOR_ON | TDISP_CURSOR_BLINK); // zet alleen de cursor uit. Bewaar de overige instellingen
						break;
				}
				write_cmd(0x08 | displaycontrol);
				break;
			case TDISP_CTRL_MOVE:
				switch (value) {
					case cursorIncrease:
						cursorcontrol |= TDISP_CURSOR_INC; // increase cursor position
						break;
					case cursorDecrease:
						cursorcontrol &= ~TDISP_CURSOR_INC; // decrease cursor position
						break;
				}
				write_cmd(0x04 | cursorcontrol);
				break;
			case TDISP_CTRL_SHIFT:
				switch (value) {
					case shiftOn:
						cursorcontrol |= TDISP_SHIFT_ON;
						break;
					case shiftOff:
						cursorcontrol &= ~TDISP_SHIFT_ON;
						break;
				}
				write_cmd(0x04 | cursorcontrol);
				break;
	}
}

/* Scrolls the display in a particular direction with an amount of characters and delays the scroll between each scroll step */
void tdisp_lld_scroll(uint16_t direction, uint16_t amount, uint16_t delay) {
  uint16_t scrollcontrol = 0;
  uint16_t i;
  
  switch (direction) {
    case displayScrollLeft:
      scrollcontrol = TDISP_SCROLL_DISPLAY;
      break;
    case displayScrollRight:
      scrollcontrol = TDISP_SCROLL_DISPLAY | TDISP_SCROLL_RIGHT;
      break;
  }
  
  for(i = 0; i < amount; i++) {
    write_cmd(0x10 | scrollcontrol);
    gfxSleepMilliseconds(delay);	
  }
}

#if TDISP_USE_BACKLIGHT
/* sets the brightness of the display backlight */
void tdisp_lld_set_backlight(uint16_t percentage) {
  set_backlight(percentage);
}
#endif

#endif /* GFX_USE_TDISP */
/** @} */

