#include <EthernetServer.h>
#include <Dhcp.h>
#include <Ethernet.h>
#include <EthernetClient.h>
#include <Dns.h>
#include <Wire.h>                              // enable I2C.


#include <DHT.h>

#include <OneWire.h>
#include <DallasTemperature.h>
#include <Ethernet.h> //Load Ethernet Library
#include <EthernetUdp.h> //Load UDP Library
#include <SPI.h> //Load the SPI Library
#include <avr/wdt.h>

#define TOTAL_CIRCUITS 2                       // <-- CHANGE THIS | set how many I2C circuits are attached to the Tentacle

#define DHTTYPE DHT22

#define sump_low 22
#define sump_high 23//
#define biofilter_low 24
#define biofilter_high 25//
#define DHTambient 26
#define DHTdome 27
#define ONE_WIRE_BUS 28
#define sumpWarn 29
#define pump 48//
#define blower 49//
#define uv 50//
#define bioPump 51//
#define growoutWarn 52


OneWire oneWire(ONE_WIRE_BUS);
DHT dht_ambient(DHTambient, DHTTYPE);
DHT dht_dome(DHTdome, DHTTYPE);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

//byte mac[] = { 0x90, 0xA1, 0xDA, 0x00, 0x59, 0x09 };//IP .26
byte mac[] = { 0x90, 0xA1, 0xDA, 0x00, 0xDE, 0xAD };//IP .26

byte gateway[] = { 192, 168, 0, 254 };
byte subnet[] = { 255, 255, 255, 0 };
EthernetServer server(80);
String readString;



int buttonState1;             // the current reading from the input pin
int lastButtonState1 = HIGH;   // the previous reading from the input pin
int buttonState2;             // the current reading from the input pin
int lastButtonState2 = HIGH;
// the following variables are long's because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long lastDebounceTime1 = 0;
long lastDebounceTime2 = 0; // the last time the output pin was toggled
long debounceDelay = 50;    // the debounce time; increase if the output flickers
unsigned long resetInterval = 120000;
unsigned long resetTimer = 0;


unsigned long blowerOntime = 0;
unsigned long blowerOfftime = 0;
long blowerOninterval = 3000000;
long blowerOffinterval = 600000;
//long blowerOffinterval = 5000;
//long blowerOninterval = 10000;


byte blower_state = 1;
byte depthstatusSump = 1;
byte depthstatusBiofilter = 1;
byte watchDog = 1;

/////////////////////////////////////////////////////////
const unsigned int baud_host  = 9600;        // set baud rate for host serial monitor(pc/mac/other)
const unsigned int send_readings_every = 5000; // set at what intervals the readings are sent to the computer (NOTE: this is not the frequency of taking the readings!)
unsigned long next_serial_time;

char sensordata[30];                          // A 30 byte character array to hold incoming data from the sensors
byte sensor_bytes_received = 0;               // We need to know how many characters bytes have been received
byte code = 0;                                // used to hold the I2C response code.
byte in_char = 0;                             // used as a 1 byte buffer to store in bound bytes from the I2C Circuit.

int channel_ids[] = {97, 98};        // <-- CHANGE THIS. A list of I2C ids that you set your circuits to.
char *channel_names[] = {"MAINTANK", "A4TANK"};   // <-- CHANGE THIS. A list of channel names (must be the same order as in channel_ids[]) - only used to designate the readings in serial communications
String readings[TOTAL_CIRCUITS];               // an array of strings to hold the readings of each channel
int channel = 0;                              // INT pointer to hold the current position in the channel_ids/channel_names array

const unsigned int reading_delay = 1000;      // time to wait for the circuit to process a read command. datasheets say 1 second.
unsigned long next_reading_time;              // holds the time when the next reading should be ready from the circuit
boolean request_pending = false;              // wether or not we're waiting for a reading

const unsigned int blink_frequency = 250;     // the frequency of the led blinking, in milliseconds
unsigned long next_blink_time;                // holds the next time the led should change state
boolean led_state = LOW;                      // keeps track of the current led state

//////////////////////////////////////////////////////////////////


void setup() {
  Serial.begin(19200);
//  Ethernet.hostName("BREEDING");

  Serial.println("Hi Chris");


  pinMode(sumpWarn, INPUT_PULLUP);
  pinMode(growoutWarn, INPUT_PULLUP);

  pinMode(pump, OUTPUT);
  pinMode(blower, OUTPUT);
  pinMode(uv, OUTPUT);
  pinMode(bioPump, OUTPUT);
  pinMode(sump_low, INPUT_PULLUP);
  pinMode(sump_high, INPUT_PULLUP);
  pinMode (biofilter_low, INPUT_PULLUP);
  pinMode (biofilter_high, INPUT_PULLUP);
  //  pinMode (power, INPUT_PULLUP);
  sensors.begin();
  dht_dome.begin();
  dht_ambient.begin();

  Ethernet.begin(mac); //Initialize Ethernet
  Serial.print("server is at ");
  Serial.println(Ethernet.localIP());
//  Serial.println(Ethernet.getHostName());
  wdt_enable(WDTO_8S);
  Wire.begin();                   // enable I2C port.
  next_serial_time = millis() + send_readings_every;  // calculate the next point in time we should do serial communications




}

// how much serial data we expect before a newline
const unsigned int MAX_INPUT = 100;
// the maximum length of paramters we accept
const int MAX_PARAM = 20;


// Example GET line: GET /?foo=bar HTTP/1.1
void processGet (const char * data)
{
  // find where the parameters start
  const char * paramsPos = strchr (data, '?');
  if (paramsPos == NULL)
    return;  // no parameters
  // find the trailing space
  const char * spacePos = strchr (paramsPos, ' ');
  if (spacePos == NULL)
    return;  // no space found
  // work out how long the parameters are
  int paramLength = spacePos - paramsPos - 1;
  // see if too long
  if (paramLength >= MAX_PARAM)
    return;  // too long for us
  // copy parameters into a buffer
  char param [MAX_PARAM];
  memcpy (param, paramsPos + 1, paramLength);  // skip the "?"
  param [paramLength] = 0;  // null terminator

  // do things depending on argument (GET parameters)

  if (strcmp (param, "blowerOn") == 0)
  {
    Serial.println (F("Activating blower"));
    blower_state = 1;
  } else if (strcmp (param, "blowerOff") == 0)
  {
    Serial.println (F("Deactivating blower"));
    blower_state = 0;
  }
  if (strcmp (param, "sumpPumpOn") == 0)
  {
    Serial.println (F("activating pump"));
    depthstatusSump = 1;
  } else if (strcmp (param, "sumpPumpOff") == 0)
  {
    Serial.println (F("Deactivating pump"));
    depthstatusSump = 0;
  }
  if (strcmp (param, "bioPumpOn") == 0)
  {
    Serial.println (F("activating pump"));
    depthstatusBiofilter = 1;
  } else if (strcmp (param, "bioPumpOff") == 0)
  {
    Serial.println (F("Deactivating pump"));
    depthstatusBiofilter = 0;
  }
  if (strcmp (param, "fullStop") == 0)
  {
    Serial.println (F("FULL STOP"));
    depthstatusBiofilter = 0;
    depthstatusSump = 0;
    blower_state = 0;

  } else if (strcmp (param, "fullStart") == 0)
  {
    Serial.println (F("FULL START"));
   depthstatusBiofilter = 1;
    depthstatusSump = 1;
    blower_state = 1;
  }
}  // end of processGet

// here to process incoming serial data after a terminator received
void processData (const char * data)
{
  Serial.println (data);
  if (strlen (data) < 4)
    return;

  if (memcmp (data, "GET ", 4) == 0)
    processGet (&data [4]);
}  // end of processData

bool processIncomingByte (const byte inByte)
{
  static char input_line [MAX_INPUT];
  static unsigned int input_pos = 0;
  switch (inByte)
  {
    case '\n':   // end of text
      input_line [input_pos] = 0;  // terminating null byte
      if (input_pos == 0)
        return true;   // got blank line
      // terminator reached! process input_line here ...
      processData (input_line);
      // reset buffer for next time
      input_pos = 0;
      break;

    case '\r':   // discard carriage return
      break;

    default:
      // keep adding if not full ... allow for terminating null byte
      if (input_pos < (MAX_INPUT - 1))
        input_line [input_pos++] = inByte;
      break;
  }  // end of switch
  return false;    // don't have a blank line yet
} // end of processIncomingByte


void do_serial() {
  if (millis() >= next_serial_time) {                // is it time for the next serial communication?
          // loop through all the sensors
      Serial.print(channel_names[97]);                // print channel name
      Serial.print(":\t");
      Serial.println(readings[97]);                     // print the actual reading
          Serial.print(channel_names[98]);                // print channel name
      Serial.print(":\t");
      Serial.println(readings[98]);                     // print the actual reading
    next_serial_time = millis() + send_readings_every;
  }
}



// take sensor readings in a "asynchronous" way
void do_sensor_readings() {
  if (request_pending) {                          // is a request pending?
    if (millis() >= next_reading_time) {          // is it time for the reading to be taken?
      receive_reading();                          // do the actual I2C communication
    }
  } else {                                        // no request is pending,
    channel = (channel + 1) % TOTAL_CIRCUITS;     // switch to the next channel (increase current channel by 1, and roll over if we're at the last channel using the % modulo operator)
    request_reading();                            // do the actual I2C communication
  }
}



// Request a reading from the current channel
void request_reading() {
  request_pending = true;
  Wire.beginTransmission(channel_ids[channel]); // call the circuit by its ID number.
  Wire.write('r');                    // request a reading by sending 'r'
  Wire.endTransmission();                   // end the I2C data transmission.
  next_reading_time = millis() + reading_delay; // calculate the next time to request a reading
}



// Receive data from the I2C bus
void receive_reading() {
  sensor_bytes_received = 0;                        // reset data counter
  memset(sensordata, 0, sizeof(sensordata));        // clear sensordata array;

  Wire.requestFrom(channel_ids[channel], 48, 1);    // call the circuit and request 48 bytes (this is more then we need).
  code = Wire.read();

  while (Wire.available()) {          // are there bytes to receive?
    in_char = Wire.read();            // receive a byte.

    if (in_char == 0) {               // if we see that we have been sent a null command.
      Wire.endTransmission();         // end the I2C data transmission.
      break;                          // exit the while loop, we're done here
    }
    else {
      sensordata[sensor_bytes_received] = in_char;  // load this byte into our array.
      sensor_bytes_received++;
    }
  }

  switch (code) {                       // switch case based on what the response code is.
    case 1:                             // decimal 1  means the command was successful.
      readings[channel] = sensordata;
      break;                              // exits the switch case.

    case 2:                             // decimal 2 means the command has failed.
      readings[channel] = "error: command failed";
      break;                              // exits the switch case.

    case 254:                           // decimal 254  means the command has not yet been finished calculating.
      readings[channel] = "reading not ready";
      break;                              // exits the switch case.

    case 255:                           // decimal 255 means there is no further data to send.
      readings[channel] = "error: no data";
      break;                              // exits the switch case.
  }
  request_pending = false;                  // set pending to false, so we can continue to the next sensor
}

 void clientCheck(){
    // listen for incoming clients
  EthernetClient client = server.available();
  if (client)
  {
    resetTimer = millis();
    sensors.requestTemperatures(); // Send the command to get temperatures
    float sumptemp = sensors.getTempCByIndex(0);
    float tanktemp = sensors.getTempCByIndex(1);
    float domeH = dht_dome.readHumidity();
    float domeAirtemp = dht_dome.readTemperature();
    float ambientH = dht_ambient.readHumidity();
    float ambientAirtemp = dht_ambient.readTemperature();
    Serial.println(F("Client connected"));
    // an http request ends with a blank line
    boolean done = false;
    while (client.connected() && !done)
    {
      while (client.available () > 0 && !done)
        done = processIncomingByte (client.read ());
    }  // end of while client connected

    // send a standard http response header
   
    client.print(sumptemp);
    client.print(",");
    client.print(tanktemp);
    client.print(",");
    client.print(domeAirtemp);
    client.print(",");
    client.print(domeH);
    client.print(",");
    client.print(ambientAirtemp);
    client.print(",");
    client.print(ambientH);
    client.print(",");
    client.print(digitalRead(pump));
    client.print(",");
    client.print(digitalRead(bioPump));
    client.print(",");
    client.print(digitalRead(blower));
    client.print(",");
    client.print(digitalRead(sumpWarn));
    client.print(",");
    client.print(digitalRead(growoutWarn));

    // give the web browser time to receive the data
    delay(10);
    // close the connection:
    client.stop();
    Serial.println(F("Client disconnected"));
  }  // end of got a new client
}
void loop()
{

    do_sensor_readings();
    do_serial();
    clientCheck();
  


  if (digitalRead(sump_low) == LOW)
  {
    depthstatusSump = 0;
  }
  if (digitalRead(sump_high) == HIGH)
  {
    depthstatusSump = 1;
  }

  if (depthstatusSump == 1)
  {
    digitalWrite(pump, HIGH);
    digitalWrite(uv, HIGH);
  } else if (depthstatusSump == 0)
  {
    digitalWrite(pump, LOW);
    digitalWrite(uv, LOW);
  }
  if (digitalRead(biofilter_low) == LOW)
  {
    depthstatusBiofilter = 0;
  }
  if (digitalRead(biofilter_high) == HIGH)
  {
    depthstatusBiofilter = 1;
  }
  if (depthstatusBiofilter == 1)
  {
    digitalWrite(bioPump, HIGH);

  } else if (depthstatusBiofilter == 0)
  {
    digitalWrite(bioPump, LOW);

  }


  if (blower_state == 1)
  {
    blowerOfftime = millis();
  } else if (blower_state == 0)
  {
    blowerOntime = millis();
  }

  if (millis() - blowerOntime > blowerOninterval)
  {
    blower_state = 0;
    //Serial.println("blower off");
    //Serial.println(blowerOntime);
  } else if (millis() - blowerOfftime > blowerOffinterval)
  {
    blower_state = 1;
    //Serial.println("blower on");
    //Serial.println(blowerOfftime);
  }

  if (blower_state == 1)
  {
    digitalWrite(blower, HIGH);
  } else if (blower_state == 0)
  {
    digitalWrite(blower, LOW);
  }
 
 

  if (watchDog == 1)
  {
    wdt_reset();

  } else if (watchDog == 0) {

  }

  if (millis() - resetTimer > resetInterval)
  {
    watchDog = 0;
  } else if (millis() - resetTimer < resetInterval)
  {
    watchDog = 1;

  }

}

