// Copyleft (c) 2012, Zach Vorhies
// Public domain, no rights reserved.
// This object holds a frame buffer and effects can be applied. This is a higher level
// object than the TCL class which this object uses for drawing.

//#include "./tcl.h"
#include <Arduino.h>
#include "../shared/color.h"
#include "../shared/framebuffer.h"
#include "../shared/settings.h"
#include "./LedRopeTCL.h"
#include "../shared/led_layout_array.h"

#include "FastLED.h"
#include "fl/dbg.h"
#include "fl/ui.h"

using namespace fl;


#define CHIPSET WS2812
#define PIN_DATA 1
#define PIN_CLOCK 2

namespace {

UIButton buttonAllWhite("All white");

ScreenMap init_screenmap() {
  LedColumns cols = LedLayoutArray();
  const int length = cols.length;
  int sum = 0;
  for (int i = 0; i < length; ++i) {
    sum += cols.array[i];
  }
  ScreenMap screen_map(sum, 0.8f);
  int curr_idx = 0;
  for (int i = 0; i < length; ++i) {
    int n = cols.array[i];
    int stagger = i % 2 ? 4 : 0;
    for (int j = 0; j < n; ++j) {
      fl::pair_xy_float xy(i*4, j*8 + stagger);
      screen_map.set(curr_idx++, xy);
    }
  }

  return screen_map;
}

}


///////////////////////////////////////////////////////////////////////////////
void LedRopeTCL::PreDrawSetup() {
  if (!lazy_initialized_) {
    // This used to do something, now it does nothing.
    lazy_initialized_ = true;
  }
}

///////////////////////////////////////////////////////////////////////////////
void LedRopeTCL::RawBeginDraw() {
  PreDrawSetup();
  led_buffer_.clear();
}

///////////////////////////////////////////////////////////////////////////////
void LedRopeTCL::RawDrawPixel(const Color3i& c) {
  RawDrawPixel(c.r_, c.g_, c.b_);
}

///////////////////////////////////////////////////////////////////////////////
void LedRopeTCL::RawDrawPixel(byte r, byte g, byte b) {
  if (led_buffer_.size() >= mScreenMap.getLength()) {
    return;
  }
  if (buttonAllWhite.isPressed()) {
    r = 0xff;
    g = 0xff;
    b = 0xff;
  }
  CRGB c(r, g, b);
  led_buffer_.push_back(CRGB(r, g, b));
}

///////////////////////////////////////////////////////////////////////////////
void LedRopeTCL::RawDrawPixels(const Color3i& c, int n) {
  for (int i = 0; i < n; ++i) {
    RawDrawPixel(c);
  }
}

///////////////////////////////////////////////////////////////////////////////
void LedRopeTCL::set_draw_offset(int val) {
  draw_offset_ = constrain(val, 0, frame_buffer_.length());
}


///////////////////////////////////////////////////////////////////////////////
void LedRopeTCL::RawCommitDraw() {
  if (!controller_added_) {
    controller_added_ = true;
    CRGB* leds = led_buffer_.data();
    size_t n_leds = led_buffer_.size();
    FastLED.addLeds<APA102, PIN_DATA, PIN_CLOCK>(leds, n_leds).setScreenMap(mScreenMap);
  }
  FastLED.show();
}

///////////////////////////////////////////////////////////////////////////////
LedRopeTCL::LedRopeTCL(int n_pixels)
	: draw_offset_(0), lazy_initialized_(false), frame_buffer_(n_pixels) {
  mScreenMap = init_screenmap();
  led_buffer_.reserve(mScreenMap.getLength());
}

///////////////////////////////////////////////////////////////////////////////
LedRopeTCL::~LedRopeTCL() {
}

///////////////////////////////////////////////////////////////////////////////
void LedRopeTCL::Draw() {
  RawBeginDraw();

  const Color3i* begin = GetIterator(0);
  const Color3i* middle = GetIterator(draw_offset_);
  const Color3i* end = GetIterator(length() - 1);

  for (const Color3i* it = middle; it != end; ++it) {
    RawDrawPixel(*it);
  }
  for (const Color3i* it = begin; it != middle; ++it) {
    RawDrawPixel(*it);
  }
  RawCommitDraw();
}

///////////////////////////////////////////////////////////////////////////////
void LedRopeTCL::DrawSequentialRepeat(int repeat) {
  RawBeginDraw();

  const Color3i* begin = GetIterator(0);
  const Color3i* middle = GetIterator(draw_offset_);
  const Color3i* end = GetIterator(length());
  for (const Color3i* it = middle; it != end; ++it) {
    for (int i = 0; i < repeat; ++i) {
      RawDrawPixel(it->r_, it->g_, it->b_);
    }
  }
  for (const Color3i* it = begin; it != middle; ++it) {
    for (int i = 0; i < repeat; ++i) {
      RawDrawPixel(it->r_, it->g_, it->b_);
    }
  }
  RawCommitDraw();
}

///////////////////////////////////////////////////////////////////////////////
void LedRopeTCL::DrawRepeat(const int* value_array, int array_length) {
  RawBeginDraw();
  
  // Make sure that the number of colors to repeat does not exceed the length
  // of the rope.
  const int len = min(array_length, frame_buffer_.length());

  for (int i = 0; i < len; ++i) {
     const Color3i* cur_color = GetIterator(i);  // Current color.
     const int repeat_count = value_array[i];
     // Repeatedly send the same color down the led rope.
     for (int k = 0; k < repeat_count; ++k) {
       RawDrawPixel(cur_color->r_, cur_color->g_, cur_color->b_);
     }
  }
  // Finish the drawing.
  RawCommitDraw();
}

