#include "LedRing.h"

LedRing::LedRing(const int count, const int pin, const int mode, const int refreshRate)
  : m_strip(count, pin, mode + refreshRate), m_lastUpdateMs(0xFFFF - LEDRING_ANIMATION_DELAY_MS), m_currentRingAnimation(ERingAnimation::CIRCULATE), m_ledCount(count),
  m_currentLed(LEDRING_START_NUM + 2), m_stepCounter(0), m_speedDelay(LEDRING_START_DELAY_MS), m_pCallbackProc(nullptr), m_pCallbackParam(nullptr) { }


void LedRing::callAnimationCallback(void) {
  if (m_pCallbackProc != nullptr) {
    m_pCallbackProc(m_currentRingAnimation, m_pCallbackParam);
  }
}

void LedRing::circulateRoutine(void) {
  m_currentLed++;

  if (m_currentLed >= m_ledCount) {
    m_currentLed = 0;
  }

  m_strip.clear();
  m_strip.setPixelColor((LEDRING_START_NUM - 1), 255, 165, 0);
  m_strip.setPixelColor(LEDRING_START_NUM, 255, 0, 0);
  m_strip.setPixelColor((LEDRING_START_NUM + 1), 255, 165, 0);
  m_strip.setPixelColor(m_currentLed, 0, 255, 0);
  m_strip.setBrightness(10); //Helligkeit auf 10%
}

bool LedRing::hitRoutine(bool full) {
  if (m_stepCounter < 5) {
    if ((m_stepCounter % 2) == 0) {
      m_strip.fill((full ? m_strip.Color(0, 255, 0) : m_strip.Color(255, 165, 0)), 0, m_ledCount); m_strip.setBrightness(10); //Helligkeit auf 10%
    }
    else {
      m_strip.clear();
    }

    return true;
  }

  m_speedDelay = (int)((float)m_speedDelay * LEDRING_SPEED_FACTOR);
  return false;
}

bool LedRing::missRoutine(void) {
  if (m_stepCounter < 5) {
    if ((m_stepCounter % 2) == 0) {
      m_strip.fill(m_strip.Color(255, 0, 0), 0, m_ledCount); m_strip.setBrightness(10); //Helligkeit auf 10%
    } else {
      m_strip.clear();
    }

    return true;
  }

  return false;
}

bool LedRing::gameOverResetRoutine(bool reset) {
  if ((m_stepCounter % 2) == 0) {
    m_strip.clear();

    for (int i = ((m_stepCounter / 2) % 2); i < m_ledCount; i += 2) {
      m_strip.setPixelColor(i, 255, 0, (reset ? 165 : 0)); m_strip.setBrightness(10); //Helligkeit auf 10%
    }

    if (m_stepCounter == 12) {
      m_speedDelay = LEDRING_START_DELAY_MS;
      return false;
    }
  }
  return true;
}


void LedRing::init(void) {
  m_strip.begin();
  m_strip.show();
}

void LedRing::reset(void) {
  m_lastUpdateMs = (millis() - LEDRING_ANIMATION_DELAY_MS);
  m_currentRingAnimation = ERingAnimation::CIRCULATE;
  m_currentLed = (LEDRING_START_NUM + 2);
  m_stepCounter = 0;
  m_speedDelay = LEDRING_START_DELAY_MS;
}

void LedRing::update(void) {
  if ((m_currentRingAnimation == ERingAnimation::CIRCULATE) && ((millis() - m_lastUpdateMs) >= m_speedDelay)) {
    circulateRoutine();
    m_strip.show();
    m_lastUpdateMs = millis();
  }
  else if ((millis() - m_lastUpdateMs) >= LEDRING_ANIMATION_DELAY_MS) {
    switch (m_currentRingAnimation) {
      case ERingAnimation::HIT_HALF:
        if (!hitRoutine(false)) {
          callAnimationCallback();
          m_currentRingAnimation = ERingAnimation::CIRCULATE;
        }
        break;

      case ERingAnimation::HIT_FULL:
        if (!hitRoutine(true)) {
          callAnimationCallback();
          m_currentRingAnimation = ERingAnimation::CIRCULATE;
        }
        break;

      case ERingAnimation::MISS:
        if (!missRoutine()) {
          callAnimationCallback();
          m_currentRingAnimation = ERingAnimation::CIRCULATE;
        }
        break;
        
      case ERingAnimation::GAME_OVER:
      case ERingAnimation::RESET:
        if (!gameOverResetRoutine(m_currentRingAnimation == ERingAnimation::RESET)) {
          callAnimationCallback();
          m_currentRingAnimation = ERingAnimation::CIRCULATE;
        }
        break;
    }

    m_strip.show();
    m_lastUpdateMs = millis();
    m_stepCounter++;
  }
}

void LedRing::playAnimation(ERingAnimation animation) {
  m_lastUpdateMs = (millis() - LEDRING_ANIMATION_DELAY_MS);
  m_currentRingAnimation = animation;
  m_currentLed = (LEDRING_START_NUM + 2);
  m_stepCounter = 0;
}

void LedRing::setAnimationCallback(void (*pCallback) (ERingAnimation, void*), void *pParam) {
  m_pCallbackProc = pCallback;
  m_pCallbackParam = pParam;
}