/*
 * MCINM.PL
 * https://mcinm.pl..
 *
 * interkom esp32 - serwer
 *
 */

#include <stdint.h>
#include <stdio.h>
#include <inttypes.h>
#include "driver/i2s_common.h"
#include "driver/i2s_types.h"
#include "esp_err.h"
#include "hal/i2s_types.h"
#include "lwip/inet.h"
#include "lwip/timeouts.h"
#include "portmacro.h"
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_netif.h"
#include "esp_wifi.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "esp_log.h"

#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>
#include <sys/_timeval.h>

#include "driver/i2s_std.h"
#include "driver/gpio.h"



#define SSID ""
#define PASSWD ""

#define SERVER_IP "192.168.0.7"
#define NETMASK "255.255.255.0"
#define GATEWAY "192.168.0.1"
#define SERVER_PORT 8876

#define SAMPLE_BUFFER_SIZE 128
#define SAMPLE_RATE 6000
#define BIT_RATE 32


#define GPIO_BCLK GPIO_NUM_3
#define GPIO_DIN GPIO_NUM_2
#define GPIO_WS GPIO_NUM_1
#define GPIO_DOUT GPIO_NUM_5


static i2s_chan_handle_t                rx_chan;
static i2s_chan_handle_t                tx_chan;


static const char *TAG = "mcinm_mikro_udp_server";


void wifi_init(void)
{
    nvs_flash_init();
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    
    esp_netif_t *netif_handle = esp_netif_create_default_wifi_sta();
    esp_netif_set_hostname(netif_handle, "MCINM_SERVER");
    
	esp_netif_ip_info_t ip;
    memset(&ip, 0 , sizeof(esp_netif_ip_info_t));
    ip.ip.addr = ipaddr_addr(SERVER_IP);
    ip.netmask.addr = ipaddr_addr(NETMASK);
    ip.gw.addr = ipaddr_addr(GATEWAY);

	esp_netif_dhcpc_stop(netif_handle); // zatrzymanie dhcp
	esp_netif_set_ip_info(netif_handle, &ip);


    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    
    wifi_config_t conf = {
        .sta = {
            .ssid = SSID,
            .password = PASSWD,
        },
    };
    
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    
    uint8_t mac[6] = {0x70, 0xB1, 0xD7, 0x6A, 0x02, 0x01};
    esp_wifi_set_mac(WIFI_IF_STA, mac);
    
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &conf));
    ESP_ERROR_CHECK(esp_wifi_start());
    ESP_ERROR_CHECK(esp_wifi_connect());
}



static void i2s_init(void) {

	i2s_chan_config_t i2s_channel_cfg;
	i2s_channel_cfg.id = I2S_NUM_0;
	i2s_channel_cfg.role = I2S_ROLE_MASTER;
	i2s_channel_cfg.dma_desc_num = 4;
	i2s_channel_cfg.dma_frame_num = 100,
	i2s_channel_cfg.allow_pd = false;
	i2s_channel_cfg.intr_priority = 0;

	i2s_new_channel(&i2s_channel_cfg, &tx_chan, &rx_chan);

	i2s_std_config_t i2s_cfg;
	i2s_std_clk_config_t clk_config = {
		.clk_src = I2S_CLK_SRC_DEFAULT,
		.sample_rate_hz = SAMPLE_RATE,
		.mclk_multiple = I2S_MCLK_MULTIPLE_192,
	};

	i2s_std_slot_config_t slot_config = {
		.data_bit_width = 32,
		.slot_bit_width = I2S_SLOT_BIT_WIDTH_32BIT,
		.slot_mode = I2S_SLOT_MODE_MONO,
		.slot_mask = I2S_STD_SLOT_LEFT,
		.ws_width = 32,
		.ws_pol = false,
		.bit_shift = true,
	};

	i2s_std_gpio_config_t i2s_gpio_config = {
		.mclk = I2S_GPIO_UNUSED,
		.bclk = GPIO_BCLK,
		.ws = GPIO_WS,
		.dout = GPIO_DOUT,
		.din = GPIO_DIN,
		.invert_flags = {
			.bclk_inv = false,
			.mclk_inv = false,
			.ws_inv = false,
		},
	};

	i2s_cfg.clk_cfg = clk_config;
	i2s_cfg.slot_cfg = slot_config;
	i2s_cfg.gpio_cfg = i2s_gpio_config;

	ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_chan, &i2s_cfg));
	ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_chan, &i2s_cfg));
	ESP_ERROR_CHECK(i2s_channel_enable(tx_chan));
	ESP_ERROR_CHECK(i2s_channel_enable(rx_chan));
}





void app_main(void)
{
  printf("MCINM - I2S Intercom UDP Server!\n");
  
  wifi_init();

  wifi_ap_record_t ap_info;
  esp_err_t ret;
  do { ret = esp_wifi_sta_get_ap_info(&ap_info); sleep(1);}
  while (ret != ESP_OK);

  i2s_init();

  struct sockaddr_in serverAddress;
  serverAddress.sin_addr.s_addr = INADDR_ANY;
  serverAddress.sin_family = AF_INET;
  serverAddress.sin_port = htons(SERVER_PORT);
    

  int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  if (s < 0 ) {
    ESP_LOGE(TAG, "Nie mozna stworzyc socketa: errno %d", errno);
    esp_restart();
  }

  // Ustawiamy timeout recvfrom
  struct timeval timeout;
  timeout.tv_sec = 2;
  timeout.tv_usec = 0;

  setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
  
  int err = bind(s, (struct sockaddr*)&serverAddress, sizeof(serverAddress));
  if (err < 0) {
    ESP_LOGE(TAG, "Nie udało się zbindować socketa: errno %d", errno);
    esp_restart();
  }
  ESP_LOGI(TAG, "Socket zbindowany na porcie: %d", SERVER_PORT);

  struct sockaddr_storage source_addr;
  socklen_t socklen = sizeof(source_addr);

  int32_t i2s_bufor_rx[128];
  int32_t i2s_bufor_tx[128];

    while (1) {
      int len = recvfrom(s, i2s_bufor_rx, sizeof(i2s_bufor_rx), 0, (struct sockaddr*)&source_addr,&socklen);
      if (len < 0) {
        ESP_LOGE(TAG, "Timeout: errno %d", errno);
       }

      else {

        // Wysyłanie danych w ramach odpowiedzi do klienta
        size_t bytes_read = 0;
        i2s_channel_read(rx_chan, i2s_bufor_tx, 128, &bytes_read, 10);
        int samples_read = bytes_read / sizeof(int);
        int err = sendto(s, (uint8_t*)i2s_bufor_tx, samples_read *sizeof(int), 0, (struct sockaddr *)&source_addr, sizeof(source_addr));

        // Odtwarzanie wcześniej odebranych danych od klienta
        for (uint8_t i=0; i<len; i++) {

          i2s_bufor_rx[i] = i2s_bufor_rx[i]*40; // wzmocnienie
         }
        i2s_channel_write(tx_chan, i2s_bufor_rx, len, NULL, 10);

      }
   }
}
