
#include "led_clock.hpp"

void led_clock::init( unsigned test_at_start)
{
    init(COLOR_SECS , COLOR_MINS , COLOR_HOURS , COLOR_NUMS, test_at_start);
}

void led_clock::init(hue_color color_secs, hue_color color_mins, hue_color color_hours, hue_color color_nums, unsigned test_at_start)
{
    pixels_test = test_at_start;
    color[COL_SECOND] = color_secs;    
    color[COL_MINUTE] = color_mins;
    color[COL_HOUR] = color_hours;
    color[COL_NUM] = color_nums;

    init_test_pixel();
    init_ringsecs();
    init_ringmins();
    init_pointers();
    init_numbers();

    //Serial.printf("Init pixels - first:%d last:%d number:%d outsidetoinside:%d\n", element_first_pixel, element_last_pixel, element_number_pixels, element_outside2inside);
}

void led_clock::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();
    if(pixels_test & TEST_MINS)
        ringmins[num].test(true);
    current_first_led += NUM_RING_PIXELS/NUM_MINUTES;
  }
}

void led_clock::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();
    if(pixels_test & TEST_SECS)
        ringsecs[num].test(true);
    current_first_led += NUM_RING_SEC_PIXELS;
  }
}

void led_clock::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++)
  {
    uint8_t real_num = num == 11? 0 : num + 1;
    number[real_num].init(pixels, current_first_led, NUM_NUMBER_PIXELS[num],color[COL_NUM]);
    number[real_num].clean();
    if(pixels_test & TEST_NUMS)
        number[real_num].test(true);
    current_first_led += NUM_NUMBER_PIXELS[num];
  }
}

void led_clock::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--)
  {
    uint8_t real_num = num == 11? 0 : num + 1;
    is_insideoutside = num%2==0? false:true;
    pointer[real_num].init(pixels, current_first_led, NUM_POINTER_PIXELS ,color[COL_MINUTE],is_insideoutside);
    pointer[real_num].clean();
    if(pixels_test & TEST_PTRS)
        pointer[real_num].test();
    current_first_led += NUM_POINTER_PIXELS;
  }
}

void led_clock::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);
  if(pixels_test & TEST_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);
      show();
    }
    FastLED.clear(true);
  }
}

void led_clock::show_minute( uint8_t minute )
{
  for(uint8_t minnr = 0; minnr < minute; minnr++)
  {
    if(minnr % 5 == 0)
    {
      ringmins[minnr].fill(ringmins[minnr].elements_huecolor.hue, ringmins[minnr].elements_huecolor.sat, ringmins[minnr].elements_huecolor.bright/2);
    }
    else
    {
      ringmins[minnr].fill(ringmins[minnr].elements_huecolor.hue, ringmins[minnr].elements_huecolor.sat, ringmins[minnr].elements_huecolor.bright/8);
    }
  }
  ringmins[minute].fill();
}

void led_clock::animate_new_minute( uint8_t minute , uint8_t second, uint8_t hour)
{
  uint8_t old_minute = minute==0?NUM_MINUTES-1:minute-1;
  FastLED.clearData();
  show_minute( old_minute );
  show_hour( hour, old_minute);
  ringsecs[second].fill();
  show();

  for(uint8_t minnr = 1; minnr < minute; minnr++)
  {
    ringmins[minnr].fill();
    if(minnr > 1){
      if(minnr-1 % 5 != 0)
        ringmins[minnr-1].fill(ringmins[minnr-1].elements_huecolor.hue, ringmins[minnr-1].elements_huecolor.sat, ringmins[minnr-1].elements_huecolor.bright/8);
      else
        ringmins[minnr-1].fill(ringmins[minnr-1].elements_huecolor.hue, ringmins[minnr-1].elements_huecolor.sat, ringmins[minnr-1].elements_huecolor.bright/2);
      
    }
    show();
  }
}

void led_clock::animate_new_hour( uint8_t minute , uint8_t second, uint8_t hour)
{
  uint8_t old_hour = hour==0?11:hour-1;
  uint8_t old_minute = NUM_MINUTES-1;
  FastLED.clearData();
  show_minute( old_minute );
  show_hour( old_hour, old_minute);
  ringsecs[second].fill();
  show();

  for(uint8_t minnr = 1; minnr < NUM_MINUTES; minnr++)
  {
    ringmins[minnr].clean();
    show();
  }
}


void led_clock::show_hour( uint8_t hour, uint8_t minute)
{

  uint8_t next_hour = hour == 11? 0:hour+1;
  uint8_t brightness = number[hour].elements_huecolor.bright;
  uint8_t basebrightness = brightness/6;
  uint8_t minutebrightness = (brightness-basebrightness)/NUM_MINUTES;
  uint8_t fractionbrightness = minutebrightness * minute;
  uint8_t numbrightness = (uint8_t)(basebrightness + fractionbrightness);

  //Serial.printf("Show hour: minute=%d, basebright=%d, mintebright=%d, fractionbright=%d, bright=%d\n", minute, basebrightness, minutebrightness, fractionbrightness,  numbrightness );

  number[hour].fill(number[hour].elements_huecolor.hue, number[hour].elements_huecolor.sat,brightness-numbrightness);
  number[next_hour].fill(number[next_hour].elements_huecolor.hue, number[next_hour].elements_huecolor.sat,numbrightness);
  pointer[hour].ffill(NUM_MINUTES-minute, NUM_MINUTES);
  pointer[next_hour].ffill(minute, NUM_MINUTES);

}

void led_clock::showtime(uint8_t hour, uint8_t minute, uint8_t second)
{
      if(second == 0)
      {
        if(minute!=0)
        {
          animate_new_minute( minute , second, hour);
        }
        else
        {
          animate_new_hour( minute , second, hour);
        }
        
      }
      else
      {
        FastLED.clearData();
        show_minute( minute );
        ringsecs[second].fill();
        show_hour( hour, minute);
        show();
      }
}

