#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <DallasTemperature.h>
#include <OneWire.h>
#include <Adafruit_NeoPixel.h>
#include "ColorUtil.h"
#include "controller.h"
#include "SerialCmd.h"

/******************************************************************************
 *                                     MACROS
 ******************************************************************************/


/******************************************************************************
 *                                     DEFINES
 ******************************************************************************/
#define FAN_ENABLED

#define DELAY_TIME            1000 /**< time between measurements [ms] */
#define MIN_FAN_SPEED_PERCENT 24 /**< minimum fan speed [%] */
#define MIN_TEMP              25 /**< turn fan off below [deg C] */
#define MAX_TEMP              40 /**< turn fan to full speed above [deg C] */
#define INVALID_TEMP          (MAX_TEMP+MAX_TEMP) /**< Invalid temperature (on sensor failures)*/

#define TEMP_SENSOR_MEASURE_SERIES  5 /**< Maximum resets */
#define FAN_RPM_READLEVEL     100

/******************************************************************************
 *                            LOCAL VARIABLES
 ******************************************************************************/
Adafruit_NeoPixel*  pixels;
Adafruit_NeoPixel*  singleLed;

OneWire oneWire(GPIO_DS18B20);
DallasTemperature sensors(&oneWire);

#ifdef FAN_ENABLED
/**
 * @brief Get the Fan Speed in round per minutes
 * 
 * @return int 
 */
int getFanSpeedRpm() {
  int highTime = pulseIn(SIGNAL_PIN, HIGH);
  int lowTime = pulseIn(SIGNAL_PIN, LOW);
  int period = highTime + lowTime;
  if (period == 0) {
    return 0;
  }
  float freq = 1000000.0 / (float)period;
  return (freq * 60.0) / 2.0; // two cycles per revolution
}

/**
 * @brief Set the Fan Speed
 * 
 * @param p expected percentage
 */
void setFanSpeedPercent(int p) {
  int value = (p / 100.0) * 255;
  analogWrite(FAN_PIN, value);
}
#else
  uint8_t mRainbowIndex;
#endif

/**
 * @brief Called once at start
 * Initialize hardware pins
 */
void setup()
{
  pixels = new Adafruit_NeoPixel(WS2812STRIP_LEDS, WS2812STRIP_GPIO_PIN, NEO_GRB + NEO_KHZ400);
  singleLed = new Adafruit_NeoPixel(WS2812SINGLE_LEDS, WS2812SINGLE_GPIO_PIN, NEO_GRB + NEO_KHZ400);

  Serial.begin(115200);
  WiFi.mode(WIFI_OFF);
  if (WiFi.getMode() == WIFI_OFF)
  {
      Serial.println(F("\nWifi mode is WIFI_OFF"));
  }

  SerialCmd_clearCmdArray();

#ifdef FAN_ENABLED
  /* Setup FAN Control */
  pinMode(FAN_PIN, OUTPUT);
  pinMode(SIGNAL_PIN, INPUT);
#else
  Serial.println(F("\nFAN NOT enabled"));
  mRainbowIndex = 0;
#endif

  pixels->begin();
  pixels->clear();
  pixels->setBrightness(100);
  pixels->fill(Color(255,0,0));
  pixels->show();


/* Initialize Temperature sensor */
  sensors.begin();
  for(int j=0; j < TEMP_SENSOR_MEASURE_SERIES && sensors.getDeviceCount() == 0; j++)
  {
    delay(100);
    sensors.begin();
    Serial.println("Reset 1-Wire Bus");
  }
  
  pixels->fill(Color(0,255,0));
  pixels->setBrightness(50);
  pixels->show();
  /* prepare LED strip pin */
  singleLed->begin();
  singleLed->clear();
  singleLed->setBrightness(100);
  singleLed->fill(Color(255,0,0));
  singleLed->show();

}

float readTemperature(void)
{
  int sensorCount = sensors.getDeviceCount();
  if (sensorCount > 0)
  {
    sensors.requestTemperatures();
    float temp1Raw = sensors.getTempCByIndex(0);
    if (temp1Raw > 0)
    {
      return temp1Raw;
    }
  }

  return INVALID_TEMP;
}

/**
 * @brief Endless loop
 * (is called as fast as possible)
 */
void loop()
{
  static int i = 0;
  static int actualFanSpeedRpm = -1;
  float temp = readTemperature();

  Serial.print(temp);
  Serial.print(";");

  int fanSpeedPercent;

  if (temp < MIN_TEMP) {
    fanSpeedPercent = 0;
  } else if (temp > MAX_TEMP) {
    fanSpeedPercent = 100;
  } else {
    fanSpeedPercent = (100 - MIN_FAN_SPEED_PERCENT) * (temp - MIN_TEMP) / (MAX_TEMP - MIN_TEMP) + MIN_FAN_SPEED_PERCENT;
  }

#ifdef FAN_ENABLED
  if (actualFanSpeedRpm == 0)
  {
    if (i > FAN_RPM_READLEVEL)
    {
      actualFanSpeedRpm = getFanSpeedRpm();
      i = 0;
    }
    i++;
  }
  else
  {
    actualFanSpeedRpm = getFanSpeedRpm();
    i = 0;
  }

  int readCharacter = SerialCmd_readFromSerial();

  if (readCharacter > 0)
  {
    int checkCmd = SerialCmd_checkCmdArrayForPrefix();
    
    if(checkCmd == 0)
    {
       Serial.println("if you dont know what to do type \"ollpehelp\"");
    }
    else
    {
      char type = SerialCmd_type();
      switch (type)
      {
      case 'a':
        uint32_t color = SerialCmd_parseColor(6);
        pixels->fill(color);
        pixels->setBrightness(200);
        break;
      }
    }
  }
  pixels->show();

#else
  RainbowCycle(pixels, &mRainbowIndex);
  Serial.print(mRainbowIndex);
#endif


#ifdef FAN_ENABLED
  Serial.print(fanSpeedPercent);
  Serial.print("%;");
  setFanSpeedPercent(fanSpeedPercent);

  Serial.print(actualFanSpeedRpm);
  Serial.println("RPM");
#else
  Serial.println("");
#endif

  singleLed->fill(Color(0,128,128));
  singleLed->setBrightness(fanSpeedPercent);
  singleLed->show();
}