#include <WiFiServer.h>
#include <ESP8266WiFiSTA.h>
#include <WiFiClient.h>
#include <ESP8266WiFiGeneric.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266WiFiType.h>
#include <ESP8266WiFiScan.h>
#include <ESP8266WiFiAP.h>
#include <WiFiClientSecure.h>
#include <WiFiUdp.h>

#include <Bounce2.h>
#include <Dhcp.h>
#include <EthernetClient.h>
#include <EthernetUdp.h>
#include <Dns.h>
#include <Ethernet.h>
#include <EthernetServer.h>

#include <PubSubClient.h> // Allows us to connect to, and publish to the MQTT broker

#include <Scheduler.h>

const int ledPin = LED_BUILTIN; // This code uses the built-in led for visual feedback that a message has been received
static const byte NSApin = D2;  // NSA, LOW wenn sich die Waehlscheibe dreht
static const byte NSIpin = D1; // NSI, Erzeugt bei der Zuckbewegung der Waehlscheibe die Pulse

/*
Project: Wifi controlled LED matrix display
NodeMCU pins    -> EasyMatrix pins
MOSI-D7-GPIO13  -> DIN
CLK-D5-GPIO14   -> Clk
GPIO0-D3        -> LOAD (CS)

*/
#include <ESP8266WebServer.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Max72xxPanel.h>

#define SSID "Freifunk"                      // insert your SSID 
#define PASS ""                    // insert your password 

// MQTT
// Make sure to update this for your own MQTT Broker!
const char* mqtt_server = "raspifarm.dedyn.io";
const char* mqtt_topic = "/vmp/ws";
const char* mqtt_username = "Test";
const char* mqtt_password = "tester";
// The client id identifies the ESP8266 device. Think of it a bit like a hostname (Or just a name, like Greg).
const char* clientID = "VMPWS";

//long period;
//int offset=1,refresh=0;
// ******************* String form to sent to the client-browser ************************************
String form =
  "<p>"
  "<center>"
  "<h1>Display Bytespeicher Erfurt</h1>"
  "<form action='msg'><p>Gebe hier dein Displaytext ein <input type='text' name='msg' size=100 autofocus> <input type='submit' value='Submit'></form>"
  "</center>";

ESP8266WebServer server(80);                             // HTTP server will listen at port 80
long period;
int offset=1,refresh=0;

int pinCS = 0; // Attach CS to this pin, DIN to MOSI and CLK to SCK (cf http://arduino.cc/en/Reference/SPI )
int numberOfHorizontalDisplays = 4;       // Anzahl der Module Horizontal
int numberOfVerticalDisplays = 1;         // Anzahl der Module Vertikal
String decodedMsg;
Max72xxPanel matrix = Max72xxPanel(pinCS, numberOfHorizontalDisplays, numberOfVerticalDisplays);


String tape = "Waehlscheibe";   // Default Laufschrift
int wait = 50;                  // Zeit in ms für Sroll Geschwindigkeit wo gewartet wird
int helligkeit = 3;             // Helligkeit einstellen 0 bis 15
int spacer = 2;                // leer zeichen laenge
int width = 5 + spacer;        // Schriftlaenge ist 5 Pixel
/*
  handles the messages coming from the webbrowser, restores a few special characters and 
  constructs the strings that can be sent to the oled display
*/
void handle_msg() {
                        
  matrix.fillScreen(LOW);
  server.send(200, "text/html", form);    // Send same page so they can send another msg
  refresh=1;
  // Display msg on Oled
  String msg = server.arg("msg");
  Serial.println(msg);
  decodedMsg = msg;
  // Restore special characters that are misformed to %char by the client browser
  decodedMsg.replace("+", " ");      
  decodedMsg.replace("%21", "!");  
  decodedMsg.replace("%22", "");  
  decodedMsg.replace("%23", "#");
  decodedMsg.replace("%24", "$");
  decodedMsg.replace("%25", "%");  
  decodedMsg.replace("%26", "&");
  decodedMsg.replace("%27", "'");  
  decodedMsg.replace("%28", "(");
  decodedMsg.replace("%29", ")");
  decodedMsg.replace("%2A", "*");
  decodedMsg.replace("%2B", "+");  
  decodedMsg.replace("%2C", ",");  
  decodedMsg.replace("%2F", "/");   
  decodedMsg.replace("%3A", ":");    
  decodedMsg.replace("%3B", ";");  
  decodedMsg.replace("%3C", "<");  
  decodedMsg.replace("%3D", "=");  
  decodedMsg.replace("%3E", ">");
  decodedMsg.replace("%3F", "?");  
  decodedMsg.replace("%40", "@"); 
  Serial.println(decodedMsg);                   // print original string to monitor
 
}

Bounce waehlpulse = Bounce();
Bounce scheibedreht = Bounce();

// Initialise the WiFi and MQTT Client objects
          WiFiClient wifiClient;
          PubSubClient client(mqtt_server, 9765, wifiClient); // 9765 / 1883 is the listener port for the Broker

bool Connect() {
  // Connect to MQTT Server and subscribe to the topic
  if (client.connect(clientID, mqtt_username, mqtt_password)) {
      client.subscribe(mqtt_topic);
      return true;
    }
    else {
      return false;
  }
}
//******************** ( HTTP starten ) ************* 
void startHTTP() {
             // Server starten
              server.on("/", []() {
                server.send(200, "text/html", form);
              });
              server.on("/msg", handle_msg);
              server.begin(); 
              Serial.println("Webserver Ready     ");
}



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

//************************ ( setup )**************************************
void setup(){    
      matrix.setIntensity(helligkeit); // Default werd Helligkeit

          // Adjust to your own needs
     //  matrix.setPosition(0, 1, 0); // The first display is at <0, 0>
    //  matrix.setPosition(1, 0, 0); // The second display is at <1, 0>

    // Adjust to your own needs
       matrix.setPosition(0, 0, 0); // The first display is at <0, 0>
       matrix.setPosition(1, 1, 0); // The second display is at <1, 0>
       matrix.setPosition(2, 2, 0); // The third display is at <2, 0>
       matrix.setPosition(3, 3, 0); // And the last display is at <3, 0>
       matrix.setRotation(0, 1);  // Display 1 mit 90°
       matrix.setRotation(1, 1); // Display 2 mit 90°
       matrix.setRotation(2, 1); // Display 3 mit 90°
       matrix.setRotation(3, 1); // Display 4 mit 90°

   
   //ESP.wdtDisable();                               // used to debug, disable wachdog timer, 
       Serial.begin(115200);                           // full speed to monitor
                               
        WiFi.begin(SSID, PASS);                         // Connect to WiFi network
        while (WiFi.status() != WL_CONNECTED) {         // Wait for connection
        delay(500);
        Serial.print(".");
       }

          Serial.print("SSID : ");                        // prints SSID in monitor
          Serial.println(SSID);                           // to monitor             
 
           char result[16];
           sprintf(result, "%3d.%3d.%1d.%3d", WiFi.localIP()[0], WiFi.localIP()[1], WiFi.localIP()[2], WiFi.localIP()[3]);
           Serial.println();
           Serial.println(result);


           Serial.println(WiFi.localIP());                 // Serial monitor prints localIP
           //Serial.print(analogRead(A0));

           // Connect to MQTT Broker
           // setCallback sets the function to be called when a message is received.
              client.setServer(mqtt_server,9765);
              if (Connect()) {
              Serial.println("Connected Successfully to MQTT Broker!");  
              if (client.subscribe(mqtt_topic)){
              Serial.println("Subscribe"+ String(mqtt_topic));
              }
             }
              else {
              Serial.println("Connection Failed!");
             }
   



  pinMode(NSApin, INPUT_PULLUP);  // Set the button as an input
  pinMode(NSIpin, INPUT_PULLUP);  // Set the button as an input
  //pinMode(ledPin,OUTPUT);

  waehlpulse.attach(NSIpin);
  waehlpulse.interval(5); // interval in ms
  scheibedreht.attach(NSApin);
  scheibedreht.interval(5); // interval in ms

  //Serial.begin(115200);
     startHTTP();

     Scheduler.startLoop(loop1);
}



//***************** Funktion Display Anzeige**********************
void anzeigen () {
    for ( int i = 0 ; i < width * decodedMsg.length() + matrix.width() - 1 - spacer; i++ ) {
    
    matrix.fillScreen(LOW);

    int letter = i / width;
    int x = (matrix.width() - 1) - i % width;
    int y = (matrix.height() - 8) / 2; // zentrieren des textes vertikal
 
    while ( x + width - spacer >= 0 && letter >= 0 ) {
      if ( letter < decodedMsg.length() ) {
        matrix.drawChar(x, y, decodedMsg[letter], HIGH, LOW, 1);
      }

      letter--;
      x -= width;
    }

    matrix.write(); // Send bitmap to display

    //delay(wait);   // Warten für Scrollgeschwindigkeit
  }
}
//***************************** (    ) *******************
unsigned int count=0;

char inttochar(int a)
{
  char b[2];
  String str;
  str = String(a);
  str.toCharArray(b,2);
  return b[0];
  
}

//********************* ( Hauptprogramm 1) *******************
void loop() {
   // If the connection is lost, try to connect again
  if (!client.connected()) {
    Connect();
  }
  // client.loop() just tells the MQTT client code to do what it needs to do itself (i.e. check for messages, etc.)
  client.loop();

  waehlpulse.update();
  scheibedreht.update();

                                        
  if(scheibedreht.read() == LOW)    // Wenn sich die Waehlscheibe dreht...
  {
                                       
    if(waehlpulse.fallingEdge()) {  //... und wenn ein Impuls anliegt
      count++;                      //... Impuls zaehlen
    }  
  }
   else // Wenn sich die Waehlscheibe nicht dreht...
   {                          
      if(count != 0) //... und wenn wir vorher etwas gezaehlt haben
      {                
        //matrix.fillScreen(LOW);
        if(count == 10) //... und wenn der Counter auf 10 steht
        {              
        
          Serial.println(0);         //... schreibe eine 0 auf dem keyboard
        
          matrix.drawChar(2, 1, inttochar(0), HIGH, LOW, 1);
          client.connect(clientID, mqtt_username, mqtt_password);
          delay(10); // This delay ensures that client.publish doesn't clash with the client.connect call
          client.publish(mqtt_topic,"0");
        
        } 
        else   //... andernfalls, wenn der Counter nicht 0 aber auch nicht 10 ist
        {                     
          Serial.println(0+count);   //... schreibe den Counterwert auf dem Keyboard 
          matrix.drawChar(2, 1, inttochar(0+count), HIGH, LOW, 1);  //drawChar(0, 0, 0+count, HIGH, LOW, 1)
          client.connect(clientID, mqtt_username, mqtt_password);
          delay(10); // This delay ensures that client.publish doesn't clash with the client.connect call
          char b[2] = {'0'+count, '\0'};
          client.publish(mqtt_topic, b);   
        }
        matrix.write(); // Send bitmap to display
                    
        count = 0;                    // Counter zuruecksetzen

      
      }
    }
}

//********************* ( Hauptprogramm 2) *******************
void loop1() {

    server.handleClient();                        // HTML Site auf neue Nachrichten pruefen

  // Abfragen ob ein Text uebermittelt wurde im Web Formular
           if (server.arg("msg").length() !=0) {
           decodedMsg = server.arg("msg"); // Wurde ein Text geschickt Lauftext aktualisieren
                                    //matrix.setIntensity(server.arg("helligkeit").toInt());      // helligkeit einstellen
                                    // wait = server.arg("warteMS").toInt());                     // Wartezeit ueberschreiben
      }
  anzeigen();
  yield();
        
}