/*!
 * \file
 *    main.c
 * \brief
 *    Main application file
 *
 * Author: Christos Choutouridis AEM: 8997
 * email : <cchoutou@ece.auth.gr>
 */
#include <hal.h>
#include "thermostat.h"
#include "console.h"

/*
 * Zero initialized globals
 */
app_data_t     app_data;
state_en       state;

// Value initialized globals
settings_t     _Init_settings(settings);

/*
 * helper api
 */
float_t temp_get_current (void) {
   return (app_data.cur) ? app_data.T[app_data.cur-1] : app_data.T[MEASUREMENTS-1];
}

/*!
 * Control functionality
 * Handles:
 *    * Sensor reading
 *    * Temp averaging
 *    * Output calculation
 * \note    Non-blocking
 */
void control (void) {
   static time_t sample =0;
   time_t now = time(0);   // get time

   // Proximity
   if (proximity(&prox) < settings.Pset)
      app_data.flag_proximity = 1;
   else if (proximity(&prox) < settings.Pset + settings.Physt)
      ;
   else
      app_data.flag_proximity = 0;

   // Temperature calculation
   if (now - sample >= MEAS_INTERVAL) {
      sample = now;
      app_data.T[app_data.cur] = temp_read();
      if (++app_data.cur >= MEASUREMENTS) {
         app_data.cur =0;
         float_t f =0;
         for (int i=0 ; i<MEASUREMENTS ; ++i) {
            f += app_data.T[i];
         }
         app_data.Tav = f / MEASUREMENTS;
         app_data.signal_cycle = 1;
         app_data.flag_init = 0;
      }
   }

   // Output handling
   if (!app_data.flag_init) {
      switch (settings.mode) {
         case HEATING: {
            if (app_data.Tav <= settings.Tset)
               app_data.flag_output = 1;
            else if (app_data.Tav < settings.Tset + settings.Thyst)
               ;
            else
               app_data.flag_output = 0;
         }
         break;

         case COOLING: {
            if (app_data.Tav <= settings.Tset - settings.Thyst)
               app_data.flag_output = 0;
            else if (app_data.Tav < settings.Tset)
               ;
            else
               app_data.flag_output = 1;
         }
         break;
      }
   }
}

/*!
 * Display functionality
 * Implement a state machine for lcd display
 * \note    Non-blocking
 */
void display (void) {
   static time_t mark =0;

   char line1[18];
   char line2[18];
   time_t now = time(0);

   switch (state) {
      case ST_INIT:
         sprintf(line1, "\fInitializing %c  ", (time(0)%2) ? '*' : ' ');
         sprintf(line2, "\n                ");

         // escapes
         if (app_data.flag_proximity)
            state = ST_USER;
         else if (app_data.signal_cycle) {
            app_data.signal_cycle =0;
            mark = now;
            state = ST_AVERAGE;
         }
      break;

      case ST_COUNT:
         sprintf(line1, "\fMeasuring %c     ", (time(0)%2) ? '*' : ' ');
         sprintf(line2, "\n%s: %s    ",
               (settings.mode == HEATING) ? "Heating" : "Cooling",
               (app_data.flag_output) ? "On " : "Off"
         );

         // escapes
         if (app_data.flag_proximity)
            state = ST_USER;
         else if (app_data.signal_cycle) {
            app_data.signal_cycle =0;
            mark = now;
            state = ST_AVERAGE;
         }
      break;

      case ST_AVERAGE:
         sprintf(line1, "\fTav=%4.1f        ", app_data.Tav);
         sprintf(line2, "\n%s: %s    ",
               (settings.mode == HEATING) ? "Heating" : "Cooling",
               (app_data.flag_output) ? "On " : "Off"
         );

         // escapes
         if (app_data.flag_proximity)
            state = ST_USER;
         else if (now - mark >= UI_AVERAGE_TIME)
            state = ST_COUNT;
      break;

      case ST_USER:
         sprintf(line1, "\fTav=%4.1f  T=%4.1f", app_data.Tav, temp_get_current());
         sprintf(line2, "\n%s: %s    ",
               (settings.mode == HEATING) ? "Heating" : "Cooling",
               (app_data.flag_output) ? "On " : "Off"
         );

         // escapes
         if (!app_data.flag_proximity)
            state = ST_COUNT;
      break;
   }

   lcd_puts(line1);
   lcd_puts(line2);
}

/*!
 * Set outputs
 * \note    Non-blocking
 */
void outputs (void) {
   static uint8_t pr_output =0;

   if (app_data.flag_init)
      return;
   led_green(app_data.flag_output && settings.mode == COOLING);
   led_red (app_data.flag_output && settings.mode == HEATING);

   if (app_data.flag_output != pr_output) {
      relay(app_data.flag_output);
      pr_output = app_data.flag_output;
   }

}

/*
 * Main loop
 */
int main(void) {

   hal_hw_init ();
   lcd_init ();
   proximity_init(&prox);
   temp_init (TEMP_11_BIT);

   lcd_enable(1);
   app_data.flag_init = 1;

   while (1) {
      control ();
      display ();
      outputs();
      console ();
      HAL_Delay(10);
   }
}
