// Based on the work by DFRobot

#include "LCDisplayI2C.h"
#include <inttypes.h>
#if defined(ARDUINO) && ARDUINO >= 100

#include "Arduino.h"

#define printIIC(args)	Wire.write(args)
inline size_t CLCDisplayI2C::write(uint8_t value) {
	send(value, Rs);
	return 1;
}

#else
#include "WProgram.h"

#define printIIC(args)	Wire.send(args)
inline void CLCDisplayI2C::write(uint8_t value) {
	send(value, Rs);
}

#endif
#include "Wire.h"

CLCDisplayI2C::CLCDisplayI2C(uint8_t lcd_Addr, uint8_t rows, uint8_t cols)
{
  _Addr = lcd_Addr;
  _cols = cols;
  _rows = rows;
  _backlightval = LCD_NOBACKLIGHT;
}

void CLCDisplayI2C::InitOLED(void)
{
  	_oled = true;
	init_priv();
}

void CLCDisplayI2C::Init(void)
{
	init_priv();
}

void CLCDisplayI2C::init_priv()
{
	Wire.begin();
	_displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS;
	Open(_cols, _rows);  
}

void CLCDisplayI2C::Open(uint8_t lines, uint8_t cols, uint8_t dotsize) 
{
	if (lines > 1) 
	{
		_displayfunction |= LCD_2LINE;
	}
	_numlines = lines;
	if ((dotsize != 0) && (lines == 1)) 
	{
		_displayfunction |= LCD_5x10DOTS;
	}
	delay(50); 
  	expanderWrite(_backlightval);	
	delay(1000);
   	write4bits(0x03 << 4);
   	delayMicroseconds(4500); 
	write4bits(0x03 << 4);
   	delayMicroseconds(4500); 
    write4bits(0x03 << 4); 
   	delayMicroseconds(150);
	write4bits(0x02 << 4); 
	Command(LCD_FUNCTIONSET | _displayfunction);  
	_displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
	DisplayOn();
	Clear();
	_displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
	Command(LCD_ENTRYMODESET | _displaymode);
	Home();
}

void CLCDisplayI2C::Clear()
{
	Command(LCD_CLEARDISPLAY);
	delayMicroseconds(2000);  
  	if (_oled) SetCursor(0, 0);
}

void CLCDisplayI2C::Home()
{
	Command(LCD_RETURNHOME);  // set cursor position to zero
	delayMicroseconds(2000);  // this command takes a long time!
}

void CLCDisplayI2C::SetCursor(uint8_t row, uint8_t col)
{
	int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 };
	if ( row > _numlines ) 
	{
		row = _numlines-1;    // we count rows starting w/0
	}
	Command(LCD_SETDDRAMADDR | (col + row_offsets[row]));
}

void CLCDisplayI2C::DisplayOff() 
{
	_displaycontrol &= ~LCD_DISPLAYON;
	Command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void CLCDisplayI2C::DisplayOn() 
{
	_displaycontrol |= LCD_DISPLAYON;
	Command(LCD_DISPLAYCONTROL | _displaycontrol);
}

void CLCDisplayI2C::CursorOff() 
{
	_displaycontrol &= ~LCD_CURSORON;
	Command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void CLCDisplayI2C::CursorOn() 
{
	_displaycontrol |= LCD_CURSORON;
	Command(LCD_DISPLAYCONTROL | _displaycontrol);
}

void CLCDisplayI2C::BlinkOff() 
{
	_displaycontrol &= ~LCD_BLINKON;
	Command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void CLCDisplayI2C::BlinkOn() 
{
	_displaycontrol |= LCD_BLINKON;
	Command(LCD_DISPLAYCONTROL | _displaycontrol);
}

void CLCDisplayI2C::ScrollDisplayLeft(void) 
{
	Command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
}
void CLCDisplayI2C::ScrollDisplayRight(void) 
{
	Command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
}

void CLCDisplayI2C::LeftToRight(void) 
{
	_displaymode |= LCD_ENTRYLEFT;
	Command(LCD_ENTRYMODESET | _displaymode);
}

void CLCDisplayI2C::RightToLeft(void) 
{
	_displaymode &= ~LCD_ENTRYLEFT;
	Command(LCD_ENTRYMODESET | _displaymode);
}

void CLCDisplayI2C::AutoScrollOn(void) 
{
	_displaymode |= LCD_ENTRYSHIFTINCREMENT;
	Command(LCD_ENTRYMODESET | _displaymode);
}

void CLCDisplayI2C::AutoScrollOff(void) 
{
	_displaymode &= ~LCD_ENTRYSHIFTINCREMENT;
	Command(LCD_ENTRYMODESET | _displaymode);
}

void CLCDisplayI2C::createChar(uint8_t location, uint8_t charmap[]) 
{
	location &= 0x7; 
	Command(LCD_SETCGRAMADDR | (location << 3));
	for (int i=0; i<8; i++) 
	{
		write(charmap[i]);
	}
}
void CLCDisplayI2C::createChar(uint8_t location, const char *charmap) 
{
	location &= 0x7; // we only have 8 locations 0-7
	Command(LCD_SETCGRAMADDR | (location << 3));
	for (int i=0; i<8; i++) 
	{
		write(pgm_read_byte_near(charmap++));
	}
}

void CLCDisplayI2C::BacklightOff(void) 
{
	_backlightval=LCD_NOBACKLIGHT;
	expanderWrite(0);
}

void CLCDisplayI2C::BacklightOn(void) 
{
	_backlightval=LCD_BACKLIGHT;
	expanderWrite(0);
}

inline void CLCDisplayI2C::Command(uint8_t value) 
{
	send(value, 0);
}


void CLCDisplayI2C::send(uint8_t value, uint8_t mode) 
{
	uint8_t highnib=value&0xf0;
	uint8_t lownib=(value<<4)&0xf0;
       write4bits((highnib)|mode);
	write4bits((lownib)|mode); 
}

void CLCDisplayI2C::write4bits(uint8_t value) 
{
	expanderWrite(value);
	pulseEnable(value);
}

void CLCDisplayI2C::expanderWrite(uint8_t _data){                                        
	Wire.beginTransmission(_Addr);
	printIIC((int)(_data) | _backlightval);
	Wire.endTransmission();   
}

void CLCDisplayI2C::pulseEnable(uint8_t _data)
{
	expanderWrite(_data | En);	// En high
	delayMicroseconds(1);		// enable pulse must be >450ns	
	expanderWrite(_data & ~En);	// En low
	delayMicroseconds(50);		// commands need > 37us to settle
} 
//


	
