#include "freertos/ringbuf.h"

#define MSG1_DELAY 500
#define MSG2_DELAY 250

#define SLOCKER 1  // 0 to disable Serial Lock or 1 to enable it

// Pick one of those 3 possible ways to print messages
#define PRINT(msg)  mailBoxSend(msg)
//#define PRINT(msg)  print1by1(msg)
//#define PRINT(msg)  printCounted(msg)


xSemaphoreHandle sLock;
#if SLOCKER
#define SERIAL_LOCK()    do {} while (xSemaphoreTake(sLock, portMAX_DELAY) != pdPASS)
#define SERIAL_UNLOCK()  xSemaphoreGive(sLock)
#else
#define SERIAL_LOCK()
#define SERIAL_UNLOCK()
#endif

void print1by1(const char *str) {
  uint32_t s = strlen(str);
  SERIAL_LOCK();
  for (uint32_t i = 0; i < s; i++) {
    Serial.print(str[i]);
    delay(random(25));
  }
  Serial.println();
  SERIAL_UNLOCK();
}


// Use the RingBuffer as a Mail Box os ordered messages
RingbufHandle_t buf_handle = NULL;

void mailBoxSend(const char *msg) {
    //Send the message, including the '\0'
    UBaseType_t res =  xRingbufferSend(buf_handle, msg, strlen(msg) + 1, pdMS_TO_TICKS(1000));
    if (res != pdTRUE) {
        Serial.println("====>Failed to send item\n");
    }
}

void mailBoxTask(void *arg) {

  while(1) {
    //Receive an item from no-split ring buffer
    size_t item_size;
    // waits for ever until receive a message 
    char *item = (char *)xRingbufferReceive(buf_handle, &item_size, pdMS_TO_TICKS(1000));
    if (item != NULL) {
      // print it with the '\0'
      printCounted(item);
      // release the memory space in the RingBuffer
      vRingbufferReturnItem(buf_handle, (void *)item);
    } 
  }
}

void printCounted(const char *str) {
    static uint32_t counter = 0;
    char msg[256];
    sprintf(msg, "#%d [%s]", counter++, str);
    print1by1(msg);
}

void msg1Writer(void *arg) {
  (void) arg;
  while (1) {
    PRINT("TASK 1 -- 1111");
    delay(MSG1_DELAY);
  }
}

void msg2Writer(void *arg) {
  (void) arg;
  while (1) {
    PRINT("TASK 2 -- 2222");
    delay(MSG2_DELAY);
  }
}

void setup() {
  Serial.begin(115200);
  Serial.println("Printing in different times:");

  sLock = xSemaphoreCreateMutex();
  if(sLock == NULL) {
    while(1) {
      Serial.println("\n------------\nSerial Lock Failure\n------------\n");
      delay(10000);
    }
  }

  buf_handle = xRingbufferCreate(8192, RINGBUF_TYPE_NOSPLIT);
  if (buf_handle == NULL) {
      while(1) {
        Serial.println("Failed to create ring buffer\n");
        delay(10000);
      }
  }

  xTaskCreate(
    mailBoxTask    // Message Printer
    ,  "MailBox"   // A name just for humans
    ,  2048        // The stack size
    ,  NULL        // Task parameter - NONE
    ,  1           // Priority
    ,  NULL        // Task handle is not used here - simply pass NULL
    );

#if 1
  xTaskCreate(
    msg1Writer     // Specific Task Function for Message 1
    ,  "PRINT 1"   // A name just for humans
    ,  2048        // The stack size
    ,  NULL        // Task parameter - NONE
    ,  2           // Priority
    ,  NULL        // Task handle is not used here - simply pass NULL
    );

  xTaskCreate(
    msg2Writer     // Specific Task Function for Message 2
    ,  "PRINT 2"   // A name just for humans
    ,  2048        // The stack size
    ,  NULL        // Task parameter - NONE
    ,  2           // Priority
    ,  NULL        // Task handle is not used here - simply pass NULL
    );
#endif
}

void loop() {
  static unsigned long msg1Time_ms = 0;
  static unsigned long msg2Time_ms = 0;

  if (millis() > msg1Time_ms + MSG1_DELAY) {
    PRINT("Message 1 -- 1111");
    msg1Time_ms = millis();
  }

  if (millis() > msg2Time_ms + MSG2_DELAY) {
    PRINT("Message 2 -- 2222");
    msg2Time_ms = millis();
  }
}