/* GPIO Example

  https://www.programmersought.com/article/7556268640/
  https://lastminuteengineers.com/handling-esp32-gpio-interrupts-tutorial/
  https://techtutorialsx.com/2017/09/30/esp32-arduino-external-interrupts/
  https://www.lucadentella.it/en/2017/02/25/esp32-12-io-e-interrupts/
  https://github.com/lucadentella/esp32-tutorial/blob/master/07_gpio_interrupts/main/main.c
  https://techtutorialsx.com/2017/05/06/esp32-arduino-creating-a-task/
  https://savjee.be/2020/01/multitasking-esp32-arduino-freertos/



   This example code is in the Public Domain (or CC0 licensed, at your option.)
   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "freertos/semphr.h"


#ifdef __cplusplus
extern "C" {
#endif

#ifdef __cplusplus
}
#endif


// This TaskHandle will allow
TaskHandle_t task1Handle = NULL;


/**
   Brief:
   This test code shows how to configure gpio and how to use gpio interrupt.

   GPIO status:
   GPIO18: output
   GPIO19: output
   GPIO4:  input, pulled up, interrupt from rising edge and falling edge
   GPIO5:  input, pulled up, interrupt from rising edge.

   Test:
   Connect GPIO18 with GPIO4
   Connect GPIO19 with GPIO5
   Generate pulses on GPIO18/19, that triggers interrupt on GPIO4/5

*/

#define  CONFIG_LED_PIN GPIO_NUM_13

#define GPIO_OUTPUT_IO_0   GPIO_NUM_18
#define GPIO_OUTPUT_IO_1    GPIO_NUM_19
#define GPIO_OUTPUT_PIN_SEL  ((1ULL<<GPIO_OUTPUT_IO_0) | (1ULL<<GPIO_OUTPUT_IO_1))
#define GPIO_INPUT_IO_0     GPIO_NUM_4
#define GPIO_INPUT_IO_1     GPIO_NUM_2
#define GPIO_INPUT_PIN_SEL  ((1ULL<<GPIO_INPUT_IO_0) | (1ULL<<GPIO_INPUT_IO_1))
#define ESP_INTR_FLAG_DEFAULT 0

#define ESP_INTR_FLAG_DEFAULT 0
SemaphoreHandle_t xSemaphore = NULL;
bool led_status = false;


static xQueueHandle gpio_evt_queue = NULL;

static void IRAM_ATTR gpio_isr_handler(void* arg)
{
  uint32_t gpio_num = (uint32_t) arg;
  xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);


  // notify the button task
  ////////// xSemaphoreGiveFromISR(xSemaphore, NULL);
}

static void gpio_task_example(void* arg)
{
  uint32_t io_num;
  for (;;) {
    if (xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {
      printf("GPIO[%d] intr, val: %d\n", io_num, gpio_get_level((gpio_num_t)io_num));
    }
  }
}


// task that will react to button clicks
void button_task(void* arg) {
  printf("Button always in  pressed!\n");
  // infinite loop
  for (;;) {
    printf("Button always in look pressed!\n");
    // wait for the notification from the ISR
    if (xSemaphoreTake(xSemaphore, portMAX_DELAY) == pdTRUE) {
      printf("Button pressed!\n");
      led_status = !led_status;
      gpio_set_level(CONFIG_LED_PIN, led_status);
    }
  }
}

// task that will react to button clicks
void Task1(void* arg) {
  printf("Button task1  always in  pressed!\n");
  // infinite loop

  int cnt = 0;
  for (;;) {
    led_status = !led_status;
    gpio_set_level(CONFIG_LED_PIN, led_status);

    // int cnt = 0;
    // while (1) {
    printf("cnt: %d\n", cnt++);
    vTaskDelay(1000 / portTICK_RATE_MS);
    //vTaskDelay(500 / portTICK_PERIOD_MS);
    gpio_set_level(GPIO_OUTPUT_IO_0, cnt % 2);
    gpio_set_level(GPIO_OUTPUT_IO_1, cnt % 2);

    //  vTaskDelete( NULL );
  }

}

void anotherTask(void * parameter) {
  // Kill task1 if it's running
  if (task1Handle != NULL) {
    vTaskDelete(task1Handle);
  }
}



void setup()
{
  gpio_config_t io_conf;
  //disable interrupt
  io_conf.intr_type = (gpio_int_type_t)GPIO_PIN_INTR_DISABLE;
  //set as output mode
  io_conf.mode = GPIO_MODE_OUTPUT;
  //bit mask of the pins that you want to set,e.g.GPIO18/19
  io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;
  //disable pull-down mode
  io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; //0;
  //disable pull-up mode
  io_conf.pull_up_en =  GPIO_PULLUP_DISABLE; // 0;
  //configure GPIO with the given settings
  gpio_config(&io_conf);

  //interrupt of rising edge
  io_conf.intr_type = (gpio_int_type_t)GPIO_PIN_INTR_POSEDGE;
  //bit mask of the pins, use GPIO4/5 here
  io_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL;
  //set as input mode
  io_conf.mode = GPIO_MODE_INPUT;
  //enable pull-up mode
  io_conf.pull_up_en = GPIO_PULLUP_ENABLE; // 1;
  gpio_config(&io_conf);

  //change gpio intrrupt type for one pin
  gpio_set_intr_type(GPIO_INPUT_IO_0, GPIO_INTR_ANYEDGE);

  //create a queue to handle gpio event from isr
  gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
  //start gpio task
  xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL);

  //Create Semaphore
  xSemaphore = xSemaphoreCreateBinary();
  xTaskCreate(button_task, "button_task", 2048, NULL, 10, NULL);

  //install gpio isr service
  gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
  //hook isr handler for specific gpio pin
  gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);
  //hook isr handler for specific gpio pin
  gpio_isr_handler_add(GPIO_INPUT_IO_1, gpio_isr_handler, (void*) GPIO_INPUT_IO_1);

  //remove isr handler for gpio number.
  gpio_isr_handler_remove(GPIO_INPUT_IO_0);
  //hook isr handler for specific gpio pin again
  gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);

  //create tasks in cores testing
  xTaskCreate(Task1, "Task1", 2048,  NULL,    1, &task1Handle   );



}

void loop()
{
  delay(1000);

  /*
    int cnt = 0;
    while (1) {
      printf("cnt: %d\n", cnt++);
      vTaskDelay(1000 / portTICK_RATE_MS);
      gpio_set_level(GPIO_OUTPUT_IO_0, cnt % 2);
      gpio_set_level(GPIO_OUTPUT_IO_1, cnt % 2);
    }
  */
}

/*
  void IRAM_ATTR isr_lane(void* arg){
  uint32_t gpio_num = (uint32_t) arg;
  Serial.println( gpio_num );
  }

  void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);

    uint8_t pin = 8;
    gpio_config_t io_conf;
    io_conf.intr_type = (gpio_int_type_t)GPIO_PIN_INTR_DISABLE;
    io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
    io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
    //io_conf.pin_bit_mask = 0B00000000000000001000000000000000;  //works for pin 15
    //io_conf.pin_bit_mask = 0B00000000000000000000000000001000;  //works for pin 8
    io_conf.pin_bit_mask = 0B00000000000000000000000100000000;  //should work for pin 8
    io_conf.mode = GPIO_MODE_INPUT;
    gpio_config(&io_conf);
    gpio_set_intr_type((gpio_num_t)pin, GPIO_INTR_HIGH_LEVEL);
    gpio_install_isr_service(0);
    gpio_isr_handler_add((gpio_num_t)pin, isr_lane, (void*) pin);
  }
  ------------------



  #include <stdio.h>
  #include <stdlib.h>
  #include "freertos/FreeRTOS.h"
  #include "freertos/task.h"
  #include "freertos/queue.h"
  #include "driver/gpio.h"


  #define GPIO_4   4
  #define ESP_INTR_FLAG_DEFAULT 0 // Interrupt flag bit

  static xQueueHandle gpio_evt_queue = NULL;

  void gpio_task1(void)
  {
    for (int i = 5; i > 0; i--)
    {
        printf("task1: %d\n", i);
        vTaskDelay(500 / portTICK_RATE_MS);
    }
    return;
  }

  void gpio_task2(void)
  {
    for (int i = 5; i > 0; i--)
    {
        printf("task2: %d\n", i);
        vTaskDelay(500 / portTICK_RATE_MS);
    }
    return;
  }

  void gpio_isr_handler(void* arg)
  {
    uint32_t gpio_num = (uint32_t) arg;
    xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);

  }

  static void gpio_task_example(void* arg)
  {
    for( ;; )
    {
        // Task code goes here.
        uint32_t io_num;
        if(xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {
            printf("GPIO[%d] intr, val: %d\n", io_num, gpio_get_level(io_num));
        }
    }
  }

  void app_main(void)
  {
    // GPIO1
    gpio_config_t io_conf = {};                 // The new configuration GPIO pad Of gpio_config Structure of functional parameters
    io_conf.pin_bit_mask = (1ULL << GPIO_4);    // Set up GPIO4 The mask of is 1
    io_conf.mode = GPIO_MODE_INPUT;            // Set up GPIO4  For output mode
    io_conf.pull_up_en = 1;                     // Pull up
    io_conf.pull_down_en = 0;                   // No pull down
    io_conf.intr_type = GPIO_INTR_POSEDGE;      //GPIO4 The rising edge triggers the interrupt
    if (gpio_config(&io_conf) == ESP_OK)       // To configure GPIO4
        printf("gpio4_config succeed \n");
    else
        printf("gpio4_config failed \n");

    if (gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT) == ESP_OK)       // Set interrupt service
        printf("gpio_install_isr_service succeed \n");
    else
        printf("gpio_install_isr_service failed \n");

    if (gpio_isr_handler_add(GPIO_4, gpio_isr_handler, (void*)GPIO_4 ) == ESP_OK)       // Set interrupt handler
        printf("gpio_isr_handler_add succeed \n");
    else
        printf("gpio_isr_handler_add failed \n");

    gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
    xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL);

    while(1)
    {
        gpio_task1();
        gpio_task2();
    }
  }
*/