/**
 * @file tkl_wired.c
 * @brief this file was auto-generated by tuyaos v&v tools, developer can add implements between BEGIN and END
 *
 * @warning: changes between user 'BEGIN' and 'END' will be keeped when run tuyaos v&v tools
 *           changes in other place will be overwrited and lost
 *
 * @copyright Copyright 2020-2021 Tuya Inc. All Rights Reserved.
 *
 */

// --- BEGIN: user defines and implements ---
#include "tkl_wired.h"
#include "tuya_error_code.h"
#include "native_debug.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <stdio.h>

#define ROUTE_PATH "/proc/net/route"

static char *net_utils_space_trim( char *str )
{
    int i = 0;  
    int count = 0;
    int size = 0;

    char *tmp  = NULL;
    if ( !str ) {
        return NULL;
    }

    size = strlen( str );
    tmp = str;
    
    while ( *str == ' ' || (unsigned char)(*str - 9) <= (13 - 9) ) {
        str++;        
        count++;
    }
    
    if ( count ) {
        memmove( tmp, str, size - count );
        memset( tmp + ( size - count ), 0x0 , size - count );
    }
      
    return (char *) str;    
}

//only find first devname
static int net_utils_get_devname( char *devname, int size )
{
    if (!devname || size <= 0) {
        LOGW("%s %d: arg err\n", __func__, __LINE__);
        return -1;
    }
    // Android 网络接口通常为 wlan0 或 eth0（USB 网络共享）
    const char *dev_candidates[] = {"wlan0", "eth0", "rndis0"};
    for (int i = 0; i < sizeof(dev_candidates)/sizeof(dev_candidates[0]); i++) {
        const char *dev = dev_candidates[i];
        if (strlen(dev) < size) {
            strncpy(devname, dev, size);
            return 0;
        }
    }
    return -1;
}

static int net_utils_check_cable( const char *ifname, int *plugged )
{

    int sk = -1;
    struct ifreq ifr; 
    int flag = IFF_UP;
    int ret = -1;

    if ( !ifname ) {
        LOGW( "%s %d: ifname is null\n", __func__, __LINE__ );
        return -1;
    }

    sk = socket(AF_INET, SOCK_DGRAM, 0);
    if ( sk < 0 ) {
        LOGW( "%s %d: failed to create sk\n", __func__, __LINE__ );
        return -1;
    }

    memset( &ifr, 0, sizeof( ifr ) );
    strncpy( ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1 );
    *plugged = 1;

//    ret = ioctl( sk, SIOCGIFFLAGS, &ifr );
//    if ( ret < 0 ) {
//        LOGW( "%s %d: failed to get iterface info \n", __func__, __LINE__ );
//        goto exit_end;
//
//    }
//
//    if ( ifr.ifr_flags & IFF_RUNNING ) {
//        //LOGW( "%s %d: %s running \n", __func__, __LINE__, ifname );
//        *plugged = 1;
//
//    }
//    else {
//        //LOGW( "%s %d: %s not running \n", __func__, __LINE__, ifname );
//        *plugged = 0;
//    }

exit_end:
    if ( sk ) {
        close( sk );
    }
    return ret;
}

TKL_WIRED_STATUS_CHANGE_CB status_cb;
/**
 * @brief  set the status change callback
 *
 * @param[in]   cb: the callback when link status changed
 *
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
static void *link_status_thread(void *arg)
{
    TKL_WIRED_STAT_E status;
    int old_status = -1;

    /* Circulate detection of network cable plugging and
       unplugging status, and report status when changing
    */
    while(1) {
        tkl_wired_get_status(&status);
        if (status == old_status) {
            sleep(1);
            continue;
        }
        old_status = status;

        status_cb(status);
        sleep(1);
    }
}


static OPERATE_RET tkl_wired_get_gateway(NW_IP_S *ip)
{
    FILE *fp = NULL;
    char *str = NULL;
    char buf[256] = {0};
    unsigned int dest = 0;
    struct in_addr addr;
    int get_gw = 0;

    /* Open file */
    if ((fp = fopen(ROUTE_PATH, "r")) == NULL) {
        LOGW("open %s failed, %d, %s\n", ROUTE_PATH, errno, strerror( errno ));
        return OPRT_COM_ERROR;
    }

    fgets(buf, sizeof(buf), fp);

    /* Read one line at a time, perform string processing */
    while(fgets(buf, sizeof(buf), fp)) {

        sscanf(buf, "%*s%x %x", &dest, &addr.s_addr);
        if ((dest == 0) && (addr.s_addr != 0)) {
            strncpy(ip->gw, inet_ntoa(addr), sizeof(ip->gw));
            get_gw = 1;
            break;
        }
    }

    if (get_gw == 0)
        LOGW("not find gw addr\n");

    fclose(fp);
    return OPRT_OK;
}
// --- END: user defines and implements ---

/**
 * @brief  init create wired link
 *
 * @param[in]   cfg: the configure for wired link
 * 
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
OPERATE_RET tkl_wired_init(TKL_WIRED_BASE_CFG_T *cfg)
{
    // --- BEGIN: user implements ---

    LOGW( "tkl_wired_init\n");
    return OPRT_OK;
    // --- END: user implements ---
}

/**
 * @brief  get the link status of wired link
 *
 * @param[out]  is_up: the wired link status is up or not
 *
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
OPERATE_RET tkl_wired_get_status(TKL_WIRED_STAT_E *status)
{

    LOGW( "Method: hackill %s", __FUNCTION__ );
    // --- BEGIN: user implements ---
    FILE *fp = NULL;
    char *str = NULL;
    char buf[256] = {0};
    char state[16] = {0};
    int sock = -1;
    struct ifreq ifr;
    char devname[IFNAMSIZ];
    int ret = -1;
    int plugged = 0;

    ret = net_utils_get_devname( devname, sizeof( devname ) );
    if ( ret < 0 ) {
        LOGW( "%s %d: not get devname\n", __func__, __LINE__ );
        return OPRT_COM_ERROR;
    }
    LOGW( "%s %d: success: devname =%s\n", __func__, __LINE__, devname);


    /* The network cable connection is normal, check whether the ip exists */
    /* Create socket */
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
         LOGW("socket create fail\n");
         return OPRT_SOCK_ERR;
    }

    LOGW( "%s %d: success: socket =%d\n", __func__, __LINE__, sock);

    ret = net_utils_check_cable( devname, &plugged );
    if ( !plugged ) {
        close(sock);
        *status = TKL_WIRED_LINK_DOWN;
        return OPRT_OK;
    }

    memset(&ifr, 0x0, sizeof(ifr));
    strncpy(ifr.ifr_name, devname, sizeof(ifr.ifr_name) - 1);

    /* get ip addr */
//    if (ioctl(sock, SIOCGIFADDR, &ifr) < 0) {
//        LOGW( "%s %d: %s get ip, error=%d,%s\n", __func__, __LINE__, devname, errno, strerror( errno ) );
//        close(sock);
//        *status = TKL_WIRED_LINK_DOWN;
//        return OPRT_OK;
//    }
    close(sock);
    *status = TKL_WIRED_LINK_UP;

    return OPRT_OK;
    // --- END: user implements ---
}

/**
 * @brief  set the status change callback
 *
 * @param[in]   cb: the callback when link status changed
 *
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
OPERATE_RET tkl_wired_set_status_cb(TKL_WIRED_STATUS_CHANGE_CB cb)
{
    // --- BEGIN: user implements ---
    pthread_t thread;

    status_cb = cb;

    return pthread_create(&thread, NULL, link_status_thread, NULL);
    // --- END: user implements ---
}

/**
 * @brief  set the ip address of the wired link, used for set the static ipaddress 
 * 
 * @param[in]   ip: the ip address
 *
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
OPERATE_RET tkl_wired_set_ip(NW_IP_S *ip)
{
    // --- BEGIN: user implements ---
    return OPRT_NOT_SUPPORTED;
    // --- END: user implements ---
}

/**
 * @brief  get the ip address of the wired link
 * 
 * @param[in]   ip: the ip address
 *
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
OPERATE_RET tkl_wired_get_ip(NW_IP_S *ip)
{
    // --- BEGIN: user implements ---
    int sock = -1;
    struct ifreq ifr;
    struct sockaddr_in *sin;
    char devname[IFNAMSIZ];
    int ret = -1;

    if (ip == NULL) {
        LOGW("invalid param\n");
        return OPRT_INVALID_PARM;
    }

    ret = net_utils_get_devname( devname, sizeof( devname ) );
    if ( ret < 0 ) {
        LOGW( "%s %d: not get devname\n", __func__, __LINE__ );
        return OPRT_COM_ERROR;
    }

    LOGW( "%s %d: ifname=%s\n", __func__, __LINE__, devname );

    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
         LOGW("socket create failed\n");
         return OPRT_SOCK_ERR;
    }

    memset( ip, 0x0, sizeof( NW_IP_S ) );
    memset(&ifr, 0x0, sizeof(ifr));
    strncpy(ifr.ifr_name, devname, sizeof(ifr.ifr_name) - 1);
    sin = (struct sockaddr_in *)&(ifr.ifr_addr);

    /* get ip addr */
    if (ioctl(sock, SIOCGIFADDR, &ifr) < 0) {
        LOGW( "%s %d: ip ioctl %s, error=%d,%s\n", __func__, __LINE__, devname, errno, strerror( errno ) );
        goto com_error;
    }
    strncpy(ip->ip, inet_ntoa(sin->sin_addr), sizeof(ip->ip));

    /* get net mask */
    if (ioctl(sock, SIOCGIFNETMASK, &ifr) < 0) {
        LOGW( "%s %d: mask ioctl %s, error=%d,%s\n", __func__, __LINE__, devname, errno, strerror( errno ) );
        goto com_error;
    }
    strncpy(ip->mask, inet_ntoa(sin->sin_addr), sizeof(ip->mask));

    /* get gateway */
//    if (tkl_wired_get_gateway(ip) < 0)
//        goto com_error;

    close(sock);
    return OPRT_OK;

com_error:
    close(sock);
    return OPRT_COM_ERROR;
    // --- END: user implements ---
}

/**
 * @brief  get the ip address of the wired link
 * 
 * @param[in]   ip: the ip address
 *
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
OPERATE_RET tkl_wired_get_ipv6(NW_IP_TYPE type, NW_IP_S *ip)
{
    // --- BEGIN: user implements ---
    return OPRT_NOT_SUPPORTED;
    // --- END: user implements ---
}

/**
 * @brief  get the mac address of the wired link
 * 
 * @param[in]   mac: the mac address
 *
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
OPERATE_RET tkl_wired_get_mac(NW_MAC_S *mac)
{
    // --- BEGIN: user implements ---
    int i;
    int sock = -1;
    struct ifreq ifr;
    struct sockaddr *addr;
    char devname[IFNAMSIZ];
    int ret = -1;

    if (mac == NULL) {
        LOGW("invalid param\n");
        return OPRT_INVALID_PARM;
    }

    ret = net_utils_get_devname( devname, sizeof( devname ) );
    if ( ret < 0 ) {
        LOGW( "%s %d: not get devname\n", __func__, __LINE__ );
        return OPRT_COM_ERROR;
    }

    LOGW( "%s %d: ifname=%s\n", __func__, __LINE__, devname );

    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
         LOGW("socket create fail\n");
         return OPRT_SOCK_ERR;
    }

    memset(&ifr, 0x0, sizeof(ifr));
    strncpy(ifr.ifr_name, devname, sizeof(ifr.ifr_name) - 1);
    addr = (struct sockaddr *)&ifr.ifr_hwaddr;
    addr->sa_family = 1;

    /* get mac addr */
    if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) {
        LOGW( "%s %d: %s get mac, error=%d,%s\n", __func__, __LINE__, devname, errno, strerror( errno ) );
        close(sock);
        return OPRT_COM_ERROR;
    }

    memcpy(mac->mac, addr->sa_data, MAC_ADDR_LEN);

    close(sock);
    return OPRT_OK;
    // --- END: user implements ---
}

/**
 * @brief  set the mac address of the wired link
 * 
 * @param[in]   mac: the mac address
 *
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
OPERATE_RET tkl_wired_set_mac(CONST NW_MAC_S *mac)
{
    // --- BEGIN: user implements ---
    int i;
    int sock = -1;
    struct ifreq ifr;
    struct sockaddr *addr;
    char devname[IFNAMSIZ];
    int ret = -1;

    if (mac == NULL) {
        LOGW("invalid param\n");
        return OPRT_INVALID_PARM;
    }
    LOGW("%s %d: %02x:%02x:%02x:%02x:%02x:%02x\n", __func__, __LINE__, mac->mac[0], mac->mac[1], mac->mac[2], mac->mac[3], mac->mac[4], mac->mac[5] );

    ret = net_utils_get_devname( devname, sizeof( devname ) );
    if ( ret < 0 ) {
        LOGW( "%s %d: not get devname\n", __func__, __LINE__ );
        return OPRT_COM_ERROR;
    }

    LOGW( "%s %d: ifname=%s\n", __func__, __LINE__, devname );

    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
         LOGW("socket create fail\n");
         return OPRT_SOCK_ERR;
    }

    memset(&ifr, 0x0, sizeof(ifr));
    strncpy(ifr.ifr_name, devname, sizeof(ifr.ifr_name) - 1);
    addr = (struct sockaddr *)&ifr.ifr_hwaddr;
    addr->sa_family = 1;
    memcpy(addr->sa_data, mac->mac, MAC_ADDR_LEN);

    /* set mac addr */
    if (ioctl(sock, SIOCSIFHWADDR, &ifr) < 0) {
        LOGW( "%s %d: %s set mac, error=%d,%s\n", __func__, __LINE__, devname, errno, strerror( errno ) );
        close(sock);
        return OPRT_COM_ERROR;
    }

    close(sock);
    return OPRT_OK;
    // --- END: user implements ---
}

