/* data-type        size        description
   -------------------------------------------------------------
   boolean          8 bit       true/false
   byte             8 bit       0-255 unsigned number
   char             8 bit       -128 to 127 signed number
   unsigned char    8 bit       0-255 unsigned number
   word             16 bit      0-65535 unsigned number
   unsigned int     16 bit      0-65535 unsigned number
   int              16 bit      -32768 to 32767 signed number
   unsigned long    32 bit      0-4,294,967,295 unsigned number (usually for millis)
   long             32 bit      -2,147,483,648 to 2,147,483,647 signed number
   float            32 bit      -3.4028235E38 to 3.4028235E38 signed number
   uint8_t          8 bit       0-255 unsigned number
   int8_t           8 bit       -127 to 127 signed number
   uint16_t         16 bit      0-65,535 unsigned number
   int16_t          16 bit      -32,768 to 32,767 signed number
   uint32_t         32 bit      0-4,294,967,295 unsigned number
   int32_t          32 bit      -2,147,483,648 to 2,147,483,647 signed number
   uint64_t         64 bit      0-18,446,744,073,709,551,615 unsigned number
   int64_t          64 bit      -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 signed number
   -------------------------------------------------------------
   camelCase                   anything that changes
   snake_case                  variable names that are exclusive in a function
   Snake_Case                  class/struct exclusive variables/functions
   iNVERTEDcAMELcASE           outside code that is being accessed (e.g. database)
   SNake_CAse                  duplicate variables inside the case function (frequently used in library names)
   ALL_CAPS                    constant variable names or defines
   -------------------------------------------------------------
   by jediRick & RefreshMyMind
*/
#include <GyverOLED.h>
#define OLED_CLOCK_SPEED 999999ul
#define ARRAY_SIZE 41
float cpuArray[ARRAY_SIZE] = {};
float gpuArray[ARRAY_SIZE] = {};  // Add array for GPU usage
float ramArray[ARRAY_SIZE] = {};
GyverOLED<SSD1306_128x64, OLED_BUFFER> oled;
void setup() {
  Serial.begin(115200);
  setupOled();
  delay(500);
}
void loop() {
  getData();
  displayData();
}
void setupOled() {
  Wire.begin();
  Wire.setClock(OLED_CLOCK_SPEED);
  oled.init();
  oled.setScale(1);
  oled.clear();
  oled.setCursor(0, 0);
  oled.println("OLED ok!");
  oled.update();
}
void getData() {
  // Shift the old values to the left
  for (int i = 0; i < ARRAY_SIZE - 1; i++) {
    cpuArray[i] = cpuArray[i + 1];
    gpuArray[i] = gpuArray[i + 1];  // Shift GPU values
    ramArray[i] = ramArray[i + 1];
  }
  // Get new data from serial
  if (Serial.available()) {
    String receivedData = Serial.readStringUntil('\n');
    // Parse the received data
    int firstCommaIndex = receivedData.indexOf(',');
    int secondCommaIndex = receivedData.indexOf(',', firstCommaIndex + 1);
    if (firstCommaIndex != -1 && secondCommaIndex != -1) {  // Check if commas exist
      String cpuString = receivedData.substring(0, firstCommaIndex);
      String ramString = receivedData.substring(firstCommaIndex + 1, secondCommaIndex);
      String gpuString = receivedData.substring(secondCommaIndex + 1);
      // Convert strings to float
      float cpuValue = cpuString.toFloat();
      float gpuValue = gpuString.toFloat();  // Corrected parsing for GPU
      float ramValue = ramString.toFloat();  // Corrected parsing for RAM
      // Put new data into the last position in cpu, gpu, and ram arrays
      cpuArray[ARRAY_SIZE - 1] = cpuValue;
      gpuArray[ARRAY_SIZE - 1] = gpuValue;
      ramArray[ARRAY_SIZE - 1] = ramValue;
    }
  }
  delay(100);  // Add a small delay to allow the Python script to send the next data
}
void displayData() {  // draw cpu, gpu, and ram arrays
  oled.clear();
  oled.rect(0, 0, 42, 63, OLED_STROKE);
  oled.rect(43, 0, 84, 63, OLED_STROKE);  // Adjusted for GPU
  oled.rect(85, 0, 127, 63, OLED_STROKE);
  for (int i = 0; i < ARRAY_SIZE - 1; i++) {
    int y1 = map(cpuArray[i], 0, 100, 61, 3);
    int y2 = map(cpuArray[i + 1], 0, 100, 61, 3);
    oled.line(i + 1, y1, i + 2, y2);
  }
  for (int i = 0; i < ARRAY_SIZE - 1; i++) {
    int y1 = map(gpuArray[i], 0, 100, 61, 3);  // Corrected index for GPU
    int y2 = map(gpuArray[i + 1], 0, 100, 61, 3);  // Corrected index for GPU
    oled.line(i + 44, y1, i + 45, y2);  // Corrected position for GPU
  }
  for (int i = 0; i < ARRAY_SIZE - 1; i++) {
    int y1 = map(ramArray[i], 0, 100, 61, 3);
    int y2 = map(ramArray[i + 1], 0, 100, 61, 3);
    oled.line(i + 86, y1, i + 87, y2);
  }
  oled.setScale(1);
  oled.setCursor(2, 0);
  oled.print("CPU");
  oled.setCursor(45, 0);
  oled.print("GPU");
  oled.setCursor(87, 0);
  oled.print("RAM");
  oled.update();
}