const storage = require("Storage");
const heatshrink = require("heatshrink");
const STATE_PATH = "stlap.state.json";
g.setFont("Vector", 24);
const BUTTON_ICONS = {
  play: heatshrink.decompress(atob("jEYwMAkAGBnACBnwCBn+AAQPgAQPwAQP8AQP/AQXAAQPwAQP8AQP+AQgICBwQUCEAn4FggyBHAQ+CIgQ")),
  pause: heatshrink.decompress(atob("jEYwMA/4BBAX4CEA")),
  reset: heatshrink.decompress(atob("jEYwMA/4BB/+BAQPDAQPnAQIAKv///0///8j///EP//wAQQICBwQUCEhgyCHAQ+CIgI="))
};

let state = storage.readJSON(STATE_PATH);
const STATE_DEFAULT = {
  wasRunning: false,              //If the stopwatch was ever running since being reset
  sessionStart: 0,                //When the stopwatch was first started
  running: false,                 //Whether the stopwatch is currently running
  startTime: 0,                   //When the stopwatch was last started.
  pausedTime: 0,                  //When the stopwatch was last paused.
  elapsedTime: 0                  //How much time was spent running before the current start time. Update on pause.
};
if (!state) {
  state = STATE_DEFAULT;
}

let lapFile;
let lapHistory;
if (state.wasRunning) {
  lapFile = 'stlap-' + state.sessionStart + '.json';
  lapHistory = storage.readJSON(lapFile);
  if (!lapHistory)
    lapHistory = {
      final: false, //Whether the stopwatch has been reset. It is expected that the stopwatch app will create a final split when reset. If this is false, it is expected that this hasn't been done, and that the current time should be used as the "final split"
      splits: []  //List of times when the Lap button was pressed
    };
} else
  lapHistory = {
    final: false, //Whether the stopwatch has been reset. It is expected that the stopwatch app will create a final split when reset. If this is false, it is expected that this hasn't been done, and that the current time should be used as the "final split"
    splits: []  //List of times when the Lap button was pressed
  };

//Get the number of milliseconds that stopwatch has run for
function getTime() {
  if (!state.wasRunning) {
    //If the timer never ran, zero ms have passed
    return 0;
  } else if (state.running) {
    //If the timer is running, the time left is current time - start time + preexisting time
    return (new Date()).getTime() - state.startTime + state.elapsedTime;
  } else {
    //If the timer is not running, the same as above but use when the timer was paused instead of now.
    return state.pausedTime - state.startTime + state.elapsedTime;
  }
}

let gestureMode = false;

function drawButtons() {
  //Draw the backdrop
  const BAR_TOP = g.getHeight() - 48;
  const BUTTON_Y = BAR_TOP + 12;
  const BUTTON_LEFT = g.getWidth() / 4 - 12;    //For the buttons, we have to subtract 12 because images do not obey alignment, but their size is known in advance
  const TEXT_LEFT = g.getWidth() / 4;           //For text, we do not have to subtract 12 because they do obey alignment.
  const BUTTON_MID = g.getWidth() / 2 - 12;
  const TEXT_MID = g.getWidth() / 2;
  const BUTTON_RIGHT = g.getHeight() * 3 / 4 - 12;

  g.setColor(0, 0, 1).setFontAlign(0, -1)
    .clearRect(0, BAR_TOP, g.getWidth(), g.getHeight())
    .fillRect(0, BAR_TOP, g.getWidth(), g.getHeight())
    .setColor(1, 1, 1);

  if (gestureMode)
    g.setFont('Vector', 16)
      .drawString('Button: Lap/Reset\nSwipe: Start/stop\nTap: Light', TEXT_MID, BAR_TOP);
  else {
    g.setFont('Vector', 24);
    if (!state.wasRunning) {  //If the timer was never running:
      if (storage.read('stlapview.app.js') !== undefined)         //If stlapview is installed, there should be a button to open it and a button to start the timer
        g.drawLine(g.getWidth() / 2, BAR_TOP, g.getWidth() / 2, g.getHeight())
          .drawString("Laps", TEXT_LEFT, BUTTON_Y)
          .drawImage(BUTTON_ICONS.play, BUTTON_RIGHT, BUTTON_Y);
      else g.drawImage(BUTTON_ICONS.play, BUTTON_MID, BUTTON_Y);  //Otherwise, only a button to start the timer
    } else {                  //If the timer was running:
      g.drawLine(g.getWidth() / 2, BAR_TOP, g.getWidth() / 2, g.getHeight());
      if (state.running) {    //If it is running now, have a lap button and a pause button
        g.drawString("LAP", TEXT_LEFT, BUTTON_Y)
          .drawImage(BUTTON_ICONS.pause, BUTTON_RIGHT, BUTTON_Y);
      } else {                //If it is not running now, have a reset button and a
        g.drawImage(BUTTON_ICONS.reset, BUTTON_LEFT, BUTTON_Y)
          .drawImage(BUTTON_ICONS.play, BUTTON_RIGHT, BUTTON_Y);
      }
    }
  }
}

function drawTime() {
  function pad(number) {
    return ('00' + parseInt(number)).slice(-2);
  }

  let time = getTime();
  g.reset(0, 0, 0)
    .setFontAlign(0, 0)
    .setFont("Vector", 36)
    .clearRect(0, 24, g.getWidth(), g.getHeight() - 48)

    //Draw the time
    .drawString((() => {
      let hours = Math.floor(time / 3600000);
      let minutes = Math.floor((time % 3600000) / 60000);
      let seconds = Math.floor((time % 60000) / 1000);
      let hundredths = Math.floor((time % 1000) / 10);

      if (hours >= 1) return `${hours}:${pad(minutes)}:${pad(seconds)}`;
      else return `${minutes}:${pad(seconds)}:${pad(hundredths)}`;
    })(), g.getWidth() / 2, g.getHeight() / 2);

  //Draw the lap labels if necessary
  if (lapHistory.splits.length >= 1) {
    let lastLap = lapHistory.splits.length;
    let curLap = lastLap + 1;

    g.setFont("Vector", 12)
      .drawString((() => {
        let lapTime = time - lapHistory.splits[lastLap - 1];
        let hours = Math.floor(lapTime / 3600000);
        let minutes = Math.floor((lapTime % 3600000) / 60000);
        let seconds = Math.floor((lapTime % 60000) / 1000);
        let hundredths = Math.floor((lapTime % 1000) / 10);

        if (hours == 0) return `Lap ${curLap}: ${pad(minutes)}:${pad(seconds)}:${pad(hundredths)}`;
        else return `Lap ${curLap}: ${hours}:${pad(minutes)}:${pad(seconds)}:${pad(hundredths)}`;
      })(), g.getWidth() / 2, g.getHeight() / 2 + 18)
      .drawString((() => {
        let lapTime;
        if (lastLap == 1) lapTime = lapHistory.splits[lastLap - 1];
        else lapTime = lapHistory.splits[lastLap - 1] - lapHistory.splits[lastLap - 2];
        let hours = Math.floor(lapTime / 3600000);
        let minutes = Math.floor((lapTime % 3600000) / 60000);
        let seconds = Math.floor((lapTime % 60000) / 1000);
        let hundredths = Math.floor((lapTime % 1000) / 10);

        if (hours == 0) return `Lap ${lastLap}: ${pad(minutes)}:${pad(seconds)}:${pad(hundredths)}`;
        else return `Lap ${lastLap}: ${hours}:${pad(minutes)}:${pad(seconds)}:${pad(hundredths)}`;
      })(), g.getWidth() / 2, g.getHeight() / 2 + 30);
  }
}

drawButtons();

function firstTimeStart(now, time) {
  state = {
    wasRunning: true,
    sessionStart: Math.floor(now),
    running: true,
    startTime: now,
    pausedTime: 0,
    elapsedTime: 0,
  };
  lapFile = 'stlap-' + state.sessionStart + '.json';
  setupTimerInterval();
  Bangle.buzz(200);
  drawButtons();
}

function split(now, time) {
  lapHistory.splits.push(time);
  Bangle.buzz();
}

function pause(now, time) {
  //Record the exact moment that we paused
  state.pausedTime = now;

  //Stop the timer
  state.running = false;
  stopTimerInterval();
  Bangle.buzz(200);
  drawTime();
  drawButtons();
}

function reset(now, time) {
  //Record the time
  lapHistory.splits.push(time);
  lapHistory.final = true;
  storage.writeJSON(lapFile, lapHistory);

  //Reset the timer
  state = STATE_DEFAULT;
  lapHistory = {
    final: false,
    splits: []
  };
  Bangle.buzz(500);
  drawTime();
  drawButtons();
}

function start(now, time) {
  //Start the timer and record when we started
  state.elapsedTime += (state.pausedTime - state.startTime);
  state.startTime = now;
  state.running = true;
  setupTimerInterval();
  Bangle.buzz(200);
  drawTime();
  drawButtons();
}

Bangle.on("touch", (button, xy) => {
  //In gesture mode, just turn on the light and then return
  if (gestureMode) {
    Bangle.setLCDPower(true);
    return;
  }

  //If we support full touch and we're not touching the keys, ignore.
  //If we don't support full touch, we can't tell so just assume we are.
  if (xy !== undefined && xy.y <= g.getHeight() - 48) return;

  let now = (new Date()).getTime();
  let time = getTime();

  if (!state.wasRunning) {
    if (storage.read('stlapview.app.js') !== undefined) {
      //If we were never running and stlapview is installed, there are two buttons: open stlapview and start the timer
      if (button == 1) load('stlapview.app.js');
      else firstTimeStart(now, time);
    }
    //If stlapview there is only one button: the start button
    else firstTimeStart(now, time);
  } else if (state.running) {
    //If we are running, there are two buttons: lap and pause
    if (button == 1) split(now, time);
    else pause(now, time);

  } else {
    //If we are stopped, there are two buttons: reset and continue
    if (button == 1) reset(now, time);
    else start(now, time);
  }
});

Bangle.on('swipe', direction => {
  let now = (new Date()).getTime();
  let time = getTime();

  if (gestureMode) {
    Bangle.setLCDPower(true);
    if (!state.wasRunning) firstTimeStart(now, time);
    else if (state.running) pause(now, time);
    else start(now, time);
  } else {
    gestureMode = true;
    Bangle.setOptions({
      lockTimeout: 0
    });
    drawTime();
    drawButtons();
  }
});

setWatch(() => {
  let now = (new Date()).getTime();
  let time = getTime();

  if (gestureMode) {
    Bangle.setLCDPower(true);
    if (state.running) split(now, time);
    else reset(now, time);
  }
}, BTN1, { repeat: true });

let timerInterval;

function setupTimerInterval() {
  if (timerInterval !== undefined) {
    clearInterval(timerInterval);
  }
  timerInterval = setInterval(drawTime, 10);
}

function stopTimerInterval() {
  if (timerInterval !== undefined) {
    clearInterval(timerInterval);
    timerInterval = undefined;
  }
}

drawTime();
if (state.running) {
  setupTimerInterval();
}

//Save our state when the app is closed
E.on('kill', () => {
  storage.writeJSON(STATE_PATH, state);
  if (state.wasRunning) {
    storage.writeJSON(lapFile, lapHistory);
  }
});

Bangle.loadWidgets();
Bangle.drawWidgets();