#include "TZ51_LiquidCrystal.h"

#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "Arduino.h"

// When the display powers up, it is configured as follows:
//
// 1. Display clear
// 2. Function set: 
//    DL = 1; 8-bit interface data 
//    N = 0; 1-line display 
//    F = 0; 5x8 dot character font 
// 3. Display on/off control: 
//    D = 0; Display off 
//    C = 0; Cursor off 
//    B = 0; Blinking off 
// 4. Entry mode set: 
//    I/D = 1; Increment by 1 
//    S = 0; No shift 
//
// Note, however, that resetting the Arduino doesn't reset the LCD, so we
// can't assume that its in that state when a sketch starts (and the
// LiquidCrystal constructor is called).

//Définitions of accentuated characters stored in Program Flash Memory
const byte PROGMEM agrave[8] = {  B01000,  B00100,  B01110,  B00001,  B01111,  B10001,  B01111,  B00000};//{136,134,128,142,145,147,141,128};
const byte PROGMEM acirc[8] = {132,138,128,142,145,147,141,128};
const byte PROGMEM atilda[8] = {  B00101,  B01010,  B00000,  B01110,  B10001,  B10011,  B01101,  B00000};
const byte PROGMEM atrema[8] ={  B01010,  B00000,  B01110,  B00001,  B01111,  B10001,  B01111,  B00000};
const byte PROGMEM eaigu[8] = {130,132,142,145,159,144,142,128};
const byte PROGMEM egrave[8] = {136,132,142,145,159,144,142,128};
const byte PROGMEM ecirc[8] = {132,138,142,145,159,144,142,128};
const byte PROGMEM imajcirc[8] = {  B00100,  B01010,  B00000,  B01110,  B00100,  B00100,  B01110,  B00000};//{132,138,128,140,132,132,142,128};
const byte PROGMEM imajtrema[8] = {  B01010,  B00000,  B01110,  B00100,  B00100,  B00100,  B01110,  B00000};
const byte PROGMEM itrema[8] = {  B01010,  B00000,  B01100,  B00100,  B00100,  B00100,  B01110,  B00000};
const byte PROGMEM icirc[8] ={  B00100,  B01010,  B00000,  B01100,  B00100,  B00100,  B01110,  B00000};
const byte PROGMEM ocirc[8] = {132,138,128,142,145,145,142,128};
const byte PROGMEM ugrave[8] = {136,134,128,145,145,147,141,128};
const byte PROGMEM ucirc[8] = {132,138,128,145,145,147,141,128};
const byte PROGMEM utrema[8] ={  B00000,  B01010,  B00000,  B10001,  B10001,  B10011,  B01101,  B00000};
const byte PROGMEM ccedille[8] ={  B00000,  B01110,  B10001,  B10000,  B10000,  B01110,  B00100,  B01000};
const byte PROGMEM etrema[8] ={  B01010,  B00000,  B01110,  B10001,  B11111,  B10000,  B01110,  B00000};
const byte PROGMEM otrema[8] ={  B00000,  B01010,  B00000,  B01110,  B10001,  B10001,  B01110,  B00000};
const byte PROGMEM ntilda[8] ={  B00101,  B01010,  B00000,  B10111,  B11001,  B10001,  B10001,  B00000};
const byte PROGMEM euro[8] ={  B01110,  B10001,  B11100,  B10000,  B11100,  B10001,  B01110,  B00000};
const byte PROGMEM technozone[8] ={  B01110,  B11111,  B10101,  B11111,  B11111,  B01010,  B01110,  B00100};//{  B01110,  B11111,  B10101,  B11111,  B11111,  B11011,  B01110,  B00100};
const byte PROGMEM degre[8] ={  B00010,  B00101,  B00010,  B00000,  B00000,  B00000,  B00000,  B00000};
byte tempcar[8]={0,0,0,0,0,0,0,0};

LiquidCrystal::LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable,
			     uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
			     uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7)
{
  init(0, rs, rw, enable, d0, d1, d2, d3, d4, d5, d6, d7);
}

LiquidCrystal::LiquidCrystal(uint8_t rs, uint8_t enable,
			     uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
			     uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7)
{
  init(0, rs, 255, enable, d0, d1, d2, d3, d4, d5, d6, d7);
}

LiquidCrystal::LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable,
			     uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3)
{
  init(1, rs, rw, enable, d0, d1, d2, d3, 0, 0, 0, 0);
}

LiquidCrystal::LiquidCrystal(uint8_t rs,  uint8_t enable,
			     uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3)
{
  init(1, rs, 255, enable, d0, d1, d2, d3, 0, 0, 0, 0);
}

void LiquidCrystal::init(uint8_t fourbitmode, uint8_t rs, uint8_t rw, uint8_t enable,
			 uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
			 uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7)
{
  _rs_pin = rs;
  _rw_pin = rw;
  _enable_pin = enable;
  
  _data_pins[0] = d0;
  _data_pins[1] = d1;
  _data_pins[2] = d2;
  _data_pins[3] = d3; 
  _data_pins[4] = d4;
  _data_pins[5] = d5;
  _data_pins[6] = d6;
  _data_pins[7] = d7; 

  if (fourbitmode)
    _displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS;
  else 
    _displayfunction = LCD_8BITMODE | LCD_1LINE | LCD_5x8DOTS;
  
  begin(16, 1);  
}

void LiquidCrystal::set_pos(uint8_t x,uint8_t y){
 _posx=x;
 _posy=y;
}

void LiquidCrystal::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) {
  if (lines > 1) {
    _displayfunction |= LCD_2LINE;
  }
  _numlines = lines;

  setRowOffsets(0x00, 0x40, 0x00 + cols, 0x40 + cols);  

  // for some 1 line displays you can select a 10 pixel high font
  if ((dotsize != LCD_5x8DOTS) && (lines == 1)) {
    _displayfunction |= LCD_5x10DOTS;
  }

  pinMode(_rs_pin, OUTPUT);
  // we can save 1 pin by not using RW. Indicate by passing 255 instead of pin#
  if (_rw_pin != 255) { 
    pinMode(_rw_pin, OUTPUT);
  }
  pinMode(_enable_pin, OUTPUT);
  
  // Do these once, instead of every time a character is drawn for speed reasons.
  for (int i=0; i<((_displayfunction & LCD_8BITMODE) ? 8 : 4); ++i)
  {
    pinMode(_data_pins[i], OUTPUT);
   } 

  // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
  // according to datasheet, we need at least 40ms after power rises above 2.7V
  // before sending commands. Arduino can turn on way before 4.5V so we'll wait 50
  delayMicroseconds(50000); 
  // Now we pull both RS and R/W low to begin commands
  digitalWrite(_rs_pin, LOW);
  digitalWrite(_enable_pin, LOW);
  if (_rw_pin != 255) { 
    digitalWrite(_rw_pin, LOW);
  }
  
  //put the LCD into 4 bit or 8 bit mode
  if (! (_displayfunction & LCD_8BITMODE)) {
    // this is according to the hitachi HD44780 datasheet
    // figure 24, pg 46

    // we start in 8bit mode, try to set 4 bit mode
    write4bits(0x03);
    delayMicroseconds(4500); // wait min 4.1ms

    // second try
    write4bits(0x03);
    delayMicroseconds(4500); // wait min 4.1ms
    
    // third go!
    write4bits(0x03); 
    delayMicroseconds(150);

    // finally, set to 4-bit interface
    write4bits(0x02); 
  } else {
    // this is according to the hitachi HD44780 datasheet
    // page 45 figure 23

    // Send function set command sequence
    command(LCD_FUNCTIONSET | _displayfunction);
    delayMicroseconds(4500);  // wait more than 4.1ms

    // second try
    command(LCD_FUNCTIONSET | _displayfunction);
    delayMicroseconds(150);

    // third go
    command(LCD_FUNCTIONSET | _displayfunction);
  }

  // finally, set # lines, font size, etc.
  command(LCD_FUNCTIONSET | _displayfunction);  

  // turn the display on with no cursor or blinking default
  _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;  
  display();

  // clear it off
  clear();

  // Initialize to default text direction (for romance languages)
  _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
  // set the entry mode
  command(LCD_ENTRYMODESET | _displaymode);

}

void LiquidCrystal::setRowOffsets(int row0, int row1, int row2, int row3)
{
  _row_offsets[0] = row0;
  _row_offsets[1] = row1;
  _row_offsets[2] = row2;
  _row_offsets[3] = row3;
}

/********** high level commands, for the user! */
void LiquidCrystal::clear()
{
  command(LCD_CLEARDISPLAY);  // clear display, set cursor position to zero
  delayMicroseconds(2000);  // this command takes a long time!
  set_pos(0,0);
  for(int i=0;i<8;i++) {_custom[i]=0;}
}

void LiquidCrystal::home()
{
  command(LCD_RETURNHOME);  // set cursor position to zero
  delayMicroseconds(2000);  // this command takes a long time!
  set_pos(0,0);
}

void LiquidCrystal::setCursor(uint8_t col, uint8_t row)
{
  const size_t max_lines = sizeof(_row_offsets) / sizeof(*_row_offsets);
  if ( row >= max_lines ) {
    row = max_lines - 1;    // we count rows starting w/0
  }
  if ( row >= _numlines ) {
    row = _numlines - 1;    // we count rows starting w/0
  }
  
  command(LCD_SETDDRAMADDR | (col + _row_offsets[row]));
  set_pos(col,row);
}

// Turn the display on/off (quickly)
void LiquidCrystal::noDisplay() {
  _displaycontrol &= ~LCD_DISPLAYON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LiquidCrystal::display() {
  _displaycontrol |= LCD_DISPLAYON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}

// Turns the underline cursor on/off
void LiquidCrystal::noCursor() {
  _displaycontrol &= ~LCD_CURSORON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LiquidCrystal::cursor() {
  _displaycontrol |= LCD_CURSORON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}

// Turn on and off the blinking cursor
void LiquidCrystal::noBlink() {
  _displaycontrol &= ~LCD_BLINKON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LiquidCrystal::blink() {
  _displaycontrol |= LCD_BLINKON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}

// These commands scroll the display without changing the RAM
void LiquidCrystal::scrollDisplayLeft(void) {
  command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
}
void LiquidCrystal::scrollDisplayRight(void) {
  command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
}

// This is for text that flows Left to Right
void LiquidCrystal::leftToRight(void) {
  _displaymode |= LCD_ENTRYLEFT;
  command(LCD_ENTRYMODESET | _displaymode);
}

// This is for text that flows Right to Left
void LiquidCrystal::rightToLeft(void) {
  _displaymode &= ~LCD_ENTRYLEFT;
  command(LCD_ENTRYMODESET | _displaymode);
}

// This will 'right justify' text from the cursor
void LiquidCrystal::autoscroll(void) {
  _displaymode |= LCD_ENTRYSHIFTINCREMENT;
  command(LCD_ENTRYMODESET | _displaymode);
}

// This will 'left justify' text from the cursor
void LiquidCrystal::noAutoscroll(void) {
  _displaymode &= ~LCD_ENTRYSHIFTINCREMENT;
  command(LCD_ENTRYMODESET | _displaymode);
}

// Allows us to fill the first 8 CGRAM locations
// with custom characters
void LiquidCrystal::createChar(uint8_t location, uint8_t charmap[]) {
  location &= 0x7; // we only have 8 locations 0-7
  command(LCD_SETCGRAMADDR | (location << 3));
  for (int i=0; i<8; i++) {
    write(charmap[i]);
  }
  setCursor(_posx,_posy);
}
/********** Added to take care of accentuated characters *******************/
void chargeCar_P(const byte tableau[]) {
  for (int i = 0; i < 8; i++) {
    tempcar[i] = pgm_read_byte(tableau + i); // Normalement on aurait fait tableau[i], mais pas ici vu que le tableau est marqué avec PROGMEM
	//Serial.print("ChargeCar ");
	//Serial.print(tempcar[i],DEC);
  }
}
										
uint8_t LiquidCrystal::substitute(uint8_t v) {
 Serial.print("in subtitute ");
 Serial.println(v,DEC);
 //test si un custom est déjà créé et si il y a de la place
 int index_available=-1;
 int index_ready=-1; 
 for (int i=0;i<8;i++) {
   //Serial.println("into for loop");
   //Serial.println(_custom[i],DEC);
   if (_custom[i]==v) {
	   index_ready=i;
   }
   if (_custom[i]==0) {
	   index_available=i;
   }
 }
 /*Serial.print("index_ready = ");
 Serial.println(index_ready,DEC);
 Serial.print("index_available = ");
 Serial.println(index_available,DEC);*/
 uint8_t value=32;
 if (index_ready!=-1) {
   //Si le custom est déjà créer; il suffit de renvoyer son index 
    //Serial.println("custom existant");
    return index_ready;
 } else {
   //Le custom n'existe pas encore il faut le créer si possible
   if (index_available!=-1) {
     //Il y a encore de la place pour créer le nouveau custom
	 Serial.println("creation nouveau custom");
	 Serial.println(_skipnext);
	 _increate=1;
	 switch(_skipnext) {
	  case 1 : {	 
		switch (v) {
		  //case 130 : chargeCar_P(euro);createChar(index_available,tempcar);break;//à
		  case 160 : chargeCar_P(agrave);createChar(index_available,tempcar);break;//à
		  case 162 : chargeCar_P(acirc);createChar(index_available,tempcar);break;//â
		  case 163 : chargeCar_P(atilda);createChar(index_available,tempcar);break;//ã
		  case 164 : chargeCar_P(atrema);createChar(index_available,tempcar);break;//ä
		  case 167 : chargeCar_P(ccedille);createChar(index_available,tempcar);break;//ç
		  case 169 : chargeCar_P(eaigu);createChar(index_available,tempcar);break;//é
		  case 168 : chargeCar_P(egrave);createChar(index_available,tempcar);break;//è
		  case 170 : chargeCar_P(ecirc);createChar(index_available,tempcar);break;//ê
		  case 171 : chargeCar_P(etrema);createChar(index_available,tempcar);break;//ë
		  case 142 : chargeCar_P(imajcirc);createChar(index_available,tempcar);break;//Î
		  case 143 : chargeCar_P(imajtrema);createChar(index_available,tempcar);break;//Ï
		  case 174 : chargeCar_P(icirc);createChar(index_available,tempcar);break;//î
		  case 175 : chargeCar_P(itrema);createChar(index_available,tempcar);break;//ï
		  case 177 : chargeCar_P(ntilda);createChar(index_available,tempcar);break;//ñ
		  case 180 : chargeCar_P(ocirc);createChar(index_available,tempcar);break;//ô
		  case 182 : chargeCar_P(otrema);createChar(index_available,tempcar);break;//ö
		  case 185 : chargeCar_P(ugrave);createChar(index_available,tempcar);break;//ù
		  case 187 : chargeCar_P(ucirc);createChar(index_available,tempcar);break;//û
		  case 188 : chargeCar_P(utrema);createChar(index_available,tempcar);break;//û
		  default : value=63;break;
		  }
	    break;
		}
	  case 2 : {
		  switch (v) {
		  case 130 : chargeCar_P(euro);createChar(index_available,tempcar);break;//à
		  default : value=63;break;
		  }
		break;
	  }
	  case 3 : {
		  switch (v) {
		  case 164 : chargeCar_P(technozone);createChar(index_available,tempcar);break;//¤
		  case 176 : chargeCar_P(degre);createChar(index_available,tempcar);break;//°
		  default : value=63;break;
		  }
		break;
	  }
	 }
	   _increate=0;
	   if (value==32) {
		   _custom[index_available]=v;
		   return index_available;
	   } else {
		   //Le caractere n'est pas encore défini , on renvoie le caractere non accentué
		   return value;
	   }
	 
   } else {
     //Il n'y a plus de place pour un nouveau custom, on renvoie la lettre non accentué
	 //Serial.println("plus de place");
	 
	 switch (v) {
		  case 130 : value=69;break;//€
		  case 160 : value=97;break;//à
		  case 162 : value=97;break;//â
		  case 163 : value=97;break;//ã
		  case 164 : value=97;break;//ä
		  case 167 : value=99;break;//ç
		  case 169 : value=101;break; //é
		  case 168 : value=101;break; //è
		  case 170 : value=101;break; //ê
		  case 171 : value=101;break;//ë
		  case 142 : value=73;break; //î
		  case 143 : value=73;break; //Ï
		  case 174 : value=105;break;//î
		  case 175 : value=105;break;//ï 
		  case 177 : value=110;break;//ñ
		  case 180 : value=111;break; //ô
		  case 182 : value=111;break;//ö
		  case 185 : value=117;break;//ù
		  case 187 : value=117;break;//ü
		  case 188 : value=117;break;//ü
		  default : value=63;break;
	  }
	  return value;
   }
 }
}



/*********** mid level commands, for sending data/cmds */

inline void LiquidCrystal::command(uint8_t value) {
  send(value, LOW);
}

inline size_t LiquidCrystal::write(uint8_t value) {
	//Updated to take care of the accentuated characters
	uint8_t v=value;
	if (_increate==1) {
		//Serial.print("increate : ");
		//Serial.println(value,DEC);
		send(value, HIGH);
		return 1;
	}
    if (_skipnext!=0) {
      //Subtitution of non accentuated characters by accentuated characters
	  v=substitute(value);
	  _skipnext=0;
	}
	if (v==195) {_skipnext=1;}
	if (v==226) {_skipnext=2;}
	if (v==194) {_skipnext=3;}
	if (_skipnext==0) {
		if (v!=172) {
		Serial.print(v,DEC);
		Serial.print(" at ");
		Serial.print(_posx,DEC);
		Serial.print(",");
		Serial.println(_posy,DEC);
		send(v, HIGH);
		_posx+=1;}
		}
	return 1;
}

/************ low level data pushing commands **********/

// write either command or data, with automatic 4/8-bit selection
void LiquidCrystal::send(uint8_t value, uint8_t mode) {
  digitalWrite(_rs_pin, mode);

  // if there is a RW pin indicated, set it low to Write
  if (_rw_pin != 255) { 
    digitalWrite(_rw_pin, LOW);
  }
  
  if (_displayfunction & LCD_8BITMODE) {
    write8bits(value); 
  } else {
    write4bits(value>>4);
    write4bits(value);
  }
}

void LiquidCrystal::pulseEnable(void) {
  digitalWrite(_enable_pin, LOW);
  delayMicroseconds(1);    
  digitalWrite(_enable_pin, HIGH);
  delayMicroseconds(1);    // enable pulse must be >450ns
  digitalWrite(_enable_pin, LOW);
  delayMicroseconds(100);   // commands need > 37us to settle
}

void LiquidCrystal::write4bits(uint8_t value) {
  for (int i = 0; i < 4; i++) {
    digitalWrite(_data_pins[i], (value >> i) & 0x01);
  }

  pulseEnable();
}

void LiquidCrystal::write8bits(uint8_t value) {
  for (int i = 0; i < 8; i++) {
    digitalWrite(_data_pins[i], (value >> i) & 0x01);
  }
  
  pulseEnable();
}
