/******************************************************************************
 *
 * Copyright:
 *    (C) 2006 Embedded Artists AB
 *
 * File:
 *    lcd.c
 *
 * Description:
 *    Implements routines for the LCD.
 *
 *****************************************************************************/

/******************************************************************************
 * Includes
 *****************************************************************************/
#include "lcd.h"
#include "ascii.h"
#include "lcd_hw.h"
#include "inttypes.h"
#include "functions.h"

/******************************************************************************
 * Typedefs and defines
 *****************************************************************************/
#define LCD_CMD_SWRESET   0x01
#define LCD_CMD_BSTRON    0x03
#define LCD_CMD_SLEEPIN   0x10
#define LCD_CMD_SLEEPOUT  0x11
#define LCD_CMD_INVON     0x21
#define LCD_CMD_SETCON    0x25
#define LCD_CMD_DISPON    0x29
#define LCD_CMD_CASET     0x2A
#define LCD_CMD_PASET     0x2B
#define LCD_CMD_RAMWR     0x2C
#define LCD_CMD_RGBSET    0x2D
#define LCD_CMD_MADCTL    0x36
#define LCD_CMD_COLMOD    0x3A

#define MADCTL_HORIZ      0x48
#define MADCTL_VERT       0x68


/*****************************************************************************
 * Local variables
 ****************************************************************************/
static uint8 lcd_x;
static uint8 lcd_y;
static uint8 bkgColor;
static uint8 textColor;
static uint8 setcolmark;

/*****************************************************************************
 * Local prototypes
 ****************************************************************************/
static void lcdWindow1(uint8 xp, uint8 yp, uint8 xe, uint8 ye);


/*****************************************************************************
 *
 * Description:
 *    Initialize the LCD controller and SPI interface
 *    (0,0) is top left and (129,129) is bottom right
 *    8 bits Color mode uses RRRGGGBB layout
 *
 ****************************************************************************/
void lcdInit(void) {
  bkgColor  = 0;
  textColor = 0;
  
  //init SPI interface
  initSpiForLcd();
  
  //select controller
  selectLCD(TRUE);

	lcdWrcmd(LCD_CMD_SWRESET);

	delayT(1);
	lcdWrcmd(LCD_CMD_SLEEPOUT);
	lcdWrcmd(LCD_CMD_DISPON);
	lcdWrcmd(LCD_CMD_BSTRON);
	delayT(1);
		
	lcdWrcmd(LCD_CMD_MADCTL);   //Memory data acces control
	lcdWrdata(MADCTL_HORIZ);    //X Mirror and BGR format
	lcdWrcmd(LCD_CMD_COLMOD);   //Colour mode
	lcdWrdata(0x02);            //256 colour mode select
	lcdWrcmd(LCD_CMD_INVON);    //Non Invert mode

	lcdWrcmd(LCD_CMD_RGBSET);   //LUT write
  lcdWrdata(0);               //Red
  lcdWrdata(2);
  lcdWrdata(4);
  lcdWrdata(6);
  lcdWrdata(9);
  lcdWrdata(11);
  lcdWrdata(13);
  lcdWrdata(15);
  lcdWrdata(0);               //Green
  lcdWrdata(2);
  lcdWrdata(4);
  lcdWrdata(6);
  lcdWrdata(9);
  lcdWrdata(11);
  lcdWrdata(13);
  lcdWrdata(15);
  lcdWrdata(0);               //Blue
  lcdWrdata(6);
	lcdWrdata(10);
	lcdWrdata(15);

  //deselect controller
  selectLCD(FALSE);

	lcdContrast(56);

	lcdClrscr();
}


/*****************************************************************************
 *
 * Description:
 *    Clear screen (with current background color)
 *
 ****************************************************************************/
void lcdClrscr(void) {
  uint32 i;

	lcd_x = 0;
  lcd_y = 0;

  //select controller
  selectLCD(TRUE);   

  lcdWindow1(255,255,128,128);
  
  lcdWrcmd(LCD_CMD_RAMWR);    //write memory
  
  for(i=0; i<16900; i++)
    lcdWrdata(bkgColor);

  //deselect controller
  selectLCD(FALSE);
}


/*****************************************************************************
 *
 * Description:
 *    Shut down the LCD controller
 *
 ****************************************************************************/
void lcdOff(void) {
  lcdClrscr();

  //select controller
  selectLCD(TRUE);   

  lcdWrcmd(LCD_CMD_SLEEPIN);

  //deselect controller
  selectLCD(FALSE);
}


/*****************************************************************************
 *
 * Description:
 *    Set current foreground and background color
 *
 ****************************************************************************/
void lcdColor(uint8 bkg, uint8 text) {
  bkgColor  = bkg;
  textColor = text;
}


/*****************************************************************************
 *
 * Description:
 *    Set display contrast
 *
 ****************************************************************************/
void lcdContrast(uint8 cont) //vary between 0 - 127
{
  //select controller
  selectLCD(TRUE);

  //set contrast cmd.
  lcdWrcmd(LCD_CMD_SETCON);
	lcdWrdata(cont);

  //deselect controller
  selectLCD(FALSE);
}


/*****************************************************************************
 *
 * Description:
 *    Draw a rectangular area with specified color.
 *
 ****************************************************************************/
void lcdRect(uint8 x, uint8 y, uint8 xLen, uint8 yLen, uint8 color) {
  uint32 i;
  uint32 len;

  //select controller
  selectLCD(TRUE);   

  lcdWindow1(x,y,x+xLen-1,y+yLen-1);
  
  lcdWrcmd(LCD_CMD_RAMWR);    //write memory
  
  len = xLen*yLen;
  for(i=0; i<len; i++)
    lcdWrdata(color);

  //deselect controller
  selectLCD(FALSE);
}


/*****************************************************************************
 *
 * Description:
 *    Draw rectangular area with different boardser colors. Currently used
 *    by example game.
 *
 ****************************************************************************/
void lcdRectBrd(uint8 x, uint8 y, uint8 xLen, uint8 yLen, uint8 color1, uint8 color2, uint8 color3) {
  uint32 i,j;

  //select controller
  selectLCD(TRUE);   

  lcdWindow1(x,y,x+xLen-1,y+yLen-1);
  
  lcdWrcmd(LCD_CMD_RAMWR);    //write memory
  
  for(i=0; i<xLen; i++)
    lcdWrdata(color2);
  for(j=1; j<(yLen-2); j++)
  {
    lcdWrdata(color2);
    for(i=0; i<(xLen-2); i++)
      lcdWrdata(color1);
    lcdWrdata(color3);
  }
  for(i=0; i<xLen; i++)
    lcdWrdata(color3);

  //deselect controller
  selectLCD(FALSE);
}


/*****************************************************************************
 *
 * Description:
 *    Draw rectangular area from bitmap. Specify xy-position and xy-length.
 *    Compressed format is supported.
 *
 *    In uncompressed color mode, pData points to an area of xLen * yLen
 *    bytes with the icon.
 *    In compressed mode the escapeChar is used to denote that the next
 *    two bytes contain a length and a color (run length encoding)
 *    Note that is is still possible to specify the color value that
 *    equals the escape value in a compressed string.
 *
 ****************************************************************************/
void lcdIcon(uint8 x, uint8 y, uint8 xLen, uint8 yLen, uint8 compressionOn, uint8 escapeChar, const uint8* pData) {
  uint32 i,j;
  int32 len;

  //select controller
  selectLCD(TRUE);   

  lcdWindow1(x,y,x+xLen-1,y+yLen-1);
  
  lcdWrcmd(LCD_CMD_RAMWR);    //write memory
  
  len = xLen*yLen;
  if (compressionOn == FALSE)
  {
    for(i=0; i<len; i++)
      lcdWrdata(*pData++);
  }
  else
    while(len > 0)
    {
      if(*pData == escapeChar)
      {
        pData++;
        j = *pData++;
        for(i=0;i<j;i++)
          lcdWrdata(*pData);
        pData++;
        len -= j;
      }
      else
      {
        lcdWrdata(*pData++);
        len--;
      }
    }

  //deselect controller
  selectLCD(FALSE);
}


/*****************************************************************************
 *
 * Description:
 *    Update xy-position (by also creating a window).
 *
 ****************************************************************************/
void lcdGotoxy(uint8 x, uint8 y) {
  lcd_x = x;
  lcd_y = y;
  lcdWindow(x, y, 129, 129);
}


/*****************************************************************************
 *
 * Description:
 *    Initialize LCD controller for a window (to write in).
 *    Set start xy-position and xy-length.
 *    Selects/deselects LCD controller.
 *
 ****************************************************************************/
void lcdWindow(uint8 xp, uint8 yp, uint8 xe, uint8 ye) {
  //select controller
  selectLCD(TRUE);

	lcdWindow1(xp, yp, xe, ye);

  //deselect controller
  selectLCD(FALSE);
}


/*****************************************************************************
 *
 * Description:
 *    Initialize LCD controller for a window (to write in).
 *    Set start xy-position and xy-length
 *    No select/deselect of LCD controller.
 *
 ****************************************************************************/
static void lcdWindow1(uint8 xp, uint8 yp, uint8 xe, uint8 ye) {
  lcdWrcmd(LCD_CMD_CASET);    //set X
  lcdWrdata(xp+2);
	lcdWrdata(xe+2);

	lcdWrcmd(LCD_CMD_PASET);    //set Y
	lcdWrdata(yp+2);
	lcdWrdata(ye+2);
}


/*****************************************************************************
 *
 * Description:
 *    Update xy-position for newline situation.
 *
 ****************************************************************************/
static void lcdNewline(void) {
  lcd_x  = 0;
  lcd_y	+= 14;
  if (lcd_y >= 126)
    lcd_y = 126;
}


/*****************************************************************************
 *
 * Description:
 *    Draw one character withc current foreground and background color
 *    at current xy position on display. Update x-position (+8).
 *
 ****************************************************************************/
void lcdData(uint8 data) {
  //select controller
  selectLCD(TRUE);
  
  if (data <= 127)
  {
    uint32 mapOffset;
    uint8 i,j,byteToShift;

    data -= 30;
    mapOffset = 14*data;

    lcdWrcmd(LCD_CMD_CASET);
    lcdWrdata(lcd_x+2);
    lcdWrdata(lcd_x+9);
    lcdWrcmd(LCD_CMD_PASET);
    lcdWrdata(lcd_y+2);
    lcdWrdata(lcd_y+15);
    lcdWrcmd(LCD_CMD_RAMWR);
    
    for(i=0; i<14; i++)
    {
      byteToShift = charMap[mapOffset++];
      for(j=0; j<8; j++)
      {
        if (byteToShift & 0x80)
          lcdWrdata(textColor);
        else
          lcdWrdata(bkgColor);
        byteToShift <<= 1;
      }
    }
  }

  //deselect controller
  selectLCD(FALSE);

  lcd_x += 8;
}


/*****************************************************************************
 *
 * Description:
 *    Write/draw one character at current xy-position.
 *    The xy-position is updated afterwards
 *
 ****************************************************************************/
void lcdPutchar(uint8 data) {
  if (data == '\n')
    lcdNewline();
  else if (data != '\r')
  {
    if (setcolmark == TRUE)
    {
      textColor = data;
      setcolmark = FALSE;
    }
    else if (data == 0xff)
      setcolmark = TRUE;
    else if (lcd_x <= 124)
    {
      lcdData(data);
    }
  }
}

/*****************************************************************************
 *
 * Description:
 *    Write/draw (null-terminated) string of character at current xy-position
 *
 ****************************************************************************/
void lcdPuts(char *s) {
  while(*s != '\0')
    lcdPutchar(*s++);
}

char  hexChars[] = "0123456789abcdef";
char  buf[33];

void lcdPutsNumber(uint32 number) {

	char *p;

	p = buf;

	do {
		*p++ = hexChars[number % 10];
	} while (number /= 10);

	//*p++ = '\0';

	do {
		//outputFnk(*--p);
		lcdPutchar(*--p);
	} while (p > buf);


}




/*****************************************************************************
 *
 * Description:
 *    Send command data to LCD controller
 *
 ****************************************************************************/
void lcdWrcmd(uint8 data) {
  sendToLCD(0, data);
}


/*****************************************************************************
 *
 * Description:
 *    Send data byte to LCD controller
 *
 ****************************************************************************/
void lcdWrdata(uint8 data) {
  sendToLCD(1, data);
}


