/*
 * ==========================================================
 * Focus Pebble V1.0
 *
 * Focus Controller
 *
 * Controls:
 *  - Touch Input
 *  - Focus Session
 *  - Motion Events
 *  - Progress Tracking
 *  - Session Statistics
 *  - Focus Score
 * ==========================================================
 */

#include "FocusController.h"

////////////////////////////////////////////////////////////
// Constructor
////////////////////////////////////////////////////////////

FocusController::FocusController(
  Motion& motionSensor,
  Effects& effectsManager)
  : motion(motionSensor),
    effects(effectsManager) {
  state = STATE_READY;

  lastTouchState = false;

  sessionStartTime = 0;

  pickupHandled = false;
  shakeHandled = false;

  resetStatistics();
}

////////////////////////////////////////////////////////////

void FocusController::resetStatistics() {
  pickupCount = 0;
  shakeCount = 0;

  stillTime = 0;
  lastStillUpdate = millis();

  lastProgressLogged = 0;

  focusScore = 100;
}

////////////////////////////////////////////////////////////

void FocusController::begin() {
  pinMode(TOUCH_PIN, INPUT);

  effects.startup();

  LOG("SYSTEM", "Focus Controller Ready");
}

////////////////////////////////////////////////////////////

void FocusController::update() {
  updateTouch();

  motion.update();

  updateMotion();

  updateFocus();

  if (state == STATE_READY) {
    effects.idle();
  }

  effects.update();
}

////////////////////////////////////////////////////////////

void FocusController::updateTouch() {
  bool touch = digitalRead(TOUCH_PIN);

  if (touch && !lastTouchState) {
    LOG("TOUCH", "Touch Detected");

    if (state == STATE_READY) {
      startSession();
    } else if (state == STATE_FOCUS) {
      stopSession();
    }
  }

  lastTouchState = touch;
}

////////////////////////////////////////////////////////////

void FocusController::startSession() {
  state = STATE_FOCUS;

  sessionStartTime = millis();

  pickupHandled = false;
  shakeHandled = false;

  resetStatistics();

  effects.wake();

  LOG("FOCUS", "Session Started");
}

////////////////////////////////////////////////////////////

void FocusController::stopSession() {
  state = STATE_READY;

  effects.idle();

  LOG("FOCUS", "Session Cancelled");
}

////////////////////////////////////////////////////////////

void FocusController::completeSession() {
  state = STATE_COMPLETE;

  effects.completion();

  LOG("FOCUS", "========================");
  LOG("FOCUS", "Session Complete");

  LOG_VALUE("FOCUS", "Focus Score", focusScore);
  LOG_VALUE("FOCUS", "Pickup Events", pickupCount);
  LOG_VALUE("FOCUS", "Shake Events", shakeCount);
  LOG_VALUE("FOCUS", "Still Time (sec)", stillTime / 1000);

  LOG("FOCUS", "========================");

  state = STATE_READY;

  effects.idle();
}

////////////////////////////////////////////////////////////

void FocusController::updateMotion() {
  if (state != STATE_FOCUS)
    return;

  // Track still time
  if (motion.isStill()) {
    stillTime += millis() - lastStillUpdate;
  }

  lastStillUpdate = millis();

  if (motion.isPickup()) {
    if (!pickupHandled) {
      pickupHandled = true;
      shakeHandled = false;

      pickupCount++;

      if (focusScore >= 2)
        focusScore -= 2;

      effects.pickup();

      LOG_VALUE("FOCUS", "Pickup Count", pickupCount);
    }
  } else {
    pickupHandled = false;
  }

  if (motion.isShake()) {
    if (!shakeHandled) {
      shakeHandled = true;
      pickupHandled = false;

      shakeCount++;

      if (focusScore >= 5)
        focusScore -= 5;
      else
        focusScore = 0;

      effects.warning();

      LOG_VALUE("FOCUS", "Shake Count", shakeCount);
    }
  } else {
    shakeHandled = false;
  }
}

////////////////////////////////////////////////////////////

void FocusController::updateFocus() {
  if (state != STATE_FOCUS)
    return;

  float progress = getProgress();

  effects.progress(progress);

  uint8_t p = (uint8_t)progress;

  if (p >= lastProgressLogged + 10) {
    lastProgressLogged = p;

    LOG_VALUE("FOCUS", "Progress", p);
  }

  if (progress >= 100.0f) {
    completeSession();
  }
}

////////////////////////////////////////////////////////////

float FocusController::getProgress() {
  unsigned long elapsed = millis() - sessionStartTime;

  float progress =
    ((float)elapsed / (float)FOCUS_DURATION) * 100.0f;

  if (progress > 100.0f)
    progress = 100.0f;

  return progress;
}