#pragma once
#include <stdint.h>

namespace LessonClock {
enum Mode { CHARACTER, CLOCK, TIMER, GAMES };
inline Mode nextMode(Mode mode) { return Mode((int(mode)+1)%4); }
inline bool screenOwned(Mode mode, bool silence, bool alert) {
  return mode!=CHARACTER || silence || alert;
}
struct Lesson { int start; int end; };
constexpr Lesson lessons[] = {
  {8*3600+30*60,9*3600+15*60}, {9*3600+25*60,10*3600+10*60},
  {10*3600+25*60,11*3600+10*60}, {11*3600+30*60,12*3600+15*60},
  {12*3600+35*60,13*3600+20*60}, {13*3600+30*60,14*3600+15*60},
  {14*3600+25*60,15*3600+10*60}
};
constexpr int count = sizeof(lessons)/sizeof(lessons[0]);
constexpr uint8_t schoolDays = 0b00111110; // Sunday=0; provisional Monday–Friday.
constexpr int silenceStart = 9*3600;
constexpr int silenceEnd = silenceStart+60;
constexpr bool dailySilence = true;
constexpr uint32_t holdMs = 2000, debounceMs = 30;
// DS3231 stores UTC. Current EU-style Kyiv DST rule, configurable if law changes.
constexpr const char* timezone = "EET-2EEST,M3.5.0/3,M10.5.0/4";
enum Phase { BEFORE, LESSON, BREAK, AFTER, WEEKEND, SILENCE };
struct Period { Phase phase; int index; int remaining; int duration; };
inline Period period(int second, int weekday) {

  if (dailySilence && second >= silenceStart && second < silenceEnd) {
    return {SILENCE,-1,silenceEnd-second,60};
  }

  if (!(schoolDays & (1u<<weekday))) {
    return {WEEKEND,-1,0,0};
  }

  for (int i=0;i<count;++i) {

    if (second < lessons[i].start) {
      int start = i ? lessons[i-1].end : 0;
      return {i ? BREAK : BEFORE,i,lessons[i].start-second,lessons[i].start-start};
    }

    if (second < lessons[i].end) {
      return {LESSON,i,lessons[i].end-second,lessons[i].end-lessons[i].start};
    }

  }

  return {AFTER,-1,0,0};
}
inline int nextLesson(int second) {

  for (int i=0;i<count;++i) {

    if (lessons[i].start > second) {
      return i;
    }

  }

  return 0;
}
enum Gesture { NONE, CLICK, HOLD };
struct Button {
  bool raw=false, down=false, fired=false, armed=false;
  uint32_t changed=0, pressed=0;
  Gesture update(bool value, uint32_t now) {

    if (value != raw) {
      raw=value;
      changed=now;
    }

    if (now-changed < debounceMs) {
      return NONE;
    }

    if (!raw && !down) {
      armed=true;
    }

    if (raw != down) {
      down=raw;

      if (down) {
        pressed=now;
        fired=false;
      } else {
        bool click=armed && !fired;
        armed=true;
        return click ? CLICK : NONE;
      }

    }

    if (down && armed && !fired && now-pressed >= holdMs) {
      fired=true;
      return HOLD;
    }

    return NONE;
  }
};
// A jump or initial sync must not replay old bells. Absolute UTC suppresses repeats.
struct Bell {
  uint32_t previous=0, last=0;
  int update(uint32_t utc, int second, int weekday) {
    uint32_t delta=utc-previous;
    bool continuous=previous && delta>0 && delta<=3;
    previous=utc;

    if (!continuous || !(schoolDays & (1u<<weekday))) {
      return -1;
    }

    for (int i=0;i<count;++i) {
      int age=second-lessons[i].start;

      if (age>=0 && age<int(delta)) {
        uint32_t event=utc-age;

        if (event>last) {
          last=event;
          return i;
        }

      }

    }

    return -1;
  }
};
}
