//#include <Arduino.h>
   
//#include <SoftwareWire.h>  // must be included here so that Arduino library object file references work
//#include <RtcDS3231.h>


#include "clockelements.hpp"

//SoftwareWire myWire(A4, A5);
//RtcDS3231<SoftwareWire> Rtc(myWire);

const unsigned int PIN_BUZZER = 9;
const uint8_t PIN_PIXELS = D3; //6;

const uint16_t NUM_RING_PIXELS = 180U;
const uint8_t NUM_SECONDS = 60U;
const uint16_t NUM_RING_SEC_PIXELS = NUM_RING_PIXELS/NUM_SECONDS;
const uint8_t NUM_MINUTES = 60U;
const uint16_t NUM_RING_MIN_PIXELS = 1;
const uint16_t NUM_POINTER_PIXELS = 6U;
const uint16_t NUM_POINTERS = 12U;
const uint16_t NUM_POINTERS_PIXELS = NUM_POINTER_PIXELS * NUM_POINTERS;
const int16_t NUM_NUMBERS = 12U;
const uint16_t NUM_NUMBER_PIXELS[NUM_NUMBERS] = {2,4,4,4,4,4,4,4,4,4,4,5};
const uint16_t NUM_NUMBERS_PIXELS = NUM_NUMBER_PIXELS[0] + NUM_NUMBER_PIXELS[1] + NUM_NUMBER_PIXELS[2] + NUM_NUMBER_PIXELS[3] + NUM_NUMBER_PIXELS[4] + NUM_NUMBER_PIXELS[5] + NUM_NUMBER_PIXELS[6] + NUM_NUMBER_PIXELS[7] + NUM_NUMBER_PIXELS[8] + NUM_NUMBER_PIXELS[9] + NUM_NUMBER_PIXELS[10] + NUM_NUMBER_PIXELS[11];
const uint16_t NUM_ALL_PIXELS = NUM_RING_PIXELS + NUM_POINTERS_PIXELS + NUM_NUMBERS_PIXELS;

CRGB * pixels;

//Adafruit_NeoPixel pixels(NUM_ALL_PIXELS, PIN_PIXELS, NEO_GRB + NEO_KHZ800);

elements number[NUM_NUMBERS];
elements pointer[NUM_POINTERS];
elements ringsecs[NUM_SECONDS];
elements ringmins[NUM_MINUTES];

const hue_color COLOR_SECS          = {HUE_GREEN,255,128};    // green
const hue_color COLOR_HOURS         = {HUE_RED,255,128};          // red
const hue_color COLOR_MINS          = {HUE_BLUE,255,128};  // blue
const hue_color COLOR_NUMS          = {0,0,255};            // white
const hue_color color[] = {COLOR_SECS, COLOR_MINS, COLOR_HOURS, COLOR_NUMS};
enum color_type {COL_SECOND = 0, COL_MINUTE, COL_HOUR, COL_NUM, COL_LAST};

const int TICK_FREQ = 40;
const int TICK_DLY = 13;
const int TOCK_FREQ = 15;
const int TOCK_DLY = 35;
bool tick_not_tock = true;

/*
void init_rtc()
{
    Rtc.Begin();

    RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
    Serial.println();

    if (!Rtc.IsDateTimeValid()) 
    {
        if (Rtc.LastError() != 0)
        {
            Serial.print("RTC communications error = ");
            Serial.println(Rtc.LastError());
        }
        else
        {
            Serial.println("RTC lost confidence in the DateTime!");
            Rtc.SetDateTime(compiled);
        }
    }

    if (!Rtc.GetIsRunning())
    {
        Serial.println("RTC was not actively running, starting now");
        Rtc.SetIsRunning(true);
    }

    RtcDateTime now = Rtc.GetDateTime();
    if (now < compiled) 
    {
        Serial.println("RTC is older than compile time!  (Updating DateTime)");
        Rtc.SetDateTime(compiled);
    }
    else if (now > compiled) 
    {
        Serial.println("RTC is newer than compile time. (this is expected)");
    }
    else if (now == compiled) 
    {
        Serial.println("RTC is the same as compile time! (not expected but all is fine)");
    }

    Rtc.Enable32kHzPin(false);
    Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeNone); 
}
*/

void init_ringmins() {
  // init all the seconds shown on the ring
  Serial.println("Init minutes on outer ring ...");
  unsigned int current_first_led = 1;
  for(uint8_t num = 0; num < NUM_MINUTES; num++)
  {
    ringmins[num].init(pixels, current_first_led, NUM_RING_MIN_PIXELS, color[COL_MINUTE]);
    ringmins[num].clean();
    //ringmins[num].test(true);
    current_first_led += NUM_RING_PIXELS/NUM_MINUTES;
  }
}

void init_ringsecs() {
  // init all the seconds shown on the ring
  Serial.println("Init seconds on outer ring ...");
  unsigned int current_first_led = 0;
  for(uint8_t num = 0; num < NUM_SECONDS; num++)
  {
    ringsecs[num].init(pixels, current_first_led, NUM_RING_SEC_PIXELS, color[COL_SECOND]);
    ringsecs[num].clean();
    //ringsecs[num].test(true);
    current_first_led += NUM_RING_SEC_PIXELS;
  }
}

void init_numbers() {
  // init all the numbers
  Serial.println("Init numbers ...");
  unsigned int current_first_led = NUM_RING_PIXELS + NUM_POINTERS_PIXELS;
  for(uint8_t num = 0; num < NUM_NUMBERS; num++)
  {
    number[num].init(pixels, current_first_led, NUM_NUMBER_PIXELS[num],color[COL_NUM]);
    number[num].clean();
    number[num].test(true);
    current_first_led += NUM_NUMBER_PIXELS[num];
  }
}

void init_pointers() {
  Serial.println("Init pointers ...");
  // init all the pointers - each as 6 leds, starts at 12,11,10 ... 1, each even is inversed (goes from outside into center)
  unsigned int current_first_led = NUM_RING_PIXELS;
  bool is_insideoutside = false;
  for(int8_t num = NUM_POINTERS-1; num >= 0; num--)
  {
    is_insideoutside = num%2==0? false:true;
    pointer[num].init(pixels, current_first_led, NUM_POINTER_PIXELS ,color[COL_MINUTE],is_insideoutside);
    pointer[num].clean();
    //pointer[num].test();
    current_first_led += NUM_POINTER_PIXELS;
  }
}

/*
void tick_tock()
{
    if(tick_not_tock == true)
      {tone(PIN_BUZZER, TICK_FREQ, TICK_DLY);}
    else
      { tone(PIN_BUZZER, TOCK_FREQ, TOCK_DLY);}
    tick_not_tock = !tick_not_tock;
}
*/

void init_test_pixel()
{
  pixels = (CRGB*) malloc(NUM_ALL_PIXELS * sizeof(CRGB) );
  FastLED.clearData();
  FastLED.addLeds<WS2812B, PIN_PIXELS, RGB>(pixels, NUM_ALL_PIXELS);  // GRB ordering is typical
  FastLED.clear(true);
  Serial.printf("Init LED pixels %d \n",NUM_ALL_PIXELS);
  /*
  for(unsigned char col = COL_SECOND; col < COL_LAST; col++)
    {
    unsigned int pixel = 0;      
    for(pixel = 0; pixel < NUM_ALL_PIXELS; pixel++)
    {
      if(pixel > 0)
      {
        pixels[pixel-1] = CHSV(0,0,0);
      }
      pixels[pixel] = CHSV(color[col].hue, color[col].sat, color[col].bright);
      FastLED.show();
      delay(FASTLEDDLY);
    }
    pixels[pixel] = CHSV(0,0,0);
    FastLED.show();
    delay(FASTLEDDLY);
  }
  */
  Serial.println("Done.");
}

void setup() {

  ESP.wdtEnable(5000);
  Serial.begin(115200);
  Serial.println("Lets tick ... tock ... tick ... tock ... ");
  // put your setup code here, to run once:
  
  init_test_pixel();
  init_ringsecs();
  init_ringmins();
  init_pointers();
  init_numbers();
  //init_rtc();

}

unsigned int hour = 0;
unsigned int minute = 0;
unsigned int second = 0;

void loop() {

  /*
  RtcDateTime now = Rtc.GetDateTime();
  if(now.Second() != second)
  {
    pixels.clear();

    //tick_tock();

    hour = now.Hour();
    minute = now.Minute();
    second = now.Second();

    pointer[hour].fill(color_pointers_hour);
    //pointer[hour].ffill(color_pointers_hour, minute/(60/NUM_POINTER_PIXELS));
    pixels.show();

    Serial.print(hour);
    Serial.print(":");
    Serial.print(minute);
    Serial.print(":");
    Serial.println(second);
    
  }
  */
  
  
  for(unsigned int second = 0; second<60; second++)
  {
      minute = second == 59 ? minute + 1 : minute;
      hour = minute == 60 ? hour+1 : hour;
      minute = minute > 59 ? 0 : minute;
      hour = hour > 11 ? 0 : hour;

      FastLED.clearData();
      ringsecs[second].fill();
      ringmins[minute].fill();
      number[hour].fill();
      FastLED.show();
      delay(100);

      //Serial.printf("%02d : %02d : %02d \n" , hour, minute, second);
  }
  
  
}