#include <Arduino.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Wire.h>
#include "RoboEyes.h"
#include "icons.h"
#include <OneWire.h>
#include <DallasTemperature.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define TemperatureSensorPin D4
#define HumiditySensorPin A0
#define TouchPin D5

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
RoboEyes<Adafruit_SSD1306> PotPet(display); 

#define FrameDelay (80)
#define FrameWidth (48)
#define FrameHeight (48)


// Display Variables
int DisplayPage = 0;
bool RefreshNeeded = true;
bool StartUp = true;
int SleepTimer = 0;
unsigned long time_now = 0;
int frame = 0;
int mins = 0;
int hours = 0;

// Non-blocking timers
unsigned long lastFrameMillis = 0;
unsigned long lastSensorMillis = 0;
unsigned long lastTouchMillis = 0;

// Sensor Variables
float Temperature = 0;
float Humidity = 0; 
int TempH = 0;
int HumH = 0;
OneWire oneWire(TemperatureSensorPin);
DallasTemperature sensors(&oneWire);

void EmotionPage() {
  PotPet.open();
  if ((TempH + HumH) >= 3) {
    PotPet.setMood(HAPPY);
    PotPet.update();
  } else if ((TempH + HumH) >= 1) {
    PotPet.setMood(DEFAULT);
    PotPet.update();
  } else if ((TempH + HumH) >= -1) {
    PotPet.setMood(TIRED);
    PotPet.update();
  } else if ((TempH + HumH) >= -4) {
    PotPet.setMood(ANGRY);
    PotPet.update();
  }
  if (Temperature >= 30 || Temperature <= 10){
    PotPet.setSweat(true);
    PotPet.update();
  } else {
    PotPet.setSweat(false);
    PotPet.update();
  }
  unsigned long now = millis();
  if (now - lastTouchMillis >= 20000) {
    PotPet.setIdleMode(true);
    PotPet.update();
  }
  now = millis();
  if (now - lastTouchMillis >= 600000) {
    PotPet.setIdleMode(false);
    PotPet.setPosition(DEFAULT);
    PotPet.setMood(TIRED);
    PotPet.update();
    now = millis();
    if (now - lastTouchMillis >= 601500) {
      PotPet.close();
      PotPet.update();
    }
  }
}

void ClockPage() {
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);
  display.drawBitmap(40, 0, ClockFrames[frame], FrameWidth, FrameHeight, 1);
  frame = (frame + 1) % ClockFrameCount;
  display.setCursor(34, 50);
  if (hours < 10) display.print("0");
  display.print(hours);
  display.print(":");
  if (mins < 10) display.print("0");
  display.print(String(mins));
  display.display();
}

void StatPage() {
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.drawBitmap(5, 0, TemperatureFrames[frame], FrameWidth, FrameHeight, 1);
  frame = (frame + 1) % TemperatureFrameCount;
  display.setCursor(17, 50);
  display.print(int(Temperature));
  display.print((char)247);
  display.print("C");
  display.drawFastVLine(64, 4, 64, 60);
  display.drawBitmap(75, 0, HumidityFrames[frame], FrameWidth, FrameHeight, 1);
  frame = (frame + 1) % HumidityFrameCount;
  display.setCursor(90, 50);
  display.print(int(Humidity));
  display.print("%"); 
  display.display();
}

void RefreshDisplay() {
  if (DisplayPage == 0) {
    EmotionPage();
  } else if (DisplayPage == 1) {
    ClockPage();
  } else if (DisplayPage == 2) {
    StatPage();
  }
}

void ReadSensor() {
  Serial.println("Reading Sensors...");
  unsigned long now = millis();
  
  //Read DS18B20 Temperature Sensor
  sensors.requestTemperatures();
  Temperature = sensors.getTempCByIndex(0);
  Serial.println("Temperature: " + String(Temperature) + " C");
  
  //Read KS0049 Soil Humidity Sensor
  int rawHumidity = analogRead(HumiditySensorPin);
  Humidity = map(rawHumidity, 5, 1023, 0, 100);
  Serial.println("Raw Humidity: " + String(rawHumidity) + " %");
  Serial.println("Humidity: " + String(Humidity) + " %");
  lastSensorMillis = now;
  
  // humidity levels
  if (Humidity >= 91) { 
    HumH = -1;
  } else if (Humidity >= 76) {
    HumH = 1;
  } else if (Humidity >= 50) {
    HumH = 2;
  } else if (Humidity >= 35) {
    HumH = -1;
  } else {
    HumH = -2;
  }

  // temperature levels
  if (Temperature > 30.0f) {
    TempH = -1;
  } else if (Temperature > 25.0f) {
    TempH = 2;
  } else if (Temperature >= 15.0f) {
    TempH = 1;
  } else if (Temperature >= 10.0f) {
    TempH = -1;
  } else {
    TempH = -2;
  }
}

void setup() {
  Serial.begin(9600);
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;);
  }
  sensors.begin();
  pinMode(HumiditySensorPin, INPUT);
  pinMode(TemperatureSensorPin, INPUT);
  pinMode(TouchPin, INPUT);
  display.clearDisplay();
}

void loop() {
  if (StartUp) {
    PotPet.begin(SCREEN_WIDTH, SCREEN_HEIGHT, 100);
    PotPet.setPosition(DEFAULT);
    PotPet.close();
    PotPet.setMood(DEFAULT);
    PotPet.setAutoblinker(true, 5, 4);
    PotPet.update();
    delay(2000);
    ReadSensor();
    StartUp = false;
  }

  unsigned long now = millis();
  if (now - lastSensorMillis >= 1000) {
    ReadSensor();
  }
  
  now = millis();
  if (now - lastFrameMillis >= FrameDelay) {
    if (DisplayPage == 0) EmotionPage();
    else if (DisplayPage == 1) ClockPage();
    else if (DisplayPage == 2) StatPage();
    lastFrameMillis = now;
  }
  
  if (digitalRead(TouchPin) == HIGH) {
    if (DisplayPage == 0) {
      PotPet.setIdleMode(false);
      PotPet.setPosition(DEFAULT);
      PotPet.update();
    }
    if (now - lastTouchMillis >= 300) {
      lastTouchMillis = now;
      DisplayPage = (DisplayPage + 1) % 3;
      RefreshNeeded = true;
    } 
  }
  if (RefreshNeeded) {
    RefreshDisplay();
    RefreshNeeded = false;
  }

  if (Serial.available() > 0) {
    String timeSet = Serial.readStringUntil('\n');
    hours = timeSet.substring(0,timeSet.indexOf(':')).toInt();
    mins = timeSet.substring(timeSet.lastIndexOf(':') + 1).toInt();
    Serial.println("Time Set to: " + String(hours) + ":" + String(mins));
  }

  now = millis();
  if (now - time_now >= 60000) {
    time_now = now;
    mins++;
    if (mins >= 60) {
      mins = 0;
      hours++;
      if (hours >= 24) {
        hours = 0;
      }
    }
  }
}