/*
 * LEIGH:plc
 * Copyright 2022 Alex Leigh
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <string.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_netif.h>
#include <esp_eth.h>
#include <esp_event.h>
#include <esp_log.h>
#include <driver/gpio.h>
#include <sdkconfig.h>

#include "lcd.h"
#include "json.h"

#define MSG_LINK_DOWN "Link Down"
#define MSG_DHCP_DISCOVER "DHCP: Discover"

// Expressif: MDC 23, MDIO 18, RST 5, ADDR 1, IP101 1
// wESAP32: MDC 16, MDIO 17, RST -1, ADDR 0, RTL8201

#ifdef BOARD_WESP32
#define CONFIG_EXAMPLE_ETH_MDC_GPIO 16
#define CONFIG_EXAMPLE_ETH_MDIO_GPIO 17
#define CONFIG_EXAMPLE_ETH_PHY_RST_GPIO -1
#define CONFIG_EXAMPLE_ETH_PHY_ADDR 0
#elif BOARD_ESPRESSIF_DEV
#define CONFIG_EXAMPLE_ETH_MDC_GPIO 23
#define CONFIG_EXAMPLE_ETH_MDIO_GPIO 18
#define CONFIG_EXAMPLE_ETH_PHY_RST_GPIO 5
#define CONFIG_EXAMPLE_ETH_PHY_ADDR 1
#endif

static const char *TAG = "eth";
static void ethF_reset_msgs();

static cJSON *ethG_field_ip;
static cJSON *ethG_field_netmask;
static cJSON *ethG_field_mac;
static cJSON *ethG_field_gw;

/** Event handler for Ethernet events */
static void eth_event_handler(void *arg, esp_event_base_t event_base,
                              int32_t event_id, void *event_data)
{
    uint8_t mac_addr[6] = {0};
    /* we can get the ethernet driver handle from event data */
    esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data;

    switch (event_id)
    {
    case ETHERNET_EVENT_CONNECTED:

        /*
        tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_ETH);
        tcpip_adapter_ip_info_t eth;
        eth.ip.addr = ipaddr_addr("192.168.3.10");
        eth.netmask.addr = ipaddr_addr("255.255.255.0");
        eth.gw .addr=ipaddr_addr("192.168.3.1");
        tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_ETH,(const char*) &eth);
        */

        esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr);

        ESP_LOGI(TAG, "Ethernet Link Up");
        ESP_LOGI(TAG, "Ethernet HW Addr %02x:%02x:%02x:%02x:%02x:%02x",
                 mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);

        char buf[32];
        snprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x",
                 mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
        lcdFlock_screen();
        jsonFset(ethG_field_mac, ELE_FIELD_VALUE, buf);
        lcdFunlock_screen();

        break;
    case ETHERNET_EVENT_DISCONNECTED:
        ESP_LOGI(TAG, "Ethernet Link Down");
        ethF_reset_msgs();
        lcdFlock_screen();
        jsonFset(ethG_field_ip, ELE_FIELD_VALUE, MSG_LINK_DOWN);
        lcdFunlock_screen();
        break;
    case ETHERNET_EVENT_START:
        ESP_LOGI(TAG, "Ethernet Started");
        break;
    case ETHERNET_EVENT_STOP:
        ESP_LOGI(TAG, "Ethernet Stopped");
        lcdFlock_screen();
        ethF_reset_msgs();
        jsonFset(ethG_field_ip, ELE_FIELD_VALUE, MSG_LINK_DOWN);
        lcdFunlock_screen();
        break;
    default:
        break;
    }
}

static void ethF_reset_msgs()
{
    //  cJSON_DeleteItemFromObject(ethG_field_ip, ELE_FIELD_VALUE);
    //   cJSON_DeleteItemFromObject(ethG_field_mac, ELE_FIELD_VALUE);
    cJSON_DeleteItemFromObject(ethG_field_netmask, ELE_FIELD_VALUE);
    cJSON_DeleteItemFromObject(ethG_field_gw, ELE_FIELD_VALUE);
}

/** Event handler for IP_EVENT_ETH_GOT_IP */
static void got_ip_event_handler(void *arg, esp_event_base_t event_base,
                                 int32_t event_id, void *event_data)
{
    ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
    const esp_netif_ip_info_t *ip_info = &event->ip_info;

    char buf[17];

    lcdFlock_screen();
    snprintf(buf, sizeof(buf), IPSTR, IP2STR(&ip_info->ip));
    jsonFset(ethG_field_ip, ELE_FIELD_VALUE, buf);

    snprintf(buf, sizeof(buf), IPSTR, IP2STR(&ip_info->netmask));
    jsonFset(ethG_field_netmask, ELE_FIELD_VALUE, buf);

    snprintf(buf, sizeof(buf), IPSTR, IP2STR(&ip_info->gw));
    jsonFset(ethG_field_gw, ELE_FIELD_VALUE, buf);
    lcdFunlock_screen();

    ESP_LOGI(TAG, "Ethernet Got IP Address");
    ESP_LOGI(TAG, "~~~~~~~~~~~");
    ESP_LOGI(TAG, "ETHIP:" IPSTR, IP2STR(&ip_info->ip));
    ESP_LOGI(TAG, "ETHMASK:" IPSTR, IP2STR(&ip_info->netmask));
    ESP_LOGI(TAG, "ETHGW:" IPSTR, IP2STR(&ip_info->gw));
    ESP_LOGI(TAG, "~~~~~~~~~~~");
}

void ethFinit()
{
    esp_netif_init();

    cJSON *netpanel = cJSON_CreateObject();
    cJSON *fields = cJSON_CreateArray();
    cJSON_AddItemToObject(netpanel, ELE_PANEL_CONTENT, fields);
    cJSON_AddStringToObject(netpanel, ELE_PANEL_TITLE, "Network");

    ethG_field_ip = cJSON_CreateObject();
    cJSON_AddStringToObject(ethG_field_ip, ELE_FIELD_TITLE, "IP");
    cJSON_AddStringToObject(ethG_field_ip, ELE_FIELD_VALUE, MSG_DHCP_DISCOVER);
    cJSON_AddItemToArray(fields, ethG_field_ip);

    ethG_field_mac = cJSON_CreateObject();
    cJSON_AddStringToObject(ethG_field_mac, ELE_FIELD_TITLE, "MAC");
    cJSON_AddStringToObject(ethG_field_ip, ELE_FIELD_VALUE, MSG_LINK_DOWN);
    cJSON_AddItemToArray(fields, ethG_field_mac);

    ethG_field_netmask = cJSON_CreateObject();
    cJSON_AddStringToObject(ethG_field_netmask, ELE_FIELD_TITLE, "Netmask");
    cJSON_AddItemToArray(fields, ethG_field_netmask);

    ethG_field_gw = cJSON_CreateObject();
    cJSON_AddStringToObject(ethG_field_gw, ELE_FIELD_TITLE, "GW");
    cJSON_AddItemToArray(fields, ethG_field_gw);

    int ret = lcdFlock_screen();
    assert(ret == 1);
    cJSON *screen = lcdFget_screen();
    assert(screen != NULL);
    cJSON *panels = cJSON_GetObjectItem(screen, ELE_SCREEN_CONTENT);
    assert(panels != NULL);
    cJSON_AddItemToArray(panels, netpanel);
    lcdFunlock_screen();

    ethF_reset_msgs();

    ESP_ERROR_CHECK(esp_event_loop_create_default());
    // ESP_ERROR_CHECK(tcpip_adapter_set_default_eth_handlers());
    ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, NULL));
    
    /* Create new default instance of esp-netif for Ethernet */
    esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH();
    esp_netif_t *eth_netif = esp_netif_new(&netif_cfg);

    eth_esp32_emac_config_t emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
    eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
    eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
    phy_config.phy_addr = CONFIG_EXAMPLE_ETH_PHY_ADDR;
    phy_config.reset_gpio_num = CONFIG_EXAMPLE_ETH_PHY_RST_GPIO;

    emac_config.smi_mdc_gpio_num  = CONFIG_EXAMPLE_ETH_MDC_GPIO;
    emac_config.smi_mdio_gpio_num = CONFIG_EXAMPLE_ETH_MDIO_GPIO;
    esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&emac_config, &mac_config);

#ifdef BOARD_WESP32
    esp_eth_phy_t *phy = esp_eth_phy_new_rtl8201(&phy_config);
#elif BOARD_ESPRESSIF_DEV
    esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config);
#endif
    esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy);
    esp_eth_handle_t eth_handle = NULL;
    ESP_ERROR_CHECK(esp_eth_driver_install(&config, &eth_handle));
    /* attach Ethernet driver to TCP/IP stack */
    ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handle)));
    ESP_ERROR_CHECK(esp_eth_start(eth_handle));
}