#include "RTClib.h"
#include <OneWire.h>
#include <DallasTemperature.h>
#include "chauffe_eau.h"

RTC_DS1307 rtc;

// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(pinOneWire);

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

byte mode_en_cours = MODE_CHAUFFAGE_OFF;

void mode_management(byte mode_demande)
{
  // si le mode demandé est déjà le mode en cours on ne fait rien
  if (mode_demande == mode_en_cours) {
    return;
  }

  Serial.print("mode demande: ");
  Serial.println(nom_mode(mode_demande));
  Serial.print("mode_en_cours: ");
  Serial.println(nom_mode(mode_en_cours));

  if (mode_demande == MODE_CHAUFFAGE_PANNEAU) {

    if (mode_en_cours == MODE_CHAUFFAGE_CHAUDIERE) {
      digitalWrite(pinChaudiere, LOW);
    } else if (mode_en_cours == MODE_CHAUFFAGE_RESISTANCE) {
      digitalWrite(pinResistance, LOW);
    }
    digitalWrite(pinRelayPump, HIGH);
    mode_en_cours = MODE_CHAUFFAGE_PANNEAU;    

  } else if (mode_demande == MODE_CHAUFFAGE_CHAUDIERE) {

    if (mode_en_cours == MODE_CHAUFFAGE_PANNEAU) {
      digitalWrite(pinRelayPump, LOW);
    } else if (mode_en_cours == MODE_CHAUFFAGE_RESISTANCE) {
      digitalWrite(pinResistance, LOW);
    }
    digitalWrite(pinChaudiere, HIGH);
    mode_en_cours = MODE_CHAUFFAGE_CHAUDIERE;    

  } else if (mode_demande == MODE_CHAUFFAGE_RESISTANCE) {

    if (mode_en_cours == MODE_CHAUFFAGE_PANNEAU) {
      digitalWrite(pinRelayPump, LOW);
    } else if (mode_en_cours == MODE_CHAUFFAGE_CHAUDIERE) {
      digitalWrite(pinChaudiere, LOW);
    }

    digitalWrite(pinResistance, HIGH);
    mode_en_cours = MODE_CHAUFFAGE_RESISTANCE;    

  } else if (mode_demande == MODE_CHAUFFAGE_OFF) {
    if (mode_en_cours == MODE_CHAUFFAGE_PANNEAU) {
      digitalWrite(pinRelayPump, LOW);
    } else if (mode_en_cours == MODE_CHAUFFAGE_RESISTANCE) {
      digitalWrite(pinResistance, LOW);
    } else if (mode_en_cours == MODE_CHAUFFAGE_CHAUDIERE) {
      digitalWrite(pinChaudiere, LOW);
    }
    mode_en_cours = MODE_CHAUFFAGE_OFF;    
  }

  Serial.print("mode affecté: ");
  Serial.println(nom_mode(mode_en_cours));

  return;
}

byte get_mode(void)
{
  return mode_en_cours;
}

void affichage_addresse_capteur(DeviceAddress deviceAddress) {
  for (uint8_t i = 0; i < 8; i++) {
    if (deviceAddress[i] < 16) Serial.print("0");
      Serial.print(deviceAddress[i], HEX);
  }
}

char* nom_mode(byte mode_demande)
{
  if (mode_demande == MODE_CHAUFFAGE_OFF)
    return "MODE_CHAUFFAGE_OFF ";
  else if (mode_demande == MODE_CHAUFFAGE_PANNEAU)
    return "MODE_CHAUFFAGE_PANNEAU ";
  else if (mode_demande == MODE_CHAUFFAGE_RESISTANCE)
    return "MODE_CHAUFFAGE_RESISTANCE ";
  else if (mode_demande == MODE_CHAUFFAGE_CHAUDIERE)
    return "MODE_CHAUFFAGE_CHAUDIERE ";
  else
    return "MODE_CHAUFFAGE_INCONNU ";
}

char* nom_du_capteur(byte capteur_id)
{
  if (capteur_id == CAPTEUR_BAS_DE_CUVE_ID)
    return "CAPTEUR_BAS_DE_CUVE ";
  else if (capteur_id == CAPTEUR_BASE_CHAUDIERE_ID)
    return "CAPTEUR_BASE_CHAUDIERE ";
  else if (capteur_id == CAPTEUR_HAUT_DE_CUVE_ID)
    return "CAPTEUR_HAUT_DE_CUVE ";
  else if (capteur_id == CAPTEUR_PANNEAU_ID)
    return "CAPTEUR_PANNEAU ";
  else
    return "CAPTEUR_INCONNU ";
}

float lecture_temperature(byte capteur_id)
{
  float temp;

  if (sensors.requestTemperaturesByAddress(addresseCapteurTemperature[capteur_id]) == false) {
    Serial.print("echec requestTemperaturesByAddress: ");
    Serial.println(nom_du_capteur(capteur_id));
    affichage_addresse_capteur(addresseCapteurTemperature[capteur_id]);
    Serial.println();
    error("echec requestTemperaturesByAddress");
  }

  temp = sensors.getTempC(addresseCapteurTemperature[capteur_id]);
  if (temp == DEVICE_DISCONNECTED_C) {

    Serial.print("echec lecture temperature du capteur: ");
    Serial.print(nom_du_capteur(capteur_id));
    Serial.print(" code erreur: ");
    Serial.println(temp);
    affichage_addresse_capteur(addresseCapteurTemperature[capteur_id]);
    Serial.println();
    error("echec lecture temperature du capteur");

  } else {
    Serial.print("lecture temperature du capteur: ");
    Serial.print(nom_du_capteur(capteur_id));
    Serial.print(temp);
    Serial.println("°C");
    return temp;
  }
}

void setup() 
{  
  byte numCapteursTemp = 0;
  // description Entrée/sortie
  pinMode(pinInterrupteur,  INPUT_PULLUP);
  pinMode(pinRelayPump,     OUTPUT);
  pinMode(pinChaudiere,     OUTPUT);
  pinMode(pinResistance,    OUTPUT);
  pinMode(pinLedCarte,      OUTPUT);

  // état initial des entrées sorties
  digitalWrite(pinRelayPump,  LOW);
  digitalWrite(pinChaudiere,  LOW);
  digitalWrite(pinResistance, LOW);
  digitalWrite(pinLedCarte,   LOW);

  // liaison serie
  Serial.begin(115200);

  // capteurs de temperatures
  sensors.begin();

  // verification qu'il y a 4 capteurs
  numCapteursTemp = sensors.getDeviceCount();

  if(numCapteursTemp != NB_CAPTEURS_DE_TEMPERATURE) {
    Serial.print("nombre de Capteurs de Temp incorrects ");
    Serial.println(numCapteursTemp);
    error("nombre de Capteurs de Temp incorrects");
  }

  // lecture des 4 capteurs et vérification qu'ils sont correctement connectés
  for (byte capteur_id = CAPTEUR_BAS_DE_CUVE_ID; capteur_id < NB_CAPTEURS_DE_TEMPERATURE; capteur_id++) {
    lecture_temperature(capteur_id);
  }
   // démarage de la RTC
  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    error("Couldn't find RTC");
  }
}

void loop() 
{
  // lecture de l'heure
  DateTime now = rtc.now();
  uint8_t heure = now.hour();
  Serial.println(heure, DEC);

  // lecture de l'interrupteur été/hiver
  if (digitalRead(pinInterrupteur) == LOW) {
    //été
    // sommes nous la journée?
    if ((heure >= 8) && (heure <= 19)) {
      // jour
      // doit on chauffer?
      float temperature_bas_de_cuve = lecture_temperature(CAPTEUR_BAS_DE_CUVE_ID);     
      if (temperature_bas_de_cuve < T_BAS_DE_CUVE_MAX) {
        // on doit chauffer
        // peut on chauffer?
        // température panneau
        float temperature_panneau = lecture_temperature(CAPTEUR_PANNEAU_ID);
        
        float delta;
        if (get_mode() == MODE_CHAUFFAGE_PANNEAU) {
          // si la pompe est en route
          // le delta de temperature est diminué pour ne pas
          // effectuer un arret trop tôt
          delta = DELTA_PANNEAU_CUVE_POMPE_ON;
        }
        else { 
          // si la pompe est à l'arret
          // on augmente le delta de temperature pour ne pas
          // effectuer un redemarrage trop tôt         
          delta = DELTA_PANNEAU_CUVE_POMPE_OFF;
        }
        Serial.print("delta: ");
        Serial.println(delta);

        if (temperature_panneau > (temperature_bas_de_cuve + delta)) {
          // on peut chauffer
          mode_management(MODE_CHAUFFAGE_PANNEAU);
        } else {
          // on ne peux pas chauffer
          mode_management(MODE_CHAUFFAGE_OFF);
        }
      } else {
        // on ne doit pas chauffer
        mode_management(MODE_CHAUFFAGE_OFF);
      }
    } else {
      // nuit
      // doit on chauffer?
      float temperature_haut_de_cuve = lecture_temperature(CAPTEUR_HAUT_DE_CUVE_ID);
      if (temperature_haut_de_cuve < T_HAUT_DE_CUVE_MAX) {
        // on doit chauffer
        mode_management(MODE_CHAUFFAGE_RESISTANCE);
      } else {
        // on ne doit pas chauffer
        mode_management(MODE_CHAUFFAGE_OFF);
      }
    }
  } else {
    // hiver
    // sommes nous la nuit?
    if ((heure < 9) || (heure > 17)) {
      // doit on chauffer?
      float temperature_base_chaudiere = lecture_temperature(CAPTEUR_BASE_CHAUDIERE_ID);
      if (temperature_base_chaudiere < T_BASE_CHAUDIERE_MAX) {
        // on doit chauffer
        mode_management(MODE_CHAUFFAGE_CHAUDIERE);
      } else {
        // on ne doit pas chauffer
        mode_management(MODE_CHAUFFAGE_OFF);
      }
    } else {
        // on ne doit pas chauffer la journée
        mode_management(MODE_CHAUFFAGE_OFF);
      }
  }

  // attente d'1s
  delay(1000);

}

void error(char* err) {
  static bool led_error = 1;
  
  if (error != NULL)
    Serial.println(err);
  Serial.flush();

  // en cas d'erreur la led Rouge L de la carte clignote toute les 500ms
  while(true) {
    led_error ^= 1;
    if (led_error)
      digitalWrite(pinLedCarte, LOW);
    else
      digitalWrite(pinLedCarte, HIGH);
    delay(500);
  }
}
GND5VSDASCLSQWRTCDS1307+