/*
 * TODO: write something here
*/
#include "projectGroot.h"

/* ===== SETUP AND LOOP ===== */
void setup() {
  // Starts the serial com with PC
  Serial.begin(9600);
  Serial.println("Running setup function");

  // Other initializations
  dht.begin();
  temperatureReadingsCounter = 0;
  pinMode(LOW_TEMP_LED_PIN, OUTPUT);
  pinMode(HIGH_TEMP_LED_PIN, OUTPUT);
  pinMode(RELAY_PIN, OUTPUT);

  pinMode(DIST_TRIG_PIN, OUTPUT);
  pinMode(DIST_ECHO_PIN, INPUT);

  byte numDigits = NUMBER_OF_DIGITS;
  byte digitPins[] = {DIGIT_1_PIN, DIGIT_2_PIN, DIGIT_3_PIN, DIGIT_4_PIN};
  byte segmentPins[] = {SEGMENT_A_PIN, SEGMENT_B_PIN, SEGMENT_C_PIN, SEGMENT_D_PIN, SEGMENT_E_PIN,
    SEGMENT_F_PIN, SEGMENT_G_PIN, DECIMAL_POINT_PIN};
  bool resistorsOnSegments = false;
  byte hardwareConfig = COMMON_CATHODE;
  bool updateWithDelays = false;
  bool leadingZeros = false;
  bool disableDecPoint = false;

  sevseg.begin(hardwareConfig, numDigits, digitPins, segmentPins, resistorsOnSegments,
    updateWithDelays, leadingZeros, disableDecPoint);
  sevseg.setBrightness(100);
}

void loop() {
  // Gets new readings
  if (!lastReading || millis() - lastReading > SLEEP_INTERVAL) {
    getNewSamples();
    lastReading = millis();
  }

  float distance = getDistance();

  if (distance < DIST_THRESHOLD || (millis() - displayTimeStart) < TEMPERATURE_DISPLAY_DURATION) {
    // Should display the average temperature
    sevseg.setNumber(averageTemp, 1);
    sevseg.refreshDisplay();
  } else {
    sevseg.blank();
    sevseg.refreshDisplay();
  }

}

/* ===== FUNCTION IMPLEMENTATIONS ===== */
void getNewSamples() {
  float lastTemperature = getNewTemp();

  if (lastTemperature >= HIGH_TEMP) {
    // Turns red led on
    digitalWrite(HIGH_TEMP_LED_PIN, HIGH);
    sendEmail(CASE_HIGH_TEMP, 0);
    //todo send email
  } else {
    // Turns red led off
    digitalWrite(HIGH_TEMP_LED_PIN, LOW);
  }
  if (lastTemperature < LOW_TEMP){
    // Turns blue led on
    digitalWrite(LOW_TEMP_LED_PIN, HIGH);
    sendEmail(CASE_LOW_TEMP, 0);
    //todo send email
  } else {
    // Turns blue led off
    digitalWrite(LOW_TEMP_LED_PIN, LOW);
  }
  if (lastTemperature > HIGH_TEMP_RELAY){
    // Turns relay on
    // this could start a fan
    digitalWrite(RELAY_PIN, HIGH);
    Serial.print(F("Warning: extremely high temperature. "));
    Serial.println(F("Fan activated."));
  } else {
    // Turns relay off
    // this could cause the fan to stop working
    digitalWrite(RELAY_PIN, LOW);
  }

  if (temperatureReadingsCounter == BUFFER_SIZE) {
    // Two minutes have passed by. At this point the desired number of readings has been reached.
    // Calculates the new average temperature
    averageTemp = calcAverageTempAndReset();
    sendEmail(CASE_AVERAGE_TEMP, averageTemp);

    // Updates the starting time for the display
    displayTimeStart = millis();
  }
}

float getNewTemp(){
  // Reads temperature as Celsius
  float temperature = dht.readTemperature();

  // Checks if the read failed and handles the failure
  if (isnan(temperature)) {
    Serial.print(F("Failed to read from DHT sensor!"));
    return -1;
  }

  // Shifts all previous readings to the right and puts the new one at the beginning of the table
  for (int i=BUFFER_SIZE-1; i>0; --i) {
    temperatures[i] = temperatures[i-1];
  }
  temperatures[0] = temperature;

  // Updates the readings counter
  ++temperatureReadingsCounter;

  return temperature;
}

float calcAverageTempAndReset() {
  float sum = 0;
  for (int i = 0; i<BUFFER_SIZE; i++){
    sum += temperatures[i];
  }

  // Resets the readings counter
  temperatureReadingsCounter = 0;

  // TODO send email -> emailAverageTemp(averageTemp)

  return sum/BUFFER_SIZE;
}

float getDistance() {
  // The sensor is triggered by a HIGH pulse of 10 or more microseconds.
  // Gives a short LOW pulse beforehand to ensure a clean HIGH pulse:
  digitalWrite(DIST_TRIG_PIN, LOW);
  delayMicroseconds(5);
  digitalWrite(DIST_TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(DIST_TRIG_PIN, LOW);

  // Reads the signal from the sensor: a HIGH pulse whose
  // duration is the time (in microseconds) from the sending
  // of the ping to the reception of its echo off of an object.
  pinMode(DIST_ECHO_PIN, INPUT);
  unsigned long duration = pulseIn(DIST_ECHO_PIN, HIGH);

  // Converts the time into a distance
  float distance = (duration/2) / 29.1;
  return distance;
}

void sendEmail(int _case, float averageTemp) {
  if (_case == CASE_HIGH_TEMP){
    Serial.print("EMAIL : The temperature is above ");
    Serial.print(HIGH_TEMP);
    Serial.println("°C ");
  } else if (_case == CASE_LOW_TEMP){
    Serial.print("EMAIL : The temperature is below ");
    Serial.print(LOW_TEMP);
    Serial.println("°C ");
  } else if (_case == CASE_AVERAGE_TEMP){
    Serial.print("EMAIL : The average temperature is ");
    Serial.print(averageTemp);
    Serial.println("°C ");
  }
}