#include <Arduino.h>
#include <MQTT.h>
#include <SPI.h>
#include <Ticker.h>
#include <SpiShiftRegisterChain.h>
#include <Adafruit_NeoPixel.h>
#include <glblData.h>
#define NUM_PIXELS 12
const int latchPin = 4;
const int dataLengthBytes = 3;
SpiShiftRegisterChain ios(latchPin, dataLengthBytes);
Adafruit_NeoPixel pixels(NUM_PIXELS, 12, NEO_GRB + NEO_KHZ800);
Ticker valveTiming;
Ticker closeValves;
MQTT connections;
bool init_done = false;
void updateTimerRing(void);

void valveState(void){
  static uint8_t v_powered = MOTOR_POWER_TIME;
  static uint8_t slowdown = 0;
  slowdown++;
  for(uint8_t i=1;i<NUM_VALVES;i++){
    if(glblData.timers[i]){
      if(slowdown%2 == 0){
        glblData.timers[i]--;
        if(!glblData.timers[i]){
          glblData.setValveState[i] = CLOSE;
        }else{
          glblData.timerChange = true;
        }
      }
    }
    switch(glblData.valveState[i]){
      case VALVE_CLOSED:
        if(glblData.setValveState[i]){
          MQTT::remoteLog("Open Valve ",i);
          glblData.valveState[i] = VALVE_OPENING_COILPWR;
          glblData.statusChange = true;
          SET_V(i);
        }
        break;
      case VALVE_OPENING_COILPWR:
        glblData.valveState[i] = VALVE_OPENING_POWER;
        glblData.statusChange = true;
        KEEP_V(i);
        break;
      case VALVE_OPEN:
        if(!glblData.setValveState[i]){
          MQTT::remoteLog("Close Valve ",i);
          glblData.valveState[i] = VALVE_CLOSING_COILPWR;
          glblData.statusChange = true;
          glblData.timers[i] = 0;
          RST_V(i);
        }
        break;
      case VALVE_CLOSING_COILPWR:
        glblData.valveState[i] = VALVE_CLOSING_POWER;
        glblData.statusChange = true;
        KEEP_V(i);
        break;
      case VALVE_OPENING_POWER:
        glblData.valveState[i] = VALVE_OPENING_POWER_COILPWR;
        glblData.statusChange = true;
        SET_V(0);
        break;
      case VALVE_OPENING_POWER_COILPWR:
        glblData.valveState[i] = VALVE_OPEN_MOTORIZED;
        glblData.statusChange = true;
        v_powered = 0;
        KEEP_V(0);
        break;
      case VALVE_CLOSING_POWER:
        glblData.valveState[i] = VALVE_CLOSING_POWER_COILPWR;
        glblData.statusChange = true;
        SET_V(0);
        break;
      case VALVE_CLOSING_POWER_COILPWR:
        glblData.valveState[i] = VALVE_CLOSED_MOTORIZED;
        glblData.statusChange = true;
        v_powered = 0;
        KEEP_V(0);
        break;
      case VALVE_OPEN_MOTORIZED:
        if(v_powered >= MOTOR_POWER_TIME){
          glblData.valveState[i] = VALVE_OPEN_COILPWR;
          glblData.statusChange = true;
          RST_V(0);
        }
        break;
      case VALVE_OPEN_COILPWR:
        glblData.valveState[i] = VALVE_OPEN;
        glblData.statusChange = true;
        KEEP_V(0);
        break;
      case VALVE_CLOSED_MOTORIZED:
        if(v_powered >= MOTOR_POWER_TIME){
          glblData.valveState[i] = VALVE_CLOSED_COILPWR;
          glblData.statusChange = true;
          RST_V(0);
        }
        break;
      case VALVE_CLOSED_COILPWR:
        glblData.valveState[i] = VALVE_CLOSED;
        glblData.statusChange = true;
        KEEP_V(0);
        break;
    }
  }
  if(v_powered < MOTOR_POWER_TIME){
    v_powered++;
  }
}

void closeValves_tick(uint8_t state){
  switch(state){
    case 0:
      ios.setDataToZeros();
      ios.writeData();
      for(uint8_t i=V1_RES;i<V1_RES+8;i++){
        ios.setBitOn(i);
        ios.setBitOn(i+8);
      }
      ios.writeData();
      closeValves.once(0.5,closeValves_tick,(uint8_t)1);  //plan next step
      break;
    case 1:
      for(uint8_t i=V1_RES;i<V1_RES+8;i++){
        ios.setBitOff(i);
        ios.setBitOff(i+8);
      }
      ios.writeData();
      SET_V(0);
      closeValves.once(0.5,closeValves_tick,(uint8_t)2);  //plan next step
      break;
    case 2:
      KEEP_V(0);
      closeValves.once(20,closeValves_tick,(uint8_t)3);  //plan next step
      break;
    case 3:
      RST_V(0);
      closeValves.once(0.5,closeValves_tick,(uint8_t)4);
      break;
    case 4:
      KEEP_V(0);
      init_done = true;
      valveTiming.attach_ms(500,valveState);
      break; 
  }
}

void init_relays(){
  closeValves.once(0.2,closeValves_tick,(uint8_t)0);
}

void setup() {
  pixels.clear();
  pixels.show();
  Serial.begin(115200);
  Serial.println("Hello");
  connections.begin();
 // statusLabelId = ESPUI.label("Status:", ControlColor::Turquoise, "Stop");
 // millisLabelId = ESPUI.label("Millis:", ControlColor::Emerald, "0");
 // ESPUI.begin(connections.getHostname());
  SPI.begin();
  //pixels.setPixelColor(0,pixels.Color(200,100,0));
  //pixels.show();

  init_relays();
}

static long oldTime = 0;
unsigned long lastTick = millis();
void loop() {
  connections.loop();

  if (millis() - oldTime > 100){
        updateTimerRing();
        oldTime += 100; //Do not allow skipping or time shifts here.
    }
  // put your main code here, to run repeatedly:
}




void updateTimerRing(void){
  static uint8_t oldsec = 0;
  static unsigned long msecs = 0;
  static uint8_t mins=30,secs=0, hours=1;
  unsigned long now = millis();
  static uint8_t slowdown = 0;
/*
  if(slowdown == 10){
    if(secs==0){
      secs = 59;
      if(mins==0){
        hours--;
        mins = 59;
      }else{
        mins--;
      }
    }else{
      secs--;
    }
    slowdown = 0;
  }else{
    slowdown ++;
  } */
  pixels.clear();
  if(glblData.timers[1] == 0 && glblData.timers[6] == 0){
    pixels.show();
    return;
  }
  uint32_t rest = glblData.timers[1];
  if(glblData.timers[6]>glblData.timers[1]){
    rest = glblData.timers[6];
  }
  hours = rest/3600;
  rest -= hours * 3600;
  mins = rest/60;
  rest -= mins * 60;
  secs = rest;

  if(secs != oldsec){
    oldsec = secs;
    msecs = now;
  }
  uint8_t LED = secs/(60/NUM_PIXELS);
  uint8_t LEDfraction = (secs%(60/NUM_PIXELS))*10 + (10-(now - msecs)/100);
  if(LED > 0 || mins){
    pixels.setPixelColor((LED+8)%NUM_PIXELS,2*(50-LEDfraction),4*(50-LEDfraction),0);
  }
  pixels.setPixelColor((LED+9)%NUM_PIXELS,2*(LEDfraction),4*(LEDfraction),0);

  LED = mins/(60/NUM_PIXELS);
  LEDfraction = mins%(60/NUM_PIXELS);

  for(uint8_t i=0;i<LED;i++){
    pixels.setPixelColor((i+8)%NUM_PIXELS,Adafruit_NeoPixel::Color(0,0,150) | pixels.getPixelColor((i+8)%NUM_PIXELS));
  }
  pixels.setPixelColor((LED+8)%NUM_PIXELS,Adafruit_NeoPixel::Color(0,0,30*(LEDfraction)) | pixels.getPixelColor((LED+8)%NUM_PIXELS));

  LED = hours;
  for(uint8_t i=0;i<LED;i++){
    pixels.setPixelColor((i+8)%NUM_PIXELS,pixels.getPixelColor(i+8) | Adafruit_NeoPixel::Color(125,0,0));
  }
  for(uint8_t i=0;i<NUM_PIXELS;i++){  //gamma correct after blending colors
    pixels.setPixelColor(i,pixels.gamma32(pixels.getPixelColor(i)));
  }
  pixels.show();
}