//
// Library for controlling Electronic Assembly eDIPTFT displays
//
//      Copyright (c) 2013 Stefan Gofferje. All rights reserved.
//
//      This library is free software; you can redistribute it and/or
//      modify it under the terms of the GNU Lesser General Public
//      License as published by the Free Software Foundation; either
//      version 2.1 of the License, or (at your option) any later
//      version.
//
//      This library 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 Lesser General Public License for more
//      details.
//
//      You should have received a copy of the GNU Lesser General
//      Public License along with this library; if not, write to the
//      Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
//      Boston, MA 02110-1301 USA
//

#include "EDIPTFT.h"
#include "Arduino.h"
#define DEBUG false
 

EDIPTFT::EDIPTFT(boolean smallprotocol, boolean disconnected)
  : _smallprotocol{smallprotocol}, disconnected{disconnected} {}


void EDIPTFT::begin(long baud) {
    SERIAL_DEV.begin(baud);
}


void EDIPTFT::sendByte(char data) {
  SERIAL_DEV.write(data);
}


char EDIPTFT::readByte() {
  return SERIAL_DEV.read();
}


void EDIPTFT::waitBytesAvailable() {
   const uint32_t t_start = millis();
   char loop = 0;
  while (loop == 0)
  {
	if (bytesAvailable() != 0){
		loop = 1;
	}
	if(t_start + 100 < millis())
	{
		//Serial.println("Error: waited to long!");
		loop =1;
	}
  }
}


char EDIPTFT::waitandreadByte() {
  waitBytesAvailable();
  char result = readByte();
  return(result);
}


unsigned char EDIPTFT::bytesAvailable() {
    return SERIAL_DEV.available();
	//ÄNDERN
}


void EDIPTFT::sendData(char* data, char len) {
	
  /*for(int i=0; i < len; i++) {
    Serial.print(data[i]);
  }
  Serial.print('\n');
  Serial.println(len);*/
  
  if (DEBUG) {
    unsigned char i;
    for (i = 0; i < len; i++) {
	  //Serial.print(byte(data[i]), HEX);
      SERIAL_DEV.print(byte(data[i]), HEX);
      SERIAL_DEV.print(" ");
    }
    SERIAL_DEV.println();
  }

  if (_smallprotocol) {
    sendSmall(data, len);
  }
  else {
    unsigned char i;
    for(i=0; i < len; i++) {
      sendByte(data[i]);
    }
  }
}


void EDIPTFT::sendSmall(char* data, char len) {
  if (disconnected) {
    return;
  }

  unsigned char i, bcc;
  char ok = 0;
  const uint32_t t_start = millis();

  while (ok == 0) {
    sendByte(0x11);
    bcc = 0x11;

    sendByte(len);
    bcc = bcc + len;

    for(i=0; i < len; i++) {
      sendByte(data[i]);
      bcc = bcc + data[i];
    }
    sendByte(bcc);
    waitBytesAvailable();
    if (bytesAvailable() > 0) {
	   char x = readByte();
	   //Serial.print(uint16_t(x));
      if (x == ACK) ok = 1;
      else {
        ok = 0;
      }
    }
    else {
      delay(200);
      ok = 0;
    }
    if (t_start + 1000 < millis()) {
      ok = 1;
      disconnected = true;
    }
  }
}


void EDIPTFT::sendSmallDC2(char* data, char len) {
  unsigned char i, bcc;
  char ok = 0;

  while (ok == 0) {

    sendByte(0x12);
    bcc = 0x12;

    for(i=0; i < len; i++) {
      sendByte(data[i]);
      bcc = bcc + data[i];
    }
    sendByte(bcc);
    waitBytesAvailable(); //delay(6); im 17er
    if (bytesAvailable() > 0) {
      if (readByte() == ACK) ok = 1;
      else ok = 0;
    }
    else {
      delay(200);
      ok = 0;
    }
  }
}


void EDIPTFT::smallProtoSelect(char address) {
  char command [] = {
    0x03, 'A', 'S', address
  };
  sendSmallDC2(command, sizeof(command));
}


void EDIPTFT::smallProtoDeselect(char address) {
  char command [] = {
    0x03, 'A', 'D', address
  };
  sendSmallDC2(command, sizeof(command));
}


unsigned char EDIPTFT::datainBuffer() {
  unsigned char result;
  char command [] = {
    0x01, 'I'
  };
  sendSmallDC2(command, sizeof(command));
  waitandreadByte();
  waitandreadByte();
  result=waitandreadByte();
  waitandreadByte();
  waitandreadByte();
  return result;
}


int EDIPTFT::readBuffer(char* data) { //return void
    unsigned char len, i; // char in 17er
    char command [] = {
        0x01, 'S'
    };
    sendSmallDC2(command, sizeof(command));
    waitandreadByte();
    len=waitandreadByte();
    char result[len];
    for (i = 0; i < len; i++) {
        result[i] = waitandreadByte();
    }
    memcpy(data, result, len);
    waitandreadByte();
    return len; //zeile nicht vorhanden 
}


void EDIPTFT::clear() { //vgl 17er
  this->deleteDisplay();
  this->removeTouchArea(0, 1);
}


void EDIPTFT::deleteDisplay() {
    char command [] = {
      27, 'D', 'L'
    };
    sendData(command, sizeof(command));
}


void EDIPTFT::invert() {
  char command [] = {
    27, 'D', 'I'
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::setDisplayColor(char fg, char bg) {
  char command [] = {
    27, 'F', 'D', fg, bg
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::fillDisplayColor(char bg) {
  char command [] = {
    27, 'D', 'F', bg
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::terminalOn(boolean on) {
  if (on) {
    char command [] = {27, 'T', 'E'};
    sendData(command, sizeof(command));
  }
  else {
    char command [] = {27, 'T', 'A'};
    sendData(command, sizeof(command));
  }
}


void EDIPTFT::loadImage(int x1, int y1, int nr) {
    char command [] = {27, 'U', 'I',
    #if COORD_SIZE == 1
        (char)x1, (char)y1,
    #else
        lowByte(x1), highByte(x1), lowByte(y1), highByte(y1),
    #endif
        nr};
    sendData(command, sizeof(command));
}


void EDIPTFT::cursorOn(boolean on) {
  if (on) {
    char command [] = {27, 'T', 'C', 1};
    sendData(command, sizeof(command));
  }
  else {
    char command [] = {27, 'T', 'C', 0};
    sendData(command, sizeof(command));
  }
}


void EDIPTFT::setCursor(char col, char row) {
  char command [] = {27, 'T', 'P', col, row};
  sendData(command, sizeof(command));
}

void EDIPTFT::displayLight(char no) {
  char command [] = {
    27, 'Y', 'H', no
  };
  sendData(command, sizeof(command));
}

void EDIPTFT::defineBargraph(char dir, char no, int x1, int y1, int x2, int y2, byte sv, byte ev, char type, char mst) {
  char command [] = {
    27, 'B', dir, no,
    #if COORD_SIZE == 1
        x1, y1, x2, y2,
    #else
        lowByte(x1), highByte(x1), lowByte(y1), highByte(y1),
        lowByte(x2), highByte(x2), lowByte(y2), highByte(y2),
    #endif
    char(sv),
    char(ev),
    type,
    mst
  };
  sendData(command, sizeof(command));
  //mst fehlt 17
}


void EDIPTFT::updateBargraph(char no, char val) {
  char command [] = {
    27, 'B', 'A', no, val
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::setBargraphColor(char no, char fg, char bg, char fr) {
  char command [] = {
    27, 'F', 'B', no, fg, bg, fr
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::linkBargraphLight(char no) {
  char command [] = {
    27, 'Y', 'B', no
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::makeBargraphTouch(char no) {
  char command [] = {
    27, 'A', 'B', no
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::deleteBargraph(char no,char n1) {
  char command [] = {
    27, 'B', 'D', no, n1
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::defineInstrument(char no, int x1, int y1, char image, char angle, char sv, char ev) {
  char command [] = {
    27, 'I', 'P', no,
    #if COORD_SIZE == 1
        x1, y1,
    #else
        lowByte(x1), highByte(x1), lowByte(y1), highByte(y1),
    #endif
    image, angle, sv, ev
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::updateInstrument(char no, char val) {
  char command [] = {
    27, 'I', 'A', no, val
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::redrawInstrument(char no) {
  char command [] = {
    27, 'I', 'N', no
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::deleteInstrument(char no, char n1, char n2) {
  char command [] = {
    27, 'B', 'D', no, n1, n2
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::setLineColor(char fg, char bg) {
  char command [] = {
    27, 'F', 'G', fg, bg
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::setLineThick(char x, char y) {
  char command [] = {
    27, 'G', 'Z', x, y
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::setTextColor(char fg, char bg) {
  char command [] = {
    27, 'F', 'Z', fg, bg
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::setTextFont(char font) {
  char command [] = {
    27, 'Z', 'F', font
  };
  sendData(command, sizeof(command));
}

void EDIPTFT::setTextSize(int xsize, int ysize){
	char command[] = {27,	'Z',	'Z',	xsize,	ysize};
	sendData(command,sizeof(command));
}

void EDIPTFT::setTextAngle(char angle) {
  // 0 = 0°, 1 = 90°, 2 = 180°, 3 = 270°
  char command [] = {
    27, 'Z', 'W', angle
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::drawText(uint16_t x1, uint16_t y1, char justification, const char* text) {
	//nicht const 17//
  byte len = strlen(text);
  byte i;
  char helper [3 + 4 + len + 1];
  helper[0] = 27; //esc
  helper[1] = 'Z';
  helper[2] = justification;
  helper[3] = x1 & 0xFF;
  helper[4] = (x1 >> 8) & 0xFF;
  helper[5] = y1 & 0xFF;
  helper[6] = (y1 >> 8) & 0xFF;
  for (i = 0; i <= len; i++) {
      helper[i + 7] = text[i];
  }
  
  sendData(helper, sizeof(helper));
}


void EDIPTFT::drawTextInRect(int x1, int y1, int x2, int y2, uint8_t align, const char* text) {
  int len_text = strlen(text);
  int len = 3 + 8 + 1 + len_text + 1;
  char data[len] = {
    27, 'Z', 'B', lowByte(x1), highByte(x1), lowByte(y1), highByte(y1),
    lowByte(x2), highByte(x2), lowByte(y2), highByte(y2), align
  };
  for (int i = 0; i < len_text; i++) {
    data[3 + 8 + 1 + i] = text[i];
  }
  data[len - 1] = 0;

  sendData(data, len);
}


void EDIPTFT::drawLine(int x1, int y1, int x2, int y2) {
  char command [] = {
    27,'G','D',
    #if COORD_SIZE == 1
        x1, y1, x2, y2
    #else
        lowByte(x1),highByte(x1),lowByte(y1),highByte(y1),
        lowByte(x2),highByte(x2),lowByte(y2),highByte(y2)
    #endif
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::drawRect(int x1, int y1, int x2, int y2) {
  char command [] = {
    27,'G','R',
    #if COORD_SIZE == 1
        x1, y1, x2, y2
    #else
        lowByte(x1),highByte(x1),lowByte(y1),highByte(y1),
        lowByte(x2),highByte(x2),lowByte(y2),highByte(y2)
    #endif
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::drawRectf(int x1, int y1, int x2, int y2, char color) {
  char command [] = {
    27,'R','F',
    #if COORD_SIZE == 1
        x1, y1, x2, y2,
    #else
        lowByte(x1),highByte(x1),lowByte(y1),highByte(y1),
        lowByte(x2),highByte(x2),lowByte(y2),highByte(y2),
    #endif
    color
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::clearRect(int x1, int y1, int x2, int y2) {
  Serial.print("Clearing ");
	Serial.print(x1); Serial.print(", ");
	Serial.print(y1); Serial.print(", ");
	Serial.print(x2); Serial.print(", ");
	Serial.println(y2);
  char command [] = {
    27, 'R', 'L',
    #if COORD_SIZE == 1
        x1, y1, x2, y2
    #else
        lowByte(x1),highByte(x1),lowByte(y1),highByte(y1),
        lowByte(x2),highByte(x2),lowByte(y2),highByte(y2),
    #endif
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::defineTouchKey(int x1, int y1, int x2, int y2, char down, char up,
                             const char* text) { //text nicht const 17
  byte len = strlen(text);
  byte i;
  char helper [len + 6 + 4 * COORD_SIZE];//len+13 17
  char command [] = {
    27, 'A', 'T',
    #if COORD_SIZE == 1
        x1, y1, x2, y2,
    #else
        lowByte(x1), highByte(x1), lowByte(y1), highByte(y1),
        lowByte(x2), highByte(x2), lowByte(y2), highByte(y2),
    #endif
    down, up
  };
  for (i = 0; i < (5 + 4 * COORD_SIZE); i++) helper[i] = command[i];//i<=12 17
  for (i = 0; i <= len+1; i++) helper[i + 5 + 4 * COORD_SIZE] = text[i];//i<=len 17
  sendData(helper, sizeof(helper));//size len+14 17
}


void EDIPTFT::defineTouchSwitch(int x1, int y1, int x2, int y2,
                                char down, char up, const char* text) {//const nicht 17
  byte len = strlen(text);
  byte i;
  char helper [len + 6 + 4 * COORD_SIZE];//len+13 17
  char command [] = {
    27, 'A', 'K',
    #if COORD_SIZE == 1
        x1, y1, x2, y2,
    #else
        lowByte(x1),highByte(x1),lowByte(y1),highByte(y1),
        lowByte(x2),highByte(x2),lowByte(y2),highByte(y2),
    #endif
    down, up
  };
  for (i = 0; i < 5 + 4 * COORD_SIZE; i++) helper[i] = command[i];
  for (i = 0; i <= len; i++) helper[i + 5 + 4 * COORD_SIZE] = text[i];
  sendData(helper, sizeof(helper));//size len+14
}


void EDIPTFT::defineTouchSwitch(int x, int y, int img, char downcode,
                                char upcode, const char* text) {
  byte len = strlen(text);
  byte i;
  byte n = 6 + 2 * COORD_SIZE;
  char helper [len + n + 1];
  char command [] = {
    27, 'A', 'J',
    #if COORD_SIZE == 1
        x, y,
    #else
        lowByte(x), highByte(x), lowByte(y), highByte(y),
    #endif
    img, downcode, upcode
  };
  for (i = 0; i < n; i++) helper[i] = command[i];
  for (i = 0; i <= len; i++) helper[i + n] = text[i];
  sendData(helper, sizeof(helper));
}


void EDIPTFT::setTouchSwitch(char code,char value) {
  char command [] = {
    27, 'A', 'P', code, value
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::setTouchkeyColors(
  char n1, char n2, char n3, char s1, char s2, char s3) {
  char command [] = {
    27, 'F', 'E', n1, n2, n3, s1, s2, s3
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::setTouchkeyFont(char font) {
  char command [] = {
    27, 'A', 'F', font
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::setTouchkeyLabelColors(char nf, char sf) {
  char command [] = {
    27, 'F', 'A', nf, sf
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::setTouchGroup(char group) {
  char command [] = {
    27, 'A', 'R', group
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::removeTouchArea(char code, char n1) {
  char command [] = {
    27, 'A', 'L', code, n1
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::callMacro(uint nr) {
  char command[] = {
    27, 'M', 'N', nr
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::callTouchMacro(uint nr) {
  char command[] = {
    27, 'M', 'T', nr
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::callMenuMacro(uint nr) {
  char command[] = {
    27, 'M', 'M', nr
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::defineTouchMenu(int x1, int y1, int x2, int y2,
    char downcode, char upcode, char mnucode, const char *text) {
  byte len = strlen(text);
  byte n = 6 + 4 * COORD_SIZE;
  char helper [len + n + 1];

  char command [] = {
    27, 'A', 'M',
    #if COORD_SIZE == 1
        x1, y1, x2, y2,
    #else
        lowByte(x1),highByte(x1),lowByte(y1),highByte(y1),
        lowByte(x2),highByte(x2),lowByte(y2),highByte(y2),
    #endif
    downcode, upcode, mnucode
  };
  for (int i = 0; i < n; i++) helper[i] = command[i];
  for (int i = 0; i <= len; i++) helper[i + n] = text[i];
  sendData(helper, sizeof(helper));
}


void EDIPTFT::openTouchMenu() {
  char command [] = {
    27, 'N', 'T', 2
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::setMenuFont(char font) {
  char command [] = {
    27, 'N', 'F', font
  };
  sendData(command, sizeof(command));
}


void EDIPTFT::setTouchMenuAutomation(bool val) {
  char n1 = val ? 1 : 0;
  char command [] = {
    27, 'N', 'T', n1
  };
  sendData(command, sizeof(command));
}