#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// SSD1306 DISPLAY:
#define SCREEN_WIDTH        128 // OLED display width, in pixels
#define SCREEN_HEIGHT        64 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET            4 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

//Clock variables
String timeStr = "00:00:00";
volatile int hours = 0;
volatile int minutes = 0;
volatile int seconds = 0;
int previousSeconds = 0;
String hoursString = "0";
String minutesString = "0";
String secondsString = "0";

//Analog Clock Variables
float radius = 31;
double angle;
int x1, x2, y1, y2;
const int X_CENTRE = SCREEN_WIDTH/2;
const int Y_CENTRE = SCREEN_HEIGHT/2;

//Stopwatch Variables
volatile bool stopwatchStarted = false;
volatile bool stopwatchFinished = false;
volatile unsigned long startingMillis = 0;
unsigned long stopwatchSeconds = 0;
unsigned long stopwatchMinutes = 0;
unsigned long stopwatchMillis = 0;
String stopwatchStr = "00:00:00";
String stopwatchMinStr = "0";
String stopwatchSecStr = "0";
String stopwatchMilliStr = "0";

void setup() 
{
  // INITIALIZE SSD1306 DISPLAY:
  Wire.begin(); // Start Wire library for I2C
  display.begin(SSD1306_SWITCHCAPVCC, 0x3c); // Initialize OLED with I2C address 0x3c
  Serial.begin(9600);
  display.clearDisplay();

  pinMode(2, INPUT);
  digitalWrite(2, HIGH); //turn on internal pull-up on pin and set resting state to HIGH
  attachInterrupt(digitalPinToInterrupt(2),toggleStopwatch,RISING);

//Setup Timer Interrupt
cli();//stop interrupts

//set timer4 interrupt at 1Hz
 TCCR4A = 0;// set entire TCCR1A register to 0
 TCCR4B = 0;// same for TCCR1B
 TCNT4  = 0;//initialize counter value to 0
 // set compare match register for 1hz increments
 OCR4A = 15624/1;// = (16*10^6) / (1*1024) - 1 (must be <65536)
 // turn on CTC mode
 TCCR4B |= (1 << WGM12);
 // Set CS12 and CS10 bits for 1024 prescaler
 TCCR4B |= (1 << CS12) | (1 << CS10);  
 // enable timer compare interrupt
 TIMSK4 |= (1 << OCIE4A);

sei();//allow interrupts

  DrawClock();
  ProcessInput();
}

ISR(TIMER4_COMPA_vect)
{
  seconds++;
  if (seconds==60){ seconds = seconds -60; minutes++; }
  if (minutes==60){ minutes = minutes -60; hours++; }
  if (hours==24){ hours = 0; }
  previousSeconds = seconds;
}

void loop() 
{
  while (stopwatchStarted == false) { DrawClock(); }
  while (stopwatchStarted == true) { DrawStopwatch(); }
}

void ProcessInput()
{
  Serial.println("Enter hours: ");
  while(Serial.available() == 0) {}
  hours = Serial.parseInt();
  Serial.end();
	Serial.begin(9600);
  Serial.println("Enter minutes: ");
  while(Serial.available() == 0) {}
  minutes = Serial.parseInt(); 
  Serial.end();
	Serial.begin(9600);
  Serial.println("Enter seconds: ");
  while(Serial.available() == 0) {}
  seconds = Serial.parseInt(); 
  Serial.end();
	Serial.begin(9600);
}

void toggleStopwatch()
{
  if (stopwatchStarted == false) { stopwatchStarted = true; startingMillis = millis(); Serial.println("Started"); }
  else if (stopwatchFinished == true) { stopwatchStarted = false; stopwatchFinished = false; Serial.println("Reset");}
  else if (stopwatchStarted == true) { stopwatchFinished = true; Serial.println("Stopped"); }
}

void DrawStopwatch()
{
  delay(27);
  if (stopwatchFinished == false) 
  {
    unsigned long milliseconds = millis() - startingMillis;
    if (milliseconds > 60000) 
    { 
      stopwatchMinutes = milliseconds/60000; 

      if (milliseconds - (stopwatchMinutes*60000) > 1000) 
      { 
        stopwatchSeconds = (milliseconds - (stopwatchMinutes*60000))/1000;
        stopwatchMillis = milliseconds - (stopwatchMinutes*60000) - (stopwatchSeconds*1000);
      }
    }
    else if (milliseconds > 1000) 
    { 
      stopwatchMinutes = 0;
      stopwatchSeconds = milliseconds/1000;
      stopwatchMillis = milliseconds - stopwatchSeconds*1000;

    }
    else 
    {
      stopwatchMinutes = 0;
      stopwatchSeconds = 0;
      stopwatchMillis = milliseconds;
    }
  }
  if (stopwatchMinutes < 10) { stopwatchMinStr = "0" + String(stopwatchMinutes); } else { stopwatchMinStr = String(stopwatchMinutes); }
  if (stopwatchSeconds < 10) { stopwatchSecStr = "0" + String(stopwatchSeconds); } else { stopwatchSecStr = String(stopwatchSeconds); }
  int twoDigitMillis = stopwatchMillis/10;
  if (twoDigitMillis < 10) { stopwatchMilliStr = "0" + String(twoDigitMillis); } else { stopwatchMilliStr = String(twoDigitMillis); }
  stopwatchStr = stopwatchMinStr + ":" + stopwatchSecStr+ ":"  + stopwatchMilliStr;
  if (stopwatchFinished != false || stopwatchStarted != false) 
  {
    display.clearDisplay();
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.setCursor(12,26);
    display.print(stopwatchStr);
    display.display();
  }
}

void DrawClock()
{
  if (previousSeconds != seconds) 
  {
    if (hours < 10) { hoursString = "0" + String(hours); } else { hoursString = String(hours); }
    if (minutes < 10) { minutesString = "0" + String(minutes); } else { minutesString = String(minutes); }
    if (seconds < 10) { secondsString = "0" + String(seconds); } else { secondsString = String(seconds); }
    timeStr = hoursString + ":" + minutesString+ ":"  + secondsString;
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(72,8);
    display.print(timeStr);
    display.drawCircle(X_CENTRE-(SCREEN_WIDTH/4),Y_CENTRE,radius,WHITE);
    display.drawCircle(X_CENTRE-(SCREEN_WIDTH/4),Y_CENTRE,1,WHITE);
    for (int i = 1; i <60; i++)
    {
      //second markings
      angle = i*6;
      angle = angle * 0.0174533;
      x1 = X_CENTRE-(SCREEN_WIDTH/4) + (sin(angle) *radius);
      y1 = Y_CENTRE + (cos(angle)*radius);
      x2 = X_CENTRE-(SCREEN_WIDTH/4) + (sin(angle) *(radius-1));
      y2 = Y_CENTRE + (cos(angle)*(radius-1));
      display.drawLine(x1,y1,x2,y2,WHITE);

      //1-12 markings
      angle = i*30;
      angle = angle * 0.0174533;
      x1 = X_CENTRE-(SCREEN_WIDTH/4) + (sin(angle) *radius);
      y1 = Y_CENTRE + (cos(angle)*radius);
      x2 = X_CENTRE-(SCREEN_WIDTH/4) + (sin(angle) *(radius-4));
      y2 = Y_CENTRE + (cos(angle)*(radius-5));
      display.drawLine(x1,y1,x2,y2,WHITE);

      //seconds hand
      angle = seconds*6;
      angle = angle * 0.0174533;
      x2 = X_CENTRE-(SCREEN_WIDTH/4) + (sin(angle) *(radius-1));
      y2 = Y_CENTRE - (cos(angle)*(radius-1));
      display.drawLine(X_CENTRE-(SCREEN_WIDTH/4),Y_CENTRE,x2,y2,WHITE);

      //minutes hand
      angle = minutes*6;
      angle = angle * 0.0174533;
      x2 = X_CENTRE-(SCREEN_WIDTH/4) + (sin(angle) *(radius-11));
      y2 = Y_CENTRE - (cos(angle)*(radius-11));
      display.drawLine(X_CENTRE-(SCREEN_WIDTH/4),Y_CENTRE,x2,y2,WHITE);

      //hours hand
      angle = hours*30 + (minutes/12) * 6;
      angle = angle * 0.0174533;
      x2 = X_CENTRE-(SCREEN_WIDTH/4) + (sin(angle) *(radius/2));
      y2 = Y_CENTRE - (cos(angle)*(radius/2));
      display.drawLine(X_CENTRE-(SCREEN_WIDTH/4),Y_CENTRE,x2,y2,WHITE);
    }
    display.display();
  }
}