#include "NerfStatTrakConfig.h"
#include "Globals.h"
#include "StateHandler.h"
#include "LowPower.h"
#include "EEPROM.h"


SegmentDisplay* display;
Button* buttonA;
Button* buttonB;
Button* buttonArmed;

uint32_t shots = 0;
uint32_t hits = 0;

unsigned long currentTime;
AbstractStateHandler* currentState;

byte shotCounterOffset;
byte hitCounterOffset;


#define counterTreshold (uint32_t)10000
uint32_t lastShotCounterIteration;
uint32_t lastHitCounterIteration;

void setCurrentState(AbstractStateHandler* value)
{
  buttonA->block();
  buttonB->block();

  currentState = value;
  currentState->setup();
}


void update()
{
  currentTime = millis();

  buttonA->update(currentTime);
  buttonB->update(currentTime);
  buttonArmed->update(currentTime);

  // Count the shot no matter which state we're in
  if (buttonArmed->changed() && !buttonArmed->pressed())
    addShot();
}


void wakeUp()
{
}


void sleep()
{
  display->clear();

  // Uses RocketScream's Low-Power library
  // https://github.com/rocketscream/Low-Power
  uint32_t interrupt = digitalPinToInterrupt(NerfButtonWakeUp);

  attachInterrupt(interrupt, wakeUp, NerfButtonWakeUpState);
  LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
  detachInterrupt(interrupt);


  currentTime = millis();

  // The button triggers the wake-up, so block the first release after
  if (NerfButtonWakeUp == NerfButtonA)
  {
    buttonA->update(currentTime);
    buttonA->block();
  }

  if (NerfButtonWakeUp == NerfButtonB)
  {
    buttonB->update(currentTime);
    buttonB->block();
  }
}


#define counterLocation(offset) 2 + (offset * sizeof(uint32_t))

void readCounters()
{
  shotCounterOffset = EEPROM.read(0);
  hitCounterOffset = EEPROM.read(1);

  EEPROM.get(counterLocation(shotCounterOffset), shots);
  EEPROM.get(counterLocation(hitCounterOffset), hits);

  lastShotCounterIteration = shots / counterTreshold;
  lastHitCounterIteration = hits / counterTreshold;
}


void setShots(uint32_t value)
{
  shots = value;

  uint32_t newShotCounterIteration = shots / counterTreshold;
  if (newShotCounterIteration != lastShotCounterIteration || value == 0)
  {
    shotCounterOffset++;
    if (counterLocation(shotCounterOffset) > EEPROM.length() - sizeof(uint32_t))
    {
      shotCounterOffset = 0;
      if (shotCounterOffset == hitCounterOffset)
        shotCounterOffset++;
    }

    EEPROM.write(0, shotCounterOffset);
    lastShotCounterIteration = newShotCounterIteration;
  }

  EEPROM.write(counterLocation(shotCounterOffset), shots);
}


void addShot()
{
  setShots(shots + 1);
}


void setHits(uint32_t value)
{
  hits = value;

  uint32_t newHitCounterIteration = hits / counterTreshold;
  if (newHitCounterIteration != lastHitCounterIteration || value == 0)
  {
    hitCounterOffset++;
    if (counterLocation(hitCounterOffset) > EEPROM.length() - sizeof(uint32_t))
    {
      hitCounterOffset = 0;
      if (hitCounterOffset == shotCounterOffset)
        hitCounterOffset++;
    }

    EEPROM.write(1, hitCounterOffset);
    lastHitCounterIteration = newHitCounterIteration;
  }

  EEPROM.write(counterLocation(hitCounterOffset), hits);
}


void addHit()
{
  setHits(hits + 1);
}


void resetShots()
{
  setShots(0);
}


void resetHits()
{
  setHits(0);
}