#include "LiquidCrystal_I2C.h"
#include "Classes.h"
#include "Servo.h"

// This section is responsible for the connection of the hardware of the circuit

LiquidCrystal_I2C lcd(0x27, 16, 2); // Create an instance of LiquidCrystal_I2C for the LCD
VendingMachine vendingMachine; // Create an instance of VendingMachine
Servo servo; // Create an instance of Servo for controlling the servo motor
unsigned long previousTime = 0; // Variable to store the previous time for interrupt handling
volatile int potentiometerValue = -1; // Volatile variable to store the potentiometer value

int main(void) {
  init();
  initialize();

  while (true) {
    cycle();
  }
}

// Initialize the hardware and setup
void initialize() {
  lcd.init(); // Initialize the LCD display
  lcd.backlight(); // Turn on the LCD backlight
  Serial.begin(9600); // Initialize the serial communication
  setupPinChangeInterrupt(); // Setup pin change interrupt for button input
  setupADC(); // Setup ADC for potentiometer input
  servo.attach(3); // Attach the servo motor to pin 3
  servo.write(0); // Set the initial position of the servo to 0 degrees
  setupSwitches(); // Setup switches as inputs with internal pull-up resistors
  setupLED(); // Setup the LED pin as output
}

// Perform a single cycle of the vending machine operation
void cycle() {
  vendingMachine.printToDisplay(lcd); // Update the display
  vendingMachine.handleInputs(); // Handle user inputs
}

// Setup pin change interrupt for handling button input
void setupPinChangeInterrupt() {
  PCICR |= (1 << PCIE0); // Enable pin change interrupt for PCINT7:0
  PCMSK0 |= (1 << PCINT0); // Enable pin change interrupt for PCINT0 (button input)
}

// Setup ADC for reading the potentiometer input
void setupADC() {
  ADCSRA |= (1 << ADEN) | (1 << ADIE); // Enable ADC and ADC interrupt
  ADMUX = 0; // Use ADC0 as the analog input channel
  ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // Set ADC prescaler to 128
  ADCSRA |= (1 << ADSC); // Start the first ADC conversion
}

// Setup switches as inputs with internal pull-up resistors
void setupSwitches() {
  DDRB &= ~(0b00111100); // Set switches as input
  PORTB |= (0b00111100); // Enable internal pull-up resistors for switches
}

// Setup LED pin as output
void setupLED() {
  DDRD |= (1 << DDD4); // Set LED pin as output
}

// Interrupt Service Routine for pin change interrupt
ISR(PCINT0_vect) {
  unsigned long currentTime = millis();

  if (currentTime - previousTime > 500) {
    vendingMachine.handleInterrupt(); // Handle the button interrupt
    previousTime = currentTime;
  }
}

// Interrupt Service Routine for ADC conversion complete interrupt
ISR(ADC_vect) {
  potentiometerValue = ADC; // Read the ADC value
  ADCSRA |= (1 << ADSC); // Start the next ADC conversion
}

// This section is responsible for handling the timer timeout

// Interrupt Service Routine for Timer2 compare match A interrupt
ISR(TIMER2_COMPA_vect) {
  vendingMachine.onTimeout(); // Handle the timer timeout
}