#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "pico/stdlib.h"
#include "car_queue.h"

//-------------------------------------------------------------------
// Defines and macros
//-------------------------------------------------------------------
// Outputs
#define EAST_R_PIN  ( 3U)
#define EAST_Y_PIN  ( 4U)
#define EAST_G_PIN  ( 5U)
#define NORTH_R_PIN ( 6U)
#define NORTH_Y_PIN ( 7U)
#define NORTH_G_PIN ( 8U)
#define NORTH_A_PIN ( 9U)
#define WEST_R_PIN  (18U)
#define WEST_Y_PIN  (17U)
#define WEST_G_PIN  (16U)
#define WEST_A_PIN  (21U)
#define NEW_CAR_PIN (15U)

//-------------------------------------------------------------------
// Enumerations and structs
//-------------------------------------------------------------------
typedef enum {
  // Outputs
  GPIO_OUT_EAST_R,
  GPIO_OUT_EAST_Y,
  GPIO_OUT_EAST_G,
  GPIO_OUT_NORTH_R,
  GPIO_OUT_NORTH_Y,
  GPIO_OUT_NORTH_G,
  GPIO_OUT_NORTH_A,
  GPIO_OUT_WEST_R,
  GPIO_OUT_WEST_Y,
  GPIO_OUT_WEST_G,
  GPIO_OUT_WEST_A,
  GPIO_IN_NEW_CAR,
  GPIO_COUNT  // Must be last
} gpioName_E;

// GPIO initialization table struct
typedef struct {
  uint pin;
  uint direction;
} gpioInit_S;

// State defintions enum
typedef enum {
  STATE_S0,
  STATE_S1,
  // Add more states here

  STATE_COUNT // Must be last
} stateName_E;

// Output value struct
typedef struct {
  bool ER; // East Red
  bool EY; // East Yellow
  bool EG; // East Green
  bool NR; // North Red
  bool NY; // North Yellow
  bool NG; // North Green
  bool NA; // North Arrow
  bool WR; // West Red
  bool WY; // West Yellow
  bool WG; // West Green
  bool WA; // West Arrow
} stateOutputs_S;

//-------------------------------------------------------------------
// Global tables and data types
//-------------------------------------------------------------------

// OutputTable
// Each row contains a specific state for { ER,EY,EG,NR,NY,NG,NA,WR,WY,WG,WA }
// For example: 
// All reds -> { true, false, false, true, false, false, false, true, false, false, false }
static const stateOutputs_S gOutputTable[STATE_COUNT] = {
//     ER,    EY,    EG,    NR,    NY,    NG,    NA,    WR,    WY,    WG,    WA
  { false, false,  false,  false, false, false, false, false, false,  false, false }, // S0
  { false,  true,  false,  false, true,  false, false, false, true,   false, false }, // S1
};

static const gpioInit_S gpioList[GPIO_COUNT] = {
  { .pin = EAST_R_PIN,  .direction = GPIO_OUT },
  { .pin = EAST_Y_PIN,  .direction = GPIO_OUT },
  { .pin = EAST_G_PIN,  .direction = GPIO_OUT },
  { .pin = NORTH_R_PIN, .direction = GPIO_OUT },
  { .pin = NORTH_Y_PIN, .direction = GPIO_OUT },
  { .pin = NORTH_G_PIN, .direction = GPIO_OUT },
  { .pin = NORTH_A_PIN, .direction = GPIO_OUT },
  { .pin = WEST_R_PIN,  .direction = GPIO_OUT },
  { .pin = WEST_Y_PIN,  .direction = GPIO_OUT },
  { .pin = WEST_G_PIN,  .direction = GPIO_OUT },
  { .pin = WEST_A_PIN,  .direction = GPIO_OUT },
  { .pin = NEW_CAR_PIN, .direction = GPIO_IN  },
};

static stateName_E gState; // Main FSM state tracker
static uint32_t tick_count = 0;
static uint32_t traffic_tick = 0;

//-------------------------------------------------------------------
// Private function definitions
//-------------------------------------------------------------------

// This function initializes all used GPIOs in this project
static void gpioSetup(void)
{
  for (size_t i = 0U; i < GPIO_COUNT; i++)
  {
    // Initialize the GPIOs here
    gpio_init(gpioList[i].pin);
    if (GPIO_OUT == gpioList[i].direction)
    {
      gpio_set_dir(gpioList[i].pin, GPIO_OUT);
    }
    else
    {
      gpio_set_dir(gpioList[i].pin, GPIO_IN);
      gpio_pull_up(gpioList[i].pin);
    }
  }
}

//-------------------------------------------------------------------
// State machine framework functions
//-------------------------------------------------------------------
static void updateOutputs(const stateName_E currentState)
{
  if (currentState < STATE_COUNT)
  {
    stateOutputs_S * const pOutput = &gOutputTable[currentState];

    // Now update all the lights accordingly
    gpio_put(gpioList[GPIO_OUT_EAST_R ].pin, pOutput->ER);
    gpio_put(gpioList[GPIO_OUT_EAST_Y ].pin, pOutput->EY);
    gpio_put(gpioList[GPIO_OUT_EAST_G ].pin, pOutput->EG);
    gpio_put(gpioList[GPIO_OUT_NORTH_R].pin, pOutput->NR);
    gpio_put(gpioList[GPIO_OUT_NORTH_Y].pin, pOutput->NY);
    gpio_put(gpioList[GPIO_OUT_NORTH_G].pin, pOutput->NG);
    gpio_put(gpioList[GPIO_OUT_NORTH_A].pin, pOutput->NA);
    gpio_put(gpioList[GPIO_OUT_WEST_R ].pin, pOutput->WR);
    gpio_put(gpioList[GPIO_OUT_WEST_Y ].pin, pOutput->WY);
    gpio_put(gpioList[GPIO_OUT_WEST_G ].pin, pOutput->WG);
    gpio_put(gpioList[GPIO_OUT_WEST_A ].pin, pOutput->WA);
  }
}

static void checkNewCarPressed(const carQueue_S *carQueue) 
{
  // False means pressed, true means unpressed
  bool button_state = gpio_get(gpioList[GPIO_IN_NEW_CAR].pin);

  /* 
   Check if the button is pressed or not
   
   Note: This must be a one time press! 
   This means if I keep pressing the button
   it should only count as a single press.

  */
  if (button_state == false)  // Note: Change this to handle a single press
  {
    // Transition to STATE_S1
    // This is a demo code, and must be changed
    gState = STATE_S1;

    // Add the code to add new car to queue here
  }
}

static stateName_E getNextStateFromQueue(stateName_E currentState, const carQueue_S *carQueue)
{
  switch (currentState) 
  {
    case STATE_S0:
      // Fill what S0 should do
      return STATE_S0;
    break;

    case STATE_S1:
      // Demo: Transition back to S0
      return STATE_S0;

    // Add more states
                      
    default:
      return STATE_COUNT;
  }
}

static bool tryPassCarFromQueue(stateName_E state, carQueue_S *queue) 
{

  if (isQueueEmpty(queue)) return false;
  bool carCanPass = false;
	
  // Get current direction of the first car of the queue
	// Add the code here
	
	// Check current state and set if the car can pass or not
  switch (state) 
  {
    case STATE_S0:
      break;

    // Add more states here (only the ones that let cars pass)

    default:
      break;
  }

  if (carCanPass) 
  {
    // Add code to dequeue the car here
    return true;
  }

  return false;
}


//-------------------------------------------------------------------
// Main application code starts here
//-------------------------------------------------------------------
int main() {

  printf("Traffic Light Controller");

  // One-time initialzation of global values
  gState = STATE_S0;

  // Call SDK and hardware initialization functions
  stdio_init_all();
  gpioSetup();

  // Initialize our car queue
  carQueue_S carQueue;
  initQueue(&carQueue);

  // Main infinite loop logic
  while (true) 
  {
    // Increment our tick
    tick_count +=1;

    // Add a new car to the queue each time button is pressed
    checkNewCarPressed(&carQueue);

    // Main processing every second
    if (tick_count - traffic_tick == 10)
    {
      traffic_tick = tick_count;

      // Get the next state depending on car in queue
      stateName_E nextState = getNextStateFromQueue(gState, &carQueue);

      // Update lights
      updateOutputs(gState);

      // Print current state and cars waiting on queue
      printf("State: S%i, ", gState);
      printQueue(&carQueue);

      // Try to get pass cars depending on lights state
      tryPassCarFromQueue(gState, &carQueue);

      // If nextState was STATE_COUNT, we stay in current state 
      if (nextState != STATE_COUNT)
      {
        gState = nextState;
      }
    }

    // Tick is 100ms
    sleep_ms(100);
  }
}
BOOTSELLED1239USBRaspberryPiPico©2020RP2-8020/21P64M15.00TTT
┌──┐ └──┘
──┐ ──┘
┌──┐ └──┘
┌─── └───
┌──┐ └──┘
East Straight
West Straight
West Left
North Left
North Right
New Car