#include "GenericQueue.h"
#include "ADebouncer.h"

#define DEBOUNCE_PERIOD 10  // debounce time in milliseconds

typedef struct {
  String name;              // Order name
  uint32_t cookingTime;     // Cooking time in milliseconds
} Order;

const uint8_t numberOfLists = 4;  // The number of order in list
const Order orderLists[numberOfLists] = {
  {"Potato  ", 5000}, // 5 seconds
  {"Sandwich", 4000}, // 4 seconds
  {"Burger  ", 3000}, // 3 seconds
  {"Chicken ", 2000}  // 2 seconds
};

// Define the order led pin
const uint8_t orderLedPins[numberOfLists] = {5, 4, 3, 2};

// Define the order button pin
const uint8_t orderButtonPins[numberOfLists] = {9, 8, 7, 6};

ADebouncer debouncers[numberOfLists];

// Define the maximum of queues
const size_t numberOfQueues = 5;
// Initializes a new queue of the numbers
// that are empty and have the specified 5-capacity.
GenericQueue<uint8_t> queues(numberOfQueues);   //

// Declare the start time (in milliseconds).
uint32_t startTime;

void setup() {
  Serial.begin(115200);

  // Set the Buttons pinmode, LEDs pinmode, and debouncer mode.
  for (uint8_t index = 0; index < numberOfLists; index ++ ) {
    pinMode(orderButtonPins[index], INPUT_PULLUP);
    pinMode(orderLedPins[index], OUTPUT);
    debouncers[index].mode(INSTANT, DEBOUNCE_PERIOD, true);
  }

  // Add the callback function on state changed.
  queues.onStateChanged(OnStateChanged);
}

void loop () {
  uint32_t currTime = millis();
  DynamicQueue();
  if (!queues.isEmpty()) {
    uint32_t elapsedTime = currTime - startTime;        // Calculate the elapsed time.
    uint8_t index = queues.peek();                      // Get a current index number.
    digitalWrite(orderLedPins[index], HIGH);
    if (elapsedTime >= orderLists[index].cookingTime) { // If the cooking time is done,
      queues.dequeue();                                 // dequeue.
      startTime = currTime;
      if (!queues.isEmpty()) {
        Cooking();
      }
    }
  }
}

void DynamicQueue() {
  for (uint8_t index = 0; index < numberOfLists; index ++ ) {
    debouncers[index].debounce(digitalRead(orderButtonPins[index]));
    if (debouncers[index].falling()) {
      if (queues.count == 0 ) {
        startTime = millis();
        Cooking();
      }
      queues.enqueue(index);
    }
  }
}

void OnStateChanged(QueueEventArgs e, uint8_t order) {
  switch (e.state) {
    case DEQUEUE:
      digitalWrite(orderLedPins[order], LOW);
      Serial.print("the Order:\t");
      Serial.print(orderLists[order].name);
      Serial.println("\tDone...");
      break;
    case ENQUEUE:
      Serial.print("New Order:\t");
      Serial.print(orderLists[order].name);
      Serial.print(" \tCooking Time:");
      Serial.println(orderLists[order].cookingTime);
      break;
  }
}

void Cooking() {
  uint8_t index = queues.peek();  // Get a current index number.
  Serial.print("The Order:\t");
  Serial.print(orderLists[index].name);
  Serial.println("\tCooking...");
}