/*
 * Copyright (c) 2007 by Hartmut Birr
 *
 * 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 2
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. 
 *
 */


#include <inttypes.h>
#include <avr/pgmspace.h>
#include <util/delay.h>

#include <string.h>


#include "i2creg.h"
#include "lcd.h"

#define NDEBUG
#include "debug.h"

// P0.x -> D7..0
// P1.0 -> E
// P1.1 -> R/W
// P1.2 -> RS

#if defined (DISPLAY208)
	static char DisplayData[16];
	const prog_char ucWhites[] = "        "; 				/* 8 spaces */
#elif defined (DISPLAY420)
    static char DisplayData[80];
	const prog_char ucWhites[] = "                    "; 	/* 20 spaces */
#endif

void LCDWriteCmd(uint8_t value)
{
    uint8_t data[2];
    
    data[0] = value;

    data[1] = 1;                        // RS=0, R/W=0, E=1
    i2c_write_regs(LCD_PCA9555D_ADRESS, 2, 2, data);

    data[1] = 0;                        // RS=0, R/W=0, E=0
    i2c_write_regs(LCD_PCA9555D_ADRESS, 3, 1, data + 1);
}

void LCDWriteData(uint8_t value)
{
    uint8_t data[2];

    data[0] = value;

    data[1] = 0x05;                     // RS=1, R/W=0, E=1
    i2c_write_regs(LCD_PCA9555D_ADRESS, 2, 2, data);

    data[1] = 0x04;                     // RS=1, R/W=0, E=0
    i2c_write_regs(LCD_PCA9555D_ADRESS, 3, 1, data + 1);
}

static const prog_uint8_t cursor[] = {
    0x00, 0x0e, 0x1f, 0x11, 0x1f, 0x0e, 0x00, 0x00,	  // 4
    0x00, 0x0e, 0x17, 0x1b, 0x1d, 0x0e, 0x00, 0x00,   // 3
    0x00, 0x0e, 0x1b, 0x1b, 0x1b, 0x0e, 0x00, 0x00,   // 2
    0x00, 0x0e, 0x1d, 0x1b, 0x17, 0x0e, 0x00, 0x00,   // 1
    0x00, 0x08, 0x15, 0x02, 0x08, 0x15, 0x02, 0x00,   // TRMSC symbol
    0x00, 0x04, 0x06, 0x07, 0x06, 0x04, 0x00, 0x00,	  // enter symbol
};

uint8_t LCDInit(void)
{
    uint8_t data[2];
    uint8_t i;


    /* onfig register see schematics */
    data[0] = 0x00;                     /* P0 is output */
    data[1] = 0xf8;                     /* P1.0-2 is output, P1.3-7 is input */
    if (i2c_write_regs(LCD_PCA9555D_ADRESS, 6, 2, data))
    {
        DPRINT(PSTR("%s: Couldn't detect a device at address LCD_PCA9555D_ADRESS \n"), __FUNCTION__);
        return 0;
    }

    /* polarity inversion register */
    data[0] = 0x00;
/* CHANGED DKS */
    data[1] = 0xf8;                     /* invert the buttons; now 4 buttons */
/* CHANGED DKS */
    i2c_write_regs(LCD_PCA9555D_ADRESS, 4, 2, data);


    _delay_ms(30);
#if defined (DISPLAY208)
    LCDWriteCmd(0x38);                  // System set
    LCDWriteCmd(0x0c);                  // display on
    LCDWriteCmd(0x01);                  // clear Display
    _delay_ms(2);
    LCDWriteCmd(0x06);                  // Entry Mode set
#elif defined (DISPLAY420)
    LCDWriteCmd(0x34);                  // System set
	LCDWriteCmd(0x09);                  // 
    LCDWriteCmd(0x30);                  
	LCDWriteCmd(0x0c);                  // display on
    LCDWriteCmd(0x01);                  // clear display
    _delay_ms(2);
    LCDWriteCmd(0x06);                  // Entry Mode set cursor auto-increment
#endif	


	LCDWriteCmd(LCD_PCA9555D_ADRESS);
    for (i = 0; i < sizeof(cursor); i++)
    {
        LCDWriteData(pgm_read_byte(&cursor[i]));
    }
	
    memset(DisplayData, ' ', sizeof(DisplayData));

    return 1;
}

static void LCDInternalWrite(uint8_t x, uint8_t y, uint8_t len, const char* data, uint8_t prog)
{
    uint8_t addr;
    uint8_t valid;
    char ch;
    char* ptr;


    if (x >= SPALTEN_MAX || y >= ZEILEN_MAX)
    {
        return;
    }
    
    addr = x;

#if defined (DISPLAY208)
    if (y == 1)
    {
        addr += 0x40;
	}
#elif defined (DISPLAY420) /* see data-sheet about adress */
	// TODO check case vs. if else if in asm
    if (y == 1)	
    { 
	   addr += 0x20;
	}
	else if (y == 2) 
	{
		addr += 0x40;
	}
	else if (y == 3) 
	{
		addr += 0x60;
	}
#endif


    if (x + len > SPALTEN_MAX)
    {
        len = SPALTEN_MAX - x;
    }

    ptr = DisplayData + SPALTEN_MAX * y + x;
    valid = 0;

    while(len--)
    {
        ch = prog ? pgm_read_byte((const prog_char*)data) : *data;
        if (*ptr == ch)
        {
            valid = 0;
        }
        else
        {
            if (!valid)
            {
                LCDWriteCmd(0x80 | addr);
                valid = 1;
            }
            LCDWriteData(ch);
            *ptr = ch;
        }
        data++;
        addr++;
        ptr++;
    }
}
    

void LCDWrite(uint8_t x, uint8_t y, uint8_t len, const char* data)
{
    LCDInternalWrite(x, y, len, data, 0);
}

void LCDWrite_P(uint8_t x, uint8_t y, uint8_t len, const prog_char* data)
{
    LCDInternalWrite(x, y, len, (const char*)data, 1);
}

void LCDOverwrite_P(uint8_t x, uint8_t y, uint8_t len, const prog_char* data)
{
    //  clearing leading and trailing whitespaces instead of calling LCDClearLine(y) to save traffic on I2C-Bus
	LCDWrite_P(0, y, x, ucWhites);
	LCDWrite_P(x+len, y, SPALTEN_MAX, ucWhites);

	// now writing real content
    LCDWrite_P(x, y, len, data);
}


void LCDClearLine(uint8_t y)
{
	LCDWrite_P(0, y, SPALTEN_MAX, ucWhites);
}


uint8_t LCDGetButton(void)
{
    uint8_t data = 0x00;
    uint8_t result = 0;

    i2c_read_regs(LCD_PCA9555D_ADRESS, 1, 1, &data);

/* ADDED DKS */
	if (data & 0x80)
	{
		result |= BUTTON_SOFT_KEY_2;
	}
    if (data & 0x40)
	{
		result |= BUTTON_SOFT_KEY_1;
	}
/* ADDED DKS */

    if (data & 0x20)
    {
        result |= BUTTON_DOWN;
    }
    if (data & 0x10)
    {
        result |= BUTTON_UP;
    }
    if (data & 0x08)
    {
        result |= BUTTON_ENTER;
    }
    

    return result;
}
