////////////////////////////////////////////////////////////////////////////////
//                                                                            //
// Programname: HSA_LCD_Shield - Code                                         //
// Date: 23.04.2018                                                           //
// Description: Code-File of the LCD-Shield, which was designed and built in  //
//              the Modul "Elektronikdesign". In this file you can find the   //
//              code of all functions mentioned in the Header-File of the     //
//              class.                                                        //
//                                                                            //
// Author: Tobias Müller, M. Eng.                                             //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
////////////////////          Include Header-File           ////////////////////
////////////////////////////////////////////////////////////////////////////////

#include "HSA_LCD_Shield.h"

////////////////////////////////////////////////////////////////////////////////
////////////////////           Privat Functions             ////////////////////
////////////////////////////////////////////////////////////////////////////////

////////////////////               Send Message             ////////////////////

void HSA_LCD_Shield::_sendMessage(byte* bytes, byte sizeBytes){

  // Small timeout before access the I²C-Bus
  delayMicroseconds(DELAY_TRANS_US);

  // Start I²C transmission
  Wire.beginTransmission(this->__i2cAddress);

  // Send an array of Bytes over I²C, depending of its size
  for(byte i = 0x00; i < sizeBytes; i++) Wire.write(bytes[i]);

  // Stop I²C transmission
  Wire.endTransmission();

  // Small timeout after access the I²C-Bus
  delayMicroseconds(DELAY_TRANS_US);

  // Exit function
  return;

}

////////////////////////////////////////////////////////////////////////////////
////////////////////           Public Functions             ////////////////////
////////////////////////////////////////////////////////////////////////////////

////////////////////               Constructor              ////////////////////

HSA_LCD_Shield::HSA_LCD_Shield(char address, const char config[0x05]) {
  
  // Save I²C-Address
  this->__i2cAddress = address;

  // Save the configuration of Buttons/LEDs
  strcpy(this->__config,config);

  // Exit Constructor
  return;

}

HSA_LCD_Shield::HSA_LCD_Shield(char address) {

  // Save I²C-Address
  this->__i2cAddress = address;

  // Save the configuration of Buttons/LEDs
  strcpy(this->__config,CONFIG_L1B1);

  // Exit Constructor
  return;

}

HSA_LCD_Shield::HSA_LCD_Shield(const char config[0x05]){

  // Save I²C-Address
  this->__i2cAddress = I2C_ADDRESS;

  // Save the configuration of Buttons/LEDs
  strcpy(this->__config,config);

  // Exit Constructor
  return;

}

HSA_LCD_Shield::HSA_LCD_Shield(){

  // Save I²C-Address
  this->__i2cAddress = I2C_ADDRESS;

  // Save the configuration of Buttons/LEDs
  strcpy(this->__config,CONFIG_L1B1);

  // Exit Constructor
  return;

}

////////////////////             Deconstructor              ////////////////////

HSA_LCD_Shield::~HSA_LCD_Shield(void) {

  // Exit function
  return;

}

////////////////////          Start Configuration           ////////////////////

bool HSA_LCD_Shield::begin(void) {

  // Check config-value and setup the LEDs, depending of config-value
  if(strcmp(this->__config,CONFIG_L1B0) == false || 
    strcmp(this->__config,CONFIG_L1B1) == false) {
    
    // Setup LEDs
    pinMode(LED_GREEN,OUTPUT);
    pinMode(LED_RED,OUTPUT);
    digitalWrite(LED_GREEN,HIGH);
    digitalWrite(LED_RED,HIGH);

    // Store the value "true", if LEDs are configured
    this->__leds = true;
    
  }
  else {
    
    // Store the value "false", if LEDs are not configured
    this->__leds = false;

  }

  // Check config-value and setup the Buttons depending of config-value
  if(strcmp(this->__config,CONFIG_L0B1) == false || 
    strcmp(this->__config,CONFIG_L1B1) == false) {
    
    // Setup Buttons
    pinMode(BUTTON_UP,INPUT);
    pinMode(BUTTON_RIGHT,INPUT);
    pinMode(BUTTON_DOWN,INPUT);
    pinMode(BUTTON_LEFT,INPUT);

    // Store the value "true", if buttons are configured
    this->__buttons = true;

  }
  else {
    
    // Store the value "false", if buttons are not configured
    this->__buttons = false;
    
  }

  // If config-value is unknown, quit configuration, otherwise configure I²C
  if(strcmp(this->__config,CONFIG_L1B1) != false &&
    strcmp(this->__config,CONFIG_L0B1) != false &&
    strcmp(this->__config,CONFIG_L1B0) != false &&
    strcmp(this->__config,CONFIG_L0B0) != false) {

    // Store the value false for buttons and LEDs are not configured
    this->__buttons = false;
    this->__leds = false;

    // If config-value unknown, exit function with negative feedback
    return false;

  }
  else {

    // Initialize I²C
    Wire.begin();

    // Define an array of Bytes for configure the Display
    byte buffer[] = {CONTROL_BYTE_CB,
                     LCD_PARA_FUNC1,
                     LCD_PARA_EXT_FUNC,
                     LCD_PARA_ENTRY_MODE,
                     LCD_PARA_BIAS_SET,
                     LCD_PARA_FUNC2,
                     LCD_PARA_INT_OSC,
                     LCD_PARA_FOL_CON,
                     LCD_PARA_POW_CON,
                     LCD_PARA_CONTRAST,
                     LCD_PARA_FUNC3,
                     LCD_PARA_DIS_CON,
                     LCD_PARA_CLR_DIS};

    // Send array of bytes
    _sendMessage(buffer, sizeof(buffer));

    // Exit function with positive feedback
    return true;

  }
}

////////////////////             LCD-Backlight              ////////////////////

void HSA_LCD_Shield::lcdBacklight(bool value) {

  // Turning on/off LCD-Backlight
  digitalWrite(LCD_BACKLIGHT,value);

  // Exit function
  return;

}

////////////////////             Clear Display              ////////////////////

void HSA_LCD_Shield::clearDisplay(void) {

  // Define an array of Bytes for clearing the Display
  byte buffer[] = {CONTROL_BYTE_CB,
                   LCD_PARA_CLR_DIS};
  
  // Send array of bytes
  _sendMessage(buffer, sizeof(buffer));

  // Exit function
  return;

}


////////////////////             Return Config              ////////////////////

char* HSA_LCD_Shield::returnConfig(void) {

  // Exit function and return the configuration value
  return this->__config;

}

////////////////////            Return I²C-Address          ////////////////////

char HSA_LCD_Shield::returnAddress(void){

  // Exit function and return the value of the I²C-Address
  return this->__i2cAddress;

}

////////////////////               Control LEDs             ////////////////////

void HSA_LCD_Shield::controlLed(char ledPin, bool state) {

  // Change state of a LED, if they are configured
  if(this->__leds) digitalWrite(ledPin,!state);

  // Exit function
  return;

}

////////////////////          Get the pressed Button        ////////////////////

char HSA_LCD_Shield::getButton() {

  // return 1, if only button "Up" is pressed and configured
  if(digitalRead(BUTTON_UP) &&
     !digitalRead(BUTTON_RIGHT) &&
     !digitalRead(BUTTON_DOWN) &&
     !digitalRead(BUTTON_LEFT) &&
     this->__buttons)
     return BUTTON_UP;
  
  // return 2, if only button "Right" is pressed and configured
  if(!digitalRead(BUTTON_UP) &&
     digitalRead(BUTTON_RIGHT) &&
     !digitalRead(BUTTON_DOWN) &&
     !digitalRead(BUTTON_LEFT) &&
     this->__buttons)
     return BUTTON_RIGHT;

  // return 3, if only button "Down" is pressed and configured
  if(!digitalRead(BUTTON_UP) &&
     !digitalRead(BUTTON_RIGHT) &&
     digitalRead(BUTTON_DOWN) &&
     !digitalRead(BUTTON_LEFT) &&
     this->__buttons)
     return BUTTON_DOWN;

  // return 4, if only button "Left" is pressed and configured
  if(!digitalRead(BUTTON_UP) &&
     !digitalRead(BUTTON_RIGHT) &&
     !digitalRead(BUTTON_DOWN) &&
     digitalRead(BUTTON_LEFT) &&
     this->__buttons)
     return BUTTON_LEFT;

  // return false, if no button or more than one button is pressed
  return false;

}

////////////////////                Write Row 1             ////////////////////

void HSA_LCD_Shield::writeRow1(const char* bytes) {

  // Define buffer for test Message
  byte buffer[ARRAY_SIZE_ROW] = ""; 

  // Define variable to store a boolean value, if a control-byte was found
  bool controlByte = false; 

  // Create data-array with message and send the message at the end
  for(byte i = 0x00; i <= ARRAY_SIZE_ROW; i++){

    // Set control-byte for changing parameter in the first iteration
    if(i == 0x00) buffer[i] = CONTROL_BYTE_CB;

    // Change the cursor position to row 1 in the second iteration
    if(i == 0x01) buffer[i] = LCD_PARA_DIS_ROW1;

    // Set control-byte for writing to the display in the third iteration
    if(i == 0x02) buffer[i] = CONTROL_BYTE_DCB;

    // Copy bytes into the buffer between iteration 2 to 13
    if(i > 0x02 && i < ARRAY_SIZE_ROW) {

      // If no control-byte was found, copy byte into buffer
      if(!controlByte) {

        // Check byte for control-byte, if not, copy byte into buffer
        if(isControl(bytes[i - 0x03])) controlByte = true;
        else buffer[i] = bytes[i - 0x03];

      }

      // If control-byte was found, fill message with free space
      if(controlByte) {
        
        // Fill Buffer with free space
        buffer[i] = FREE_SPACE;

      }
    }

    // Send message in the last iteration
    if(i == ARRAY_SIZE_ROW) _sendMessage(buffer,sizeof(buffer));

  }

  // Exit function
  return;

}

////////////////////                Write Row 2             ////////////////////

void HSA_LCD_Shield::writeRow2(const char* bytes) {

  // Define buffer for test Message
  byte buffer[ARRAY_SIZE_ROW] = ""; 

  // Define variable to store a boolean value, if a control-byte was found
  bool controlByte = false; 

  // Create data-array with message and send the message at the end
  for(byte i = 0x00; i <= ARRAY_SIZE_ROW; i++){

    // Set control-byte for changing parameter in the first iteration
    if(i == 0x00) buffer[i] = CONTROL_BYTE_CB;

    // Change the cursor position to row 2 in the second iteration
    if(i == 0x01) buffer[i] = LCD_PARA_DIS_ROW2;

    // Set control-byte for writing to the display in the third iteration
    if(i == 0x02) buffer[i] = CONTROL_BYTE_DCB;

    // Copy bytes into the buffer between iteration 2 to 13
    if(i > 0x02 && i < ARRAY_SIZE_ROW) {

      // If no control-byte was found, copy byte into buffer
      if(!controlByte) {

        // Check byte for control-byte, if not, copy byte into buffer
        if(isControl(bytes[i - 0x03])) controlByte = true;
        else buffer[i] = bytes[i - 0x03];

      }

      // If control-byte was found, fill message with free space
      if(controlByte) {
        
        // Fill Buffer with free space
        buffer[i] = FREE_SPACE;

      }
    }

    // Send message in the last iteration
    if(i == ARRAY_SIZE_ROW) _sendMessage(buffer,sizeof(buffer));

  }

  // Exit function
  return;

}

////////////////////                Write Row 3             ////////////////////

void HSA_LCD_Shield::writeRow3(const char* bytes) {

  // Define buffer for test Message
  byte buffer[ARRAY_SIZE_ROW] = ""; 

  // Define variable to store a boolean value, if a control-byte was found
  bool controlByte = false; 

  // Create data-array with message and send the message at the end
  for(byte i = 0x00; i <= ARRAY_SIZE_ROW; i++){

    // Set control-byte for changing parameter in the first iteration
    if(i == 0x00) buffer[i] = CONTROL_BYTE_CB;

    // Change the cursor position to row 3 in the second iteration
    if(i == 0x01) buffer[i] = LCD_PARA_DIS_ROW3;

    // Set control-byte for writing to the display in the third iteration
    if(i == 0x02) buffer[i] = CONTROL_BYTE_DCB;

    // Copy bytes into the buffer between iteration 2 to 13
    if(i > 0x02 && i < ARRAY_SIZE_ROW) {

      // If no control-byte was found, copy byte into buffer
      if(!controlByte) {

        // Check byte for control-byte
        if(isControl(bytes[i - 0x03])) controlByte = true;

        // If not, copy byte into buffer
        else buffer[i] = bytes[i - 0x03];

      }

      // If control-byte was found, fill message with free space
      if(controlByte) {
        
        // Fill Buffer with free space
        buffer[i] = FREE_SPACE;

      }
    }

    // Send message in the last iteration
    if(i == ARRAY_SIZE_ROW) _sendMessage(buffer,sizeof(buffer));

  }

  // Exit function
  return;
}

////////////////////                Write Row 4             ////////////////////

void HSA_LCD_Shield::writeRow4(const char* bytes) {

  // Define buffer for test Message
  byte buffer[ARRAY_SIZE_ROW] = ""; 

  // Define variable to store a boolean value, if a control-byte was found
  bool controlByte = false; 

  // Create data-array with message and send the message at the end
  for(byte i = 0x00; i <= ARRAY_SIZE_ROW; i++){

    // Set control-byte for changing parameter in the first iteration
    if(i == 0x00) buffer[i] = CONTROL_BYTE_CB;

    // Change the cursor position to row 4 in the second iteration
    if(i == 0x01) buffer[i] = LCD_PARA_DIS_ROW4;

    // Set control-byte for writing to the display in the third iteration
    if(i == 0x02) buffer[i] = CONTROL_BYTE_DCB;

    // Copy bytes into the buffer between iteration 2 to 13
    if(i > 0x02 && i < ARRAY_SIZE_ROW) {

      // If no control-byte was found, copy byte into buffer
      if(!controlByte) {

        // Check byte for control-byte
        if(isControl(bytes[i - 0x03])) controlByte = true;

        // if not, copy byte into buffer
        else buffer[i] = bytes[i - 0x03];

      }

      // If control-byte was found, fill message with free space
      if(controlByte) {
        
        // Fill Buffer with free space
        buffer[i] = FREE_SPACE;

      }
    }

    // Send message in the last iteration
    if(i == ARRAY_SIZE_ROW) _sendMessage(buffer,sizeof(buffer));

  }

  // Exit function
  return;

}

////////////////////                Write 4 Rows            ////////////////////

void HSA_LCD_Shield::write4Rows(const char* bytes) {

  // Define buffer for test Message
  char buffer[ARRAY_SIZE_ROW] = ""; 

  // Define variable to store the actual row position
  byte rowPosition = LCD_PARA_DIS_ROW1; 

  // Define variable, which count the copied bytes
  byte countBytes = 0x00;

  // Create data-array with messages for 4 rows and send the messages
  for(byte i = 0x00; i < 0x28; i++){

    // If the position of the row is not selected, skip following code
    if(rowPosition == false) continue; 

    // If first row is selected
    if(rowPosition == LCD_PARA_DIS_ROW1) {

      // If counted bytes has not reach max value, copy byte into buffer
      if(countBytes < 0x0A) buffer[countBytes] = bytes[i];

      // Check, if copied byte was control-byte
      if(isControl(bytes[i])) {
        
        // Check if control-byte was \n or \r or end of string, 
        // set counted bytes to max value
        if(bytes[i] == BACKSLASH_N || 
           bytes[i] == BACKSLASH_R ||
           bytes[i] == STRING_END) 
           countBytes = 0x0A;

        // Check if control-byte was end of the string, deactivate row position
        if(bytes[i] == STRING_END) rowPosition = false;
        
      }

      // Otherwise increase counted bytes
      else countBytes++;

      // If count bytes is max value
      if(countBytes == 0x0A) {

        // Reset count bytes 
        countBytes = 0x00;

        // write first row
        writeRow1(buffer);

        // If row position is deactivated
        if(rowPosition == false) {

          // clear following rows
          writeRow2(&bytes[i]);
          writeRow3(&bytes[i]);
          writeRow4(&bytes[i]);

        }
        
        // Otherwise set row position the row 2
        else rowPosition = LCD_PARA_DIS_ROW2;

        // skip the rest of the code in this iteration
        continue;

      }
    }

    // If second row is selected
    if(rowPosition == LCD_PARA_DIS_ROW2) {

      // If counted bytes has not reach max value, copy byte into buffer
      if(countBytes < 0x0A) buffer[countBytes] = bytes[i];

      // Check, if copied byte was control-byte
      if(isControl(bytes[i])) {
        
        // Check if control-byte was \n or \r or end of string, 
        // set counted bytes to max value
        if(bytes[i] == BACKSLASH_N || 
           bytes[i] == BACKSLASH_R ||
           bytes[i] == STRING_END) 
           countBytes = 0x0A;

        // Check if control-byte was end of the string, deactivate row position
        if(bytes[i] == STRING_END) rowPosition = false;
        
      }

      // Otherwise increase counted bytes
      else countBytes++;

      // If count bytes is max value
      if(countBytes == 0x0A) {

        // Reset count bytes 
        countBytes = 0x00;

        // write second row
        writeRow2(buffer);

        // If row position is deactivated
        if(rowPosition == false) {

          // clear following rows
          writeRow3(&bytes[i]);
          writeRow4(&bytes[i]);

        }
        
        // Otherwise set row position the row 3
        else rowPosition = LCD_PARA_DIS_ROW3;

        // skip the rest of the code in this iteration
        continue;

      }
    }

    // If third row is selected
    if(rowPosition == LCD_PARA_DIS_ROW3) {

      // If counted bytes has not reach max value, copy byte into buffer
      if(countBytes < 0x0A) buffer[countBytes] = bytes[i];

      // Check, if copied byte was control-byte
      if(isControl(bytes[i])) {
        
        // Check if control-byte was \n or \r or end of string, 
        // set counted bytes to max value
        if(bytes[i] == BACKSLASH_N || 
           bytes[i] == BACKSLASH_R ||
           bytes[i] == STRING_END) 
           countBytes = 0x0A;

        // Check if control-byte was end of the string, deactivate row position
        if(bytes[i] == STRING_END) rowPosition = false;
        
      }

      // Otherwise increase counted bytes
      else countBytes++;

      // If count bytes is max value
      if(countBytes == 0x0A) {

        // Reset count bytes 
        countBytes = 0x00;

        // write third row
        writeRow3(buffer);

        // If row position is deactivated, clear following row
        if(rowPosition == false) writeRow4(&bytes[i]);

        // Otherwise set row position the row 4
        else rowPosition = LCD_PARA_DIS_ROW4;

        // skip the rest of the code in this iteration
        continue;

      }
    }

    // If fourth row is selected
    if(rowPosition == LCD_PARA_DIS_ROW4) {

      // If counted bytes has not reach max value, copy byte into buffer
      if(countBytes < 0x0A) buffer[countBytes] = bytes[i];

      // Check, if copied byte was control-byte
      if(isControl(bytes[i])) {
        
        // Check if control-byte was \n or \r or end of string, 
        // set counted bytes to max value
        if(bytes[i] == BACKSLASH_N || 
           bytes[i] == BACKSLASH_R ||
           bytes[i] == STRING_END) 
           countBytes = 0x0A;

        // Check if control-byte was end of the string, deactivate row position
        if(bytes[i] == STRING_END) rowPosition = false;
        
      }

      // Otherwise increase counted bytes
      else countBytes++;

      // If count bytes is max value
      if(countBytes == 0x0A) {

        // Reset count bytes 
        countBytes = 0x00;

        // write fourth row
        writeRow4(buffer);

        // If row position is not deactivated, deaktivate row position
        if(rowPosition != false) rowPosition = false;

        // skip the rest of the code in this iteration
        continue;

      }
    }
  }

  // Exit function
  return;

}