#include <OneWire.h>

#include "DallasTemperature.h"

/*
 *  server_ip is the IP address of the ESP8266 module, will be
 *  printed to Serial when the module is connected.
 *  last edited 04/11/2015
 */
#include "ESP8266WiFi.h"

#define VERSION " Version 3.1.1-dev 04/11/2015"
//#define DEV (1)
//#define DEBUG (1)

// WiFi parameters
//#if defined (DEV)
const char* ssid_1 = "NETGEAR2";
//#else
const char* ssid_2 = "NETGEAR";
//#endif

const char* password = "G1nger376z";
int status = WL_IDLE_STATUS;
// specify the port to listen on as an argument
WiFiServer server(80);

OneWire oneWire(5);
DallasTemperature TempSensor(&oneWire);

//int ledPin = 2;  // Already pulled up so do not use elsewhere
int Cw = 1;
int Ccw = 0;
int RotationDirection;
int PreviousDirection;
int StepsDone;
int StepsRequired;
int StoredSteps;
int LastDirection;
int OpenDirection;
int CloseDirection;
int mNow;
int mBlindClose;
int mBlindOpen;
int Tried_1;
int Tried_2;
int Count;

unsigned long OldMillis;
unsigned long PrevMillis;

bool BlindOpen = true;
bool BlindClosed = true;
bool JustWoke = true;
bool StatusAvailable = false;
bool StatusDone = true;
bool TimeCorrect = false;
bool Connect_failed = true;
bool AutoOpenAvailable = true;
bool AutoCloseAvailable = true;
unsigned long LoopNow;
unsigned long LoopCount;
unsigned long LoopMillis;

//int StepNumber;

void setup()
{
  // makesure watchdog is disabled for the setup routine
  wdt_disable();

  // Start Serial
  Serial.begin(115200);
  delay(10);

  // configure ds18b20
  TempSensor.begin();
  //TempSensor.setResolution(SensorAddrs, 9);

  Serial.println();
  Serial.print("Trying to connect to ");
  Serial.println(ssid_1);

  // Try to connect to 1st Access Point
  WiFi.begin(ssid_1, password);
  Count = 0;
  while (WiFi.status() != WL_CONNECTED && Count < 150) {
    delay(100);
    Serial.print(".");
    Count += 1;
    if (Count % 30 == 0) {
      Serial.println();
    }
  }
  // If failed to connect, try 2nd Access Point
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println();
    Serial.print("Trying to connect to ");
    Serial.println(ssid_2);

    WiFi.begin(ssid_2, password);
    Count = 0;
    while (WiFi.status() != WL_CONNECTED) {
      delay(100);
      Serial.print(".");
      Count += 1;
      if (Count % 30 == 0) {
        Serial.println();
      }
      if (Count >= 150) {
        Serial.println("Failed to connect .... Exiting!");
        exit;
      }
    }
  }

  Serial.println();
  Serial.println("WiFi connected");

  // Start the server
  server.begin();
#if defined(DEBUG)
  Serial.println("Server started");
#endif
  // Print the IP address
  Serial.print("This IP Address is ");
  Serial.println(WiFi.localIP());

  Serial.println(VERSION);

  //  pinMode(ledPin, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(13, OUTPUT);
  pinMode(14, OUTPUT);
  pinMode(15, OUTPUT);

  PrevMillis = millis();

  wdt_enable(4000);
}

void loop()
{
  byte CwSequence[8] = {1, 3, 2, 6, 4, 12, 8, 9};
  byte CcwSequence[8] = {9, 8, 12, 4, 6, 2, 3, 1};
  byte Sequence[8];
  byte Pattern;
  int x;
  int Index;
  int StepDelay = 10;
  int mLast;
  int Count;
  bool MotorDone = true;
  bool DoneTwice = false;
  String Reply;
  float dsbReply;

  WiFiClient client;
  /*
  #if defined(DEBUG)
    Serial.begin(115200);
  #else
    Serial.end();
  #endif
  */
  /*
  #if defined (DEBUG)
    ++LoopCount;
    if (LoopCount >= 1000000) {
      LoopCount = 0;
      Serial.println(millis() - LoopMillis);
      LoopMillis = millis();
    }
  #endif
  */
  if (millis() > (PrevMillis + 60000)) {
    //Serial.print("Loop Time = ");
    //Serial.println(mNow);
    mNow = UpdateMinutes(mNow, PrevMillis);
//    dsbReply = GetTemp();
#if defined(DEBUG)
//    Serial.println(dsbReply);
#endif
    PrevMillis = millis();
  }
  //Serial.println(mNow, mLast);
  //  Serial.println(Count);
  /*
  if (!DoneTwice) {
    dsbReply = GetTemp();
    Serial.println(dsbReply);
    Count = Count + 1;
    if (Count >= 2) {
      DoneTwice = true;
    }
  }
  */
  /*
  Serial.print("Stepsdone ");
  Serial.print(StepsDone);
  Serial.print ("  Stepsrequired ");
  Serial.println(StepsRequired);
  */
  if (StepsRequired != 0 && !JustWoke)  {
    if (StepsDone < StepsRequired && LastDirection != RotationDirection)  {
      Index = StepsDone % 8;
      if (RotationDirection == Cw) {
        Pattern = CwSequence[Index];
      }
      if (RotationDirection == Ccw) {
        Pattern = CcwSequence[Index];
      }
      RunStepper(Pattern);
      StepsDone = StepsDone + 1;
      //Serial.println(StepsDone);
      delay(5);
      StatusDone = false;
    }
    else {
      // Set GPIOs for stepper to LOW
      digitalWrite(12, LOW);
      digitalWrite(13, LOW);
      digitalWrite(14, LOW);
      digitalWrite(15, LOW);

      if (StepsDone == StepsRequired && !StatusDone) {
#if defined (DEBUG)
        Serial.println("Setting Status Flag");
#endif
        if (RotationDirection == OpenDirection) {
          BlindOpen = true;
          BlindClosed = false;
          StatusAvailable = true;
        }
        if (RotationDirection == CloseDirection) {
          BlindOpen = false;
          BlindClosed = true;
          StatusAvailable = true;
        }
        LastDirection = RotationDirection;
        StepsRequired = 0;
        StatusDone = true;
      }
    }
  }

  client = server.available();
  if (!client) {
    return;
  }

  // Wait until the client sends some data
#if defined(DEBUG)
  Serial.println("new client");
#endif
  //int CountLoops;
  while (!client.available()) {
    delay(2);
    //++CountLoops;
    //if (CountLoops >= 1000) {
      //  Wait 2 secs then exit loop
      //break;
    //}
  }
  /*
  if (CountLoops >= 1000) {
    CountLoops = 0;
    client.flush();
    client.stop();
    return;
  }
  */

  // Read the first line of the request
  String req = client.readStringUntil('\r');
#if defined(DEBUG)
  Serial.println(req);
#endif
  client.flush();

  int MessageOut = WhatIsRequested(req);

#if defined (DEBUG)
  Serial.print("MessageOut = ");
  Serial.println(MessageOut);
#endif

  client.flush();

  Reply = MakeResponse(MessageOut, "Steps");

  //int val = 1;
#if defined(DEBUG)
  Serial.println(Reply);
#endif
  client.print(Reply);
  delay(5);
  client.stop();
#if defined(DEBUG)
  Serial.println("   Client disconnected");
#endif

  // The client will actually be disconnected when the
  // function returns and 'client' object is destroyed.
#if defined (DEBUG)
  Serial.print("mNow = ");
  Serial.println(mNow);
#endif
  /* Good place to start some other control??
     Time updated checked every minute.
     If no message received to operate blind
     after the operation times sent last time
     that operation times were calculated,
     after 3 minutes elapsed take control locally */
     
  if (!JustWoke && TimeCorrect) {
    if (mNow > (mBlindClose + 3) && LastDirection != CloseDirection) {
      StepsRequired = 9216;
      RotationDirection = Ccw;
      StepsDone = 0;
      AutoCloseAvailable = false;
      AutoOpenAvailable = true;
#if defined (DEBUG)
      Serial.println("Closing Blind");
#endif
    }
    if (mNow > (mBlindOpen + 3) && mNow < mBlindClose && LastDirection != OpenDirection) {
      StepsRequired = 9216;
      RotationDirection = Cw;
      StepsDone = 0;
      AutoCloseAvailable = true;
      AutoOpenAvailable = false;
#if defined (DEBUG)
      Serial.println("Opening Blind");
#endif
    }
  }

  mLast = mNow;
  //LoopMillis = millis();
}
/*###################################################################
* Functions
*####################################################################
*/


float GetTemp()
{    
  float celsius;
    // ds18b20 temperature
    TempSensor.requestTemperatures();
    yield();
    celsius = TempSensor.getTempCByIndex(0);
    yield();
    //Serial.println(celsius);
 
    //server.send(200, "text/html", String("LED is now ") + ((state)?"on":"off") + "<br>Temperature: " + celsius + "c");

    return celsius;
}

void RunStepper(byte OutputPattern) {
  if (OutputPattern & 1) {
    digitalWrite(12, HIGH);
  }
  else {
    digitalWrite(12, LOW);
  }

  if (OutputPattern & 2) {
    digitalWrite(13, HIGH);
  }
  else {
    digitalWrite(13, LOW);
  }
  if (OutputPattern & 4) {
    digitalWrite(14, HIGH);
  }
  else {
    digitalWrite(14, LOW);
  }
  if (OutputPattern & 8) {
    digitalWrite(15, HIGH);
  }
  else {
    digitalWrite(15, LOW);
  }
}

void SerialDisplayTime(int Minutes)
{
  int Num2Display;
#if defined(DEBUG)  
  Num2Display = Minutes / 60;
  if (Num2Display < 10) {
    Serial.print("0");
  }
  Serial.print(Num2Display);
  Serial.print(":");
  Num2Display = Minutes % 60;
  if (Num2Display < 10) {
    Serial.print("0");
  }
  if (Num2Display == 0) {
    Serial.println("0");
  }
  else {
    Serial.println(Num2Display);
  }
#endif  
}

int UpdateMinutes(int Minutes, unsigned long OldMillis) {
  int NewMinutes;

#if defined(DEBUG)
  if (TimeCorrect) {
    Serial.print("True ");
  }
  else {
    Serial.print("False ");
  }
  Serial.print("Minutes = ");
  Serial.println(Minutes);
#endif  

  NewMinutes = Minutes + (millis() - OldMillis) / 60000;
  if (NewMinutes == 1440) {
    NewMinutes = 0;
  }
  return NewMinutes;
}

int WhatIsRequested(String Request)
{
  WiFiClient client = server.available();
#if defined(DEBUG)  
  Serial.print("Request message is ... ");
  Serial.println(Request);
#endif  

  yield();

  // Match the request
  String Temp = "";
  int StartPosn;
  int EndPosn;
  int MessageOut;
  boolean Ignored = false;

  if (Request.indexOf("/steps/") != -1) {
    MessageOut = 1;
    Temp = "";
    StartPosn = Request.indexOf("/", 5);
    EndPosn = Request.indexOf("/", StartPosn + 1);
    for (int x = StartPosn + 1; x <= EndPosn - 1; x++) {
      Temp += Request.charAt(x);
      yield();
    }
    StepsRequired = Temp.toInt();
    StoredSteps = StepsRequired;
#if defined(DEBUG)
    Serial.println(StepsRequired);
#endif
    StartPosn = EndPosn;
    EndPosn = Request.indexOf("/", StartPosn + 1);
    Temp = "";

    for (int x = StartPosn + 1; x <= EndPosn - 1; x++) {
      Temp += Request.charAt(x);
      yield();
    }
#if defined(DEBUG)
    Serial.println(Temp);
#endif
    if (Temp == "Cw") {
      RotationDirection = Cw;
    }
    if (Temp == "Ccw") {
      RotationDirection = Ccw;
    }
    StepsDone = 0;

    mNow = UpdateMinutes(mNow, millis());
    //yield();
    //WiFiClient client = server.available();
    yield();

    SerialDisplayTime(mNow);

  }
  else if (Request.indexOf("/Ca va?/") != -1) {
    MessageOut = 2;
#if defined(DEBUG)
    Serial.println(MessageOut);
#endif    
    Temp = "";
    StartPosn = Request.indexOf("/", 5);
    EndPosn = Request.indexOf("/", StartPosn + 1);
    // Extract time from message
    for (int x = StartPosn + 1; x <= EndPosn - 1; x++) {
      Temp += Request.charAt(x);
      yield;
    }
    // Update local timekeeping
    mNow = Temp.toInt();
    OldMillis = millis();
    if (!JustWoke) {
      TimeCorrect = true;
    }

    SerialDisplayTime(mNow);
  }
  else if (Request.indexOf("/BlindClose/") != -1) {
    MessageOut = 3;
    if (Request.indexOf("/Ccw/") != -1) {
      CloseDirection = Ccw;
    }
    else {
      CloseDirection = Cw;
    }
    Temp = "";
    StartPosn = Request.indexOf("/", 5);
    EndPosn = Request.indexOf("/", StartPosn + 1);
    for (int x = StartPosn + 1; x <= EndPosn - 1; x++) {
      Temp += Request.charAt(x);
      yield();
    }
    mBlindClose = Temp.toInt();

    SerialDisplayTime(mBlindClose);
  }
  else if (Request.indexOf("/BlindOpen/") != -1) {
    MessageOut = 4;
    if (Request.indexOf("/Ccw/") != -1) {
      OpenDirection = Ccw;
    }
    else {
      OpenDirection = Cw;
    }
    Temp = "";
    StartPosn = Request.indexOf("/", 5);
    EndPosn = Request.indexOf("/", StartPosn + 1);
    for (int x = StartPosn + 1; x <= EndPosn - 1; x++) {
      Temp += Request.charAt(x);
      yield();
    }
    mBlindOpen = Temp.toInt();

    SerialDisplayTime(mBlindOpen);
  }
  else if (Request.indexOf("/Temperature/") != -1) {
    MessageOut = 5;

  }
  else if (Request.indexOf("/LastAction/") != -1) {
    MessageOut = 6;
    if (Request.indexOf("/Ccw/") != -1) {
      LastDirection = Ccw;
#if defined(DEBUG)      
      Serial.println("LastDirection = Ccw");
#endif      
    }
    else if (Request.indexOf("/Cw/") != -1) {
      LastDirection = Cw;
#if defined(DEBUG)      
      Serial.println("LastDirection = Cw");
#endif      
    }
  }
  else {
    MessageOut = 99;
#if defined(DEBUG)
    Serial.println("invalid request");
#endif    
  }
  client.stop();
  yield();
  return MessageOut;
}


 
