/*
 ESP8266 MQTT 

this sketch subscibes 2 MQTT topics:

inTopic = "huette/clubraum/000/redshift/actuators/frame

 receive a binary MQTT message and update the whole display
 currently: 8 pixel in one byte  , 8 x 8 = 64 byte per frame(picture)
 later versions: maybe 1 byte per pixel for different level of brightness

inTopic2 = huette/clubraum/000/redshift/actuators/set_pixel

 receive a  command (ascii)
 possible commands:
 0-512 #1  (set one pixel)
 0-512 #0  (clear one pixel)
 text Hello (display Text, max 10 letters)
 
*/

#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#include <PubSubClient.h>

#include "nokia5110_chars.h"
#include "conf.h"
                 //war:
#define TAKT D5  // D1
#define HIDE D6  // D2
#define DATA D7  // D3

#define DEBUG
#ifdef DEBUG
 #define DEBUG_PRINT(x)     Serial.print (x)
 #define DEBUG_PRINTDEC(x)     Serial.print (x, DEC)
 #define DEBUG_PRINTLN(x)  Serial.println (x)
#else
 #define DEBUG_PRINT(x)
 #define DEBUG_PRINTDEC(x)
 #define DEBUG_PRINTLN(x)
#endif 


#define BIT_S(var,b) ((var&(1<<b))?1:0)

// clear bit
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif

// set bit
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif


WiFiClient espClient;
PubSubClient client(espClient);

    /*  position */
uint8_t cursor_x;
uint8_t cursor_y;



int r,g,b;

byte framebuffer[NUMPIXELS];
String stringPixel, stringFrame, stringIntopic ;

// WIP
String scrolltext = " Hello World!";


void setup() {
 pinMode(BUILTIN_LED, OUTPUT);     // Initialize the BUILTIN_LED pin as an output
 pinMode(DATA, OUTPUT);
 pinMode(HIDE, OUTPUT);
 pinMode(TAKT, OUTPUT);
 Serial.begin(115200);

  setup_wifi();
  delay(500);
  client.setServer(mqtt_server, 1883);
 
  client.subscribe("inTopic");
  client.subscribe("inTopic2");
  
  client.setCallback(callback);

  stringPixel = String(inTopic2);
  stringFrame = String(inTopic);

  ArduinoOTA.setHostname("redshift"); // give an name to our module
  ArduinoOTA.begin(); // OTA initialization

  
} //setup

void setup_wifi() {
  delay(10);
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.softAPdisconnect(true);
  WiFi.mode(WIFI_STA);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
     framebuffer[8]=8;
     show();
    delay(500);
    Serial.print(".");
    
  }

  Serial.println("");
  Serial.println("WiFi connected");
  framebuffer[8]=4;
  show();
  
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  client.subscribe(inTopic);
  client.setCallback(callback);
  
} //setup_wifi



void set_pixel(int x, int y,int color ){
  byte b_index , mybit;
  b_index = x/8 + y*8;
  mybit = x % 8;
  
  // color= 0 = off, alles andere: on
  if (color > 0)
    sbi(framebuffer[b_index], mybit);
  else
    cbi(framebuffer[b_index], mybit);
  //Serial.println(b_index);
  //Serial.println(mybit);
  //Serial.println(image[b_index]);
  //Serial.println("-----");
} // set_pixel(x,y,col)


// eindimensional, ueber alle Zeilen
void setPixelColor(int j, char r ){

  //nothing todo - it works on all lines
 //int x=j%8;
 //int y = j/8;
 DEBUG_PRINT("setPixelColor:");
 DEBUG_PRINTDEC(j);
 DEBUG_PRINT(" r:");
 DEBUG_PRINTDEC(r);
 DEBUG_PRINTLN("");
 //DEBUG_PRINTDEC(y);
 set_pixel( j, 0 , r );
}


void shiftbyte(byte dat)  // wird von show() benutzt
{
  unsigned char p;
  int i;
  p=0x01;

  for(i=0;i<8;i++)
  {
  if (dat & p)
       digitalWrite(DATA, 1);   
  else
      digitalWrite(DATA, 0);
    
  digitalWrite(TAKT, 0);
  digitalWrite(TAKT, 1);
  p<<=1;
  }
 
}


void show()
{
  int line,j;
  int offset=0;
  byte val;  
   digitalWrite(HIDE, 1)  ;   //hide 
  //for (line=0;line<8;line++)
  for (line=8;line>=0;line--)
  {
    //for(j=7;j>=0;j--)
    // 8 bytes on each line
    for(j=0;j<8;j++)
    { 
     //val=framebuffer[offset]-1; //show inverted
     offset=line*8+j;
      val=framebuffer[offset];
     shiftbyte(val);
    }
  }
   digitalWrite(HIDE, 0);    //show
  
  }


void setpixel( byte* payload, unsigned int length){
  int splitat;
  String s = "";
  for(int i = 0; i < length; i++) {
    
        s += (char)payload[i];
    //  Serial.println((char)payload[i]);
  
    
     if (isWhitespace((char)payload[i])) {
        splitat = i;
        i = length;
     } //if
      
   } //for
  
   String Numberstring = "";
   String Colorstring = "";
   for(int i = 0; i < splitat; i++) {
       Numberstring += (char) payload[i];   
   }

   for (int i = (splitat + 1); i < length; i++) {
      Colorstring += (char) payload[i];
   }
 
//Colorstring contains something like = "#00ff00" or "00ff00"
// you need to convert it to 0,255,0 and insert it to the two calls below
// Am ende Newline.
   int intcolors=(int) strtol(&Colorstring[1],NULL,16);
   int r = intcolors >> 16;
   int g = intcolors >> 8 & 0xFF;
   int b = intcolors & 0xFF;

  
  Serial.print("Number:" + Numberstring + " Color:" + Colorstring + " intcolors:");
  //Serial.println(intcolors);

 if(Numberstring == "text") {
    //scrolltext = Colorstring;
    //DEBUG_PRINT("scrolltext:");
    //DEBUG_PRINTLN(scrolltext);
    //scrolltext(); 
    cursor_x=0;
    cursor_y=0;
    nokia_lcd_write_string(Colorstring.c_str() ,1);
    show(); 
  }
  
  if(Numberstring.toInt() == 255) {
    for(int i = 0; i < NUMPIXELS; i++) {
      if (r == 0)
        framebuffer[i]=0;
      else
        framebuffer[i]=1;
      
    }
    show(); 
  }
  else {
    setPixelColor(Numberstring.toInt(), r ); 
    show(); // This sends the updated pixel color to the hardware.
  }
}  //setpixel



// ein Frame das binär via MQTT empfangen wurde
// wird in den Frambuffer geschrieben und 1x show() aufgerufen
// 
void writeframe(byte* payload, unsigned int length){
   //Serial.print("TOPIC=frame");

  // 1 pixel = 1 bit , 1 byte = 8 pixel
  //write bytes to framebuffer
  for (int i = 0; i < (NUMPIXELS) ; i++) {
    //Serial.print((char)payload[i]);
    //Serial.print(" ");
    framebuffer[i] = payload[i];
  }


  // wenn 1 pixel = 1 byte dann das hier
   //write frame to LEDs
  //  for(int i = 0; i < NUMPIXELS ; i++) {
      //Serial.print(" ");
      //Serial.print(j);
      //Serial.print(":");
      //Serial.print(r);
      
   //   r = payload[i];
   //   setPixelColor(i, r ); 
   //  }
  Serial.println();
  show();  
} //writeframe

void callback(char* topic, byte* payload, unsigned int length) {

  //Serial.print("Message arrived [");
  //Serial.print(topic);
  //Serial.print("] ");
  //Serial.print("length:");
  //Serial.print(   length );

  stringIntopic = String(topic);
  if (stringIntopic == stringPixel ){
    //Serial.print("TOPIC=setpixel");
    setpixel(payload,length);
  }
  
  if (stringIntopic == stringFrame ){
    writeframe(payload,length);
  }
} //callback

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect(mqtt_client_id)) {
      Serial.println("connected");
      // Once connected, publish an announcement...
      client.publish(outTopic, "hello world");
      // ... and resubscribe
      client.subscribe(inTopic);
      client.subscribe(inTopic2);
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
} //reconnect



void nokia_lcd_write_char(char code, uint8_t scale)
{
  register uint8_t x, y;

  for (x = 0; x < 5*scale; x++)
    for (y = 0; y < 7*scale; y++)
      if (pgm_read_byte(&CHARSET[code-32][x/scale]) & (1 << y/scale))
        set_pixel(cursor_x + x, cursor_y + y, 1);
      else
        set_pixel(cursor_x + x, cursor_y + y, 0);

  cursor_x += 5*scale + 1;
  if (cursor_x >= 64) {
    cursor_x = 0;
    //cursor_y += 7*scale + 1;
    cursor_y = 0;
  }
  if (cursor_y >= 10) {
    cursor_x = 0;
    cursor_y = 0;
  }
}

void nokia_lcd_write_string(const char *str, uint8_t scale)
{
  while(*str)
    nokia_lcd_write_char(*str++, scale);
}

void nokia_lcd_set_cursor(uint8_t x, uint8_t y)
{
  cursor_x = x;
  cursor_y = y;
}





void loop() {

  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  ArduinoOTA.handle(); 
  
    delay(10);
    // show();
}  //loop