#include <Arduino.h>
#line 1 "C:\\Users\\chris\\Qsync\\Projects\\Scoreboard\\ScoreboardNumbers\\ScoreboardNumbers.ino"
#include <WiFi.h>
#include <ArduinoJson.h>
#include <Wire.h>
#include <U8g2lib.h>
#include <U8x8lib.h>
#include <HTTPClient.h>
#include "objects.h"

U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE);  // High speed I2C

// Define pins for the 7-segment displays
const int digitPins[4] = {2, 3, 4, 5}; // Common anode pins
const int segmentPins[7] = {6, 7, 8, 9, 10, 11, 12}; // Segment pins (a, b, c, d, e, f, g)

// Define the 7-segment display patterns
const byte digitPatterns[10] = {
  B11111100, // 0
  B01100000, // 1
  B11011010, // 2
  B11110010, // 3
  B01100110, // 4
  B10110110, // 5
  B10111110, // 6
  B11100000, // 7
  B11111110, // 8
  B11110110  // 9
};

// Wi-Fi settings
const char* ssid = "study";
const char* password = "abcde54321";

const char* displayQueue = "dQ";
const char* jsonPoints = "points";

const int wifiLoopDelay = 5000 / portTICK_PERIOD_MS;
const int displayLoopDelay = 1000 / portTICK_PERIOD_MS;
const int httpPollDelay = 3000 / portTICK_PERIOD_MS;

//FreeRTOS Queue Handles
QueueHandle_t scoreQueue;

// FreeRTOS task handles
TaskHandle_t networkTaskHandle = NULL;
TaskHandle_t displayTaskHandle = NULL;
portMUX_TYPE critical = portMUX_INITIALIZER_UNLOCKED;

// FreeRTOS Event Handles
EventGroupHandle_t wifi_event_group;
const int CONNECTED_BIT = BIT0;

void networkTask(void * parameter);
void displayTask(void * parameter);

#line 55 "C:\\Users\\chris\\Qsync\\Projects\\Scoreboard\\ScoreboardNumbers\\ScoreboardNumbers.ino"
void setup();
#line 104 "C:\\Users\\chris\\Qsync\\Projects\\Scoreboard\\ScoreboardNumbers\\ScoreboardNumbers.ino"
void WiFiEvent(arduino_event_id_t event);
#line 256 "C:\\Users\\chris\\Qsync\\Projects\\Scoreboard\\ScoreboardNumbers\\ScoreboardNumbers.ino"
void displayNumber(int display, int digitValue);
#line 287 "C:\\Users\\chris\\Qsync\\Projects\\Scoreboard\\ScoreboardNumbers\\ScoreboardNumbers.ino"
void loop();
#line 55 "C:\\Users\\chris\\Qsync\\Projects\\Scoreboard\\ScoreboardNumbers\\ScoreboardNumbers.ino"
void setup() {
  try {
    // Initialize Serial port
    Serial.begin(115200);
    #ifdef DEBUG
    Serial.println("Starting");
    #endif
    //start display driver  
    u8g2.begin();
    u8g2.setFont(u8g2_font_ncenB08_tr);   // choose a suitable font
    
    #ifdef DEBUG
    Serial.println("end dispInit");
    #endif
    // Initialize the pins
    // for (int i = 0; i < 4; i++) {
    //   pinMode(digitPins[i], OUTPUT);
    //   digitalWrite(digitPins[i], HIGH); // Turn off all displays initially
    // }
    
    // for (int i = 0; i < 7; i++) {
    //   pinMode(segmentPins[i], OUTPUT);
    //   digitalWrite(segmentPins[i], LOW); // Turn off all segments initially
    // }
    WiFi.onEvent( WiFiEvent );
    WiFi.mode(WIFI_STA);
    WiFi.disconnect();

    #ifdef DEBUG
    Serial.println("end IO init");
    #endif
    scoreQueue = xQueueCreate(2, sizeof( Scores ) );
    wifi_event_group = xEventGroupCreate();

    // Start the network task
    xTaskCreatePinnedToCore(networkTask, "Network Task", 20000, NULL, 3, &networkTaskHandle, 1);
    
    // Start the display task
    xTaskCreatePinnedToCore(displayTask, "Display Task", 20000, NULL, 4, &displayTaskHandle, 1 );
    #ifdef DEBUG
    Serial.println("ended init");
    #endif
    vTaskStartScheduler();
  } catch(String err){
    Serial.print("SetupError: ");
    Serial.println(err);
  }
}

void WiFiEvent(WiFiEvent_t event)
{
  switch (event) {
    case SYSTEM_EVENT_STA_CONNECTED:
      Serial.println("Connected to access point");
      break;
    case SYSTEM_EVENT_STA_DISCONNECTED:
      Serial.println("Disconnected from WiFi access point");
      break;
    case SYSTEM_EVENT_AP_STADISCONNECTED:
      Serial.println("WiFi client disconnected");
      break;
    default: break;
  }
}

void networkTask(void * parameter) {
  try {
    while(true){
      while ( WiFi.status() != WL_CONNECTED )
      {
    #ifdef DEBUG
        Serial.println("disconnected, connecting");
    #endif
        WiFi.disconnect();
        WiFi.begin( ssid, password );
    #ifdef DEBUG
        Serial.println(" waiting on wifi connection" );
    #endif
        vTaskDelay( 4000 );
      }      

      while (WiFi.status() != WL_CONNECTED) {    
        xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
    #ifdef DEBUG
        Serial.println("Connecting to WiFi...");
    #endif
      }
      
      xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
    #ifdef DEBUG
      Serial.println("Connected to WiFi");
    #endif

      HTTPClient http;
      const String url = "https://192.168.1.141:7005/api/score/summary";

      for (;;) {
        if(WiFi.status() != WL_CONNECTED){
          continue;
        }
    #ifdef DEBUG
        Serial.println(url);
    #endif
        http.useHTTP10(true);
        http.begin(url.c_str());
        http.addHeader("x-api-key", "396A62D6-0192-4738-8F45-8AAF7BCFA3EC");
        int httpResponseCode = http.GET();

        DynamicJsonDocument doc(2048);
        if (httpResponseCode>0) { //if no game, what code?
    #ifdef DEBUG
          Serial.print("HTTP Response code: ");
          Serial.println(httpResponseCode);
    #endif
          deserializeJson(doc, http.getStream());
        } else {
          Serial.print("HTTP Req Error code: ");
          Serial.println(httpResponseCode);
          vTaskDelay(httpPollDelay);
          continue;
        }
        // Parse JSON
        Scores pts;
        pts.home = doc["home"].as<int>();
        pts.away = doc["away"].as<int>();
        pts.state = doc["state"].as<String>();

    #ifdef DEBUG
        Serial.print("Match State: ");
        Serial.print(doc["state"].as<String>());
        Serial.print("| Home: ");
        Serial.print(doc["home"].as<int>());
        Serial.print(" Away: ");
        Serial.println(doc["away"].as<int>());
    #endif
        
        http.end();
        // Send the number to the display task
        xQueueSend(scoreQueue, &pts, portMAX_DELAY);
        
        vTaskDelay(httpPollDelay); // Wait before fetching the next update
      }

      vTaskDelay(wifiLoopDelay);
    }
  }
  catch (String err){
    Serial.print("NetworkTaskError: ");
    Serial.println(err);
  }
}

void displayTask(void * parameter) {
  try {
    Scores matchScore;
    int wifiStateY = 10;
    int matchStateY = 35;
    int scoreY = 74-u8g2.getMaxCharHeight();
    while(true) {
      u8g2.clearBuffer();                   // clear the internal memory
      int bits = (xEventGroupGetBits( wifi_event_group));

      if (bits & CONNECTED_BIT) {
    #ifdef DEBUG
        Serial.println("Connected");
    #endif
        u8g2.drawStr(0,wifiStateY,"Connected! ;)");    // write something to the internal memory
      } else {
    #ifdef DEBUG
        Serial.println("Disconnected");
    #endif
        u8g2.drawStr(0,wifiStateY,"Not Connected! :(");    // write something to the internal memory
      }

      if (xQueueReceive(scoreQueue, &matchScore, portMAX_DELAY)) {
        // for (int i = 0; i < 4; i++) {
        //   displayNumber(i, number / (int)pow(10, 3 - i) % 10); // Display the number
        //   delay(5); // Delay to display the number
        // }
        u8g2.drawStr(0, matchStateY, matchScore.state.c_str());

        String scoreMsg = "Home " + String(matchScore.home) + " Away " + String(matchScore.away);
        u8g2.drawStr(0,scoreY, scoreMsg.c_str());
      }
      u8g2.sendBuffer();                    // transfer internal memory to the display
      
      vTaskDelay(displayLoopDelay);
      // if (xQueueReceive(scoreQueue, &number, portMAX_DELAY)) {
      //   for (int i = 0; i < 4; i++) {
      //     displayNumber(i, number / (int)pow(10, 3 - i) % 10); // Display the number
      //     delay(5); // Delay to display the number
      //   }
      // }
    }
  } catch (String err){
    Serial.print("DisplayLoopError: ");
    Serial.println(err);
  }
}

// Function to display a number on a specific 7-segment display
void displayNumber(int display, int digitValue) {
  for (int i = 0; i < 7; i++) {
    if (bitRead(digitPatterns[digitValue], i)) {
      digitalWrite(segmentPins[i], HIGH); // Turn on the segment
    } else {
      digitalWrite(segmentPins[i], LOW); // Turn off the segment
    }
  }
  
  digitalWrite(digitPins[display], LOW); // Turn on the display
}

// String getStateString(GameState state){
//   switch(state){
//     case GameState::WaitingForStart:
//       return "Pending";
//     case GameState::FirstHalf:
//       return "First Half";
//     case GameState::SecondHalf:
//       return "Second Half";
//     case GameState::HalfTime:
//       return "Half Time";
//     case GameState::Finished:
//       return "Full Time";
//     case GameState::None:
//       return "Not Configured";
//     default:
//       "Other";
//   }
// }

void loop() {
  // Empty loop, tasks handle everything
}
