#include <vector>
#include <map>

#include "myComm.h"
#include "comm.h"
#include "comm_tcp.h"
#include "comm_udp.h"
#include "comm_uart.h"
#include "myLog.h"

std::map<int, stru_comm> g_comm_map;

std::map<CommType, std::string> g_comm_type_str_map = {
    {CommType::tcp_client, "tcp_client"},
    {CommType::tcp_server, "tcp_server"},
    {CommType::udp_client, "udp_client"},
    {CommType::udp_server, "udp_server"},
    {CommType::uart, "uart"}
};

static int g_comm_id = 0;

static int comm_get_id()
{
    return (++g_comm_id);
}


static void comm_debug_show(const char *str, uint8_t dir, const uint8_t *data, uint16_t len)
{
    if(NULL == data || len == 0)
    {
        LOG_E("data is NULL or len is 0");
        return;
    }

    if(str)
    {
        printf("%s", str);
    }

    if (dir == CommDir::dir_recv)
    {
        printf("\033[32m<-- rx %d bytes:\n", len);
    }
    else if(dir == CommDir::dir_send)
    {
        printf("\033[32m<-- tx %d bytes:\n", len);
    }
    else
    {
        printf("\033[32m<-- unkown direction %d, %d bytes:\n", dir, len);
    }

    for(int i = 0; i < len; i++)
    {
        if(i%64 == 0)
        {
            printf("\n");
        }
        printf("%02x ", data[i]);
    }

    printf("\n\033[0m");
}


int comm_create(CommType type, CommDebugShow debug_show, void *para)
{
    if(NULL == para)
    {
        LOG_E("para is NULL");
        return -1;
    }

    int ret = 0;
    stru_comm comm;
    comm.type = type;
    comm.init = CommInit::no;
    comm.debug_show = debug_show;

    switch(type)
    {
        case CommType::tcp_client:
        case CommType::tcp_server:
        {
            comm.p_comm = (stru_comm_tcp *)malloc(sizeof(stru_comm_tcp));
            stru_comm_tcp *p_tcp = (stru_comm_tcp *)comm.p_comm;
            p_tcp->p_para =  (stru_tcp_para *)para;
            p_tcp->debug_show = debug_show;
            ret = comm_tcp_create(p_tcp);
            break;
        }
        case CommType::udp_client:
        case CommType::udp_server:
        {
            comm.p_comm = (stru_comm_udp *)malloc(sizeof(stru_comm_udp));
            stru_comm_udp *p_udp = (stru_comm_udp *)comm.p_comm;
            p_udp->p_para = (stru_udp_para *)para;
            p_udp->debug_show = debug_show;
            ret = comm_udp_create(p_udp);
            break;
        }
        case CommType::uart:
        {
            comm.p_comm = (stru_comm_uart *)malloc(sizeof(stru_comm_uart));
            stru_comm_uart *p_uart = (stru_comm_uart *)comm.p_comm;
            p_uart->p_para = (stru_uart_para *)para;
            p_uart->debug_show = debug_show;
            ret = comm_uart_create(p_uart);
            break;
        }
        default:
        {
            LOG_E("unknown comm type:%d", type);
            type = CommType::unknown;
            break;
        }
    }
    
    if(0 != ret)
    {
        free(comm.p_comm);
        comm.p_comm = NULL;

        LOG_E("create comm %s failed", g_comm_type_str_map[type].c_str());
        return -1;
    }
    
    switch (type)
    {
        case CommType::tcp_client:
        case CommType::tcp_server:
        {
            stru_comm_tcp *p_tcp = (stru_comm_tcp *)comm.p_comm;
            ret = p_tcp->tcp_debug_show_register((void *)p_tcp, comm_debug_show);
            break;
        }
        case CommType::udp_client:
        case CommType::udp_server:
        {
            stru_comm_udp *p_udp = (stru_comm_udp *)comm.p_comm;
            ret = p_udp->udp_debug_show_register((void *)p_udp, comm_debug_show);
            break;
        }
        case CommType::uart:
        {
            stru_comm_uart *p_uart = (stru_comm_uart *)comm.p_comm;
            ret = p_uart->uart_debug_show_register((void *)p_uart, comm_debug_show);
            break;
        }
        default:
        {
            break;
        }
    }

    if(ret != 0)
    {
        LOG_E("comm_debug_show register failed !");
    }

    comm.init = CommInit::ok;

    int id = comm_get_id();

    g_comm_map[id] = comm;  

    return id;
}

int comm_connect(int id)
{
    std::map<int, stru_comm>::iterator it = g_comm_map.find(id);
    if(it ==  g_comm_map.end())
    {
        LOG_E("comm id:%d not exist", id);
        return -1;
    }
    stru_comm &comm = it->second;
    if(comm.init != CommInit::ok)
    {
        LOG_E("comm id:%d not init", id);
        return -1;
    }

    switch (comm.type)
    {
        case CommType::tcp_client:
        {
            stru_comm_tcp *p_tcp = (stru_comm_tcp *)comm.p_comm;
            return p_tcp->tcp_client_connect(id, (void *)p_tcp);
        }
        case CommType::tcp_server:
        {
            stru_comm_tcp *p_tcp = (stru_comm_tcp *)comm.p_comm;
            return p_tcp->tcp_server_connect(id, (void *)p_tcp);
        }
        case CommType::udp_client:
        {
            stru_comm_udp *p_udp = (stru_comm_udp *)comm.p_comm;
            return p_udp->udp_client_connect(id, (void *)p_udp);
        }
        case CommType::udp_server:
        {
            stru_comm_udp *p_udp = (stru_comm_udp *)comm.p_comm;
            return p_udp->udp_server_connect(id, (void *)p_udp);
        }
        case CommType::uart:
        {
            stru_comm_uart *p_uart = (stru_comm_uart *)comm.p_comm;
            return p_uart->uart_connect(id, (void *)p_uart);
            break;
        }
        default:
        {
            break;
        }
    }

    return -1;
}

int comm_disconnect(int id)
{
    std::map<int, stru_comm>::iterator it = g_comm_map.find(id);
    if(it ==  g_comm_map.end())
    {
        LOG_E("comm id:%d not exist", id);
        return -1;
    }
    stru_comm &comm = it->second;
    if(comm.init != CommInit::ok)
    {
        LOG_E("comm id:%d not init", id);
        return -1;
    }

    switch (comm.type)
    {
        case CommType::tcp_client:
        case CommType::tcp_server:
        {
            stru_comm_tcp *p_tcp = (stru_comm_tcp *)comm.p_comm;
            return p_tcp->tcp_close(id, (void *)p_tcp);
        }
        case CommType::udp_client:
        case CommType::udp_server:
        {
            stru_comm_udp *p_udp = (stru_comm_udp *)comm.p_comm;
            return p_udp->udp_close(id, (void *)p_udp);
        }
        case CommType::uart:
        {
            stru_comm_uart *p_uart = (stru_comm_uart *)comm.p_comm;
            return p_uart->uart_close((void *)p_uart);
        }
        default:
        {
            break;
        }
    }

    return -1;
}


int comm_recv_register(int id, comm_recv_cb cb)
{
    std::map<int, stru_comm>::iterator it = g_comm_map.find(id);
    if(it ==  g_comm_map.end())
    {
        LOG_E("comm id:%d not exist", id);
        return -1;
    }
    stru_comm &comm = it->second;
    if(comm.init != CommInit::ok)
    {
        LOG_E("comm id:%d not init", id);
        return -1;
    }

    switch (comm.type)
    {
        case CommType::tcp_client:
        case CommType::tcp_server:
        {
            stru_comm_tcp *p_tcp = (stru_comm_tcp *)comm.p_comm;
            return p_tcp->tcp_recv_register((void *)p_tcp, cb);
        }
        case CommType::udp_client:
        case CommType::udp_server:
        {
            stru_comm_udp *p_udp = (stru_comm_udp *)comm.p_comm;
            return p_udp->udp_recv_register((void *)p_udp, cb);
        }
        case CommType::uart:
        {
            stru_comm_uart *p_uart = (stru_comm_uart *)comm.p_comm;
            return p_uart->uart_recv_register((void *)p_uart, cb);
            break;
        }
        default:
        {
            break;
        }
    }

    return -1;
}

int comm_send(int id, int fd, const uint8_t *data, uint16_t len)
{
    std::map<int, stru_comm>::iterator it = g_comm_map.find(id);
    if(it ==  g_comm_map.end())
    {
        LOG_E("comm id:%d not exist", id);
        return -1;
    }
    stru_comm &comm = it->second;
    if(comm.init != CommInit::ok)
    {
        LOG_E("comm id:%d not init", id);
        return -1;
    }

    switch (comm.type)
    {
        case CommType::tcp_client:
        {
            stru_comm_tcp *p_tcp = (stru_comm_tcp *)comm.p_comm;
            return p_tcp->tcp_send(id, fd, "client", (void *)p_tcp, data, len);
        }
        case CommType::tcp_server:
        {
            stru_comm_tcp *p_tcp = (stru_comm_tcp *)comm.p_comm;
            return p_tcp->tcp_send(id, fd, "server", (void *)p_tcp, data, len);
        }
        case CommType::udp_client:
        {
            stru_comm_udp *p_udp = (stru_comm_udp *)comm.p_comm;
            return p_udp->udp_send(id, fd, "client", (void *)p_udp, data, len);
        }
        case CommType::udp_server:
        {
            stru_comm_udp *p_udp = (stru_comm_udp *)comm.p_comm;
            return p_udp->udp_send(id, fd, "server", (void *)p_udp, data, len);
        }
        case CommType::uart:
        {
            stru_comm_uart *p_uart = (stru_comm_uart *)comm.p_comm;
            return p_uart->uart_send((void *)p_uart, data, len);
            break;
        }
        default:
        {
            break;
        }
    }

    return -1;
}

int comm_state_register(int id, comm_state_cb cb)
{
    std::map<int, stru_comm>::iterator it = g_comm_map.find(id);
    if(it ==  g_comm_map.end())
    {
        LOG_E("comm id:%d not exist", id);
        return -1;
    }
    stru_comm &comm = it->second;
    if(comm.init != CommInit::ok)
    {
        LOG_E("comm id:%d not init", id);
        return -1;
    }

    switch (comm.type)
    {
        case CommType::tcp_client:
        case CommType::tcp_server:
        {
            stru_comm_tcp *p_tcp = (stru_comm_tcp *)comm.p_comm;
            return p_tcp->tcp_state_register((void *)p_tcp, cb);
        }
        case CommType::udp_client:
        case CommType::udp_server:
        {
            stru_comm_udp *p_udp = (stru_comm_udp *)comm.p_comm;
            return p_udp->udp_state_register((void *)p_udp, cb);
        }
        case CommType::uart:
        {
            stru_comm_uart *p_uart = (stru_comm_uart *)comm.p_comm;
            return p_uart->uart_state_register((void *)p_uart, cb);
            break;
        }
        default:
        {
            break;
        }
    }

    return -1;
}



#include <sys/stat.h>
#include <string.h>
// #include <linux/sockios.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <fcntl.h>
// #include <stdio.h>
#include <unistd.h>
#include <linux/serial.h>


CommFdType get_fd_type(int fd) 
{
    struct stat st;
    
    if (fstat(fd, &st) < 0) 
    {
        return CommFdType::unknown;
    }
    
    // 判断文件类型
    if (S_ISSOCK(st.st_mode)) 
    {
        return CommFdType::socket;
    } else if (S_ISCHR(st.st_mode)) 
    {
        // 可能是串口或终端
        #ifdef __linux__
        // 进一步判断是否为串口
        struct serial_struct serinfo;
        if (ioctl(fd, TIOCGSERIAL, &serinfo) == 0) 
        {
            return CommFdType::serial;
        }
        #endif
        return CommFdType::file;
    } else if (S_ISFIFO(st.st_mode)) 
    {
        return CommFdType::pipe;
    } else 
    {
        return CommFdType::file;
    }
}



int get_fd_info(int fd, stru_comm_fd_info *info) 
{
    if (info == NULL) return -1;
    
    memset(info, 0, sizeof(stru_comm_fd_info));
    info->type = get_fd_type(fd);
    
    switch (info->type) 
    {
        case CommFdType::socket: 
        {
            // 获取socket信息
            struct sockaddr_in addr;
            socklen_t len = sizeof(addr);
            
            // 本地地址
            if (getsockname(fd, (struct sockaddr *)&addr, &len) == 0) 
            {
                inet_ntop(AF_INET, &addr.sin_addr, info->socket_info.local_ip, INET_ADDRSTRLEN);
                info->socket_info.local_port = ntohs(addr.sin_port);
            }
            
            // 远端地址（如果已连接）
            if (getpeername(fd, (struct sockaddr *)&addr, &len) == 0) 
            {
                inet_ntop(AF_INET, &addr.sin_addr, info->socket_info.remote_ip, INET_ADDRSTRLEN);
                info->socket_info.remote_port = ntohs(addr.sin_port);
            }
            
            // 获取socket类型
            int sock_type;
            len = sizeof(sock_type);
            getsockopt(fd, SOL_SOCKET, SO_TYPE, &sock_type, &len);
            info->socket_info.protocol = sock_type;
            
            break;
        }

        case CommFdType::serial: 
        {
            // 获取串口信息
            #ifdef __linux__
            char path[64];
            snprintf(path, sizeof(path), "/proc/self/fd/%d", fd);
            
            char real_path[256];
            ssize_t n = readlink(path, real_path, sizeof(real_path) - 1);
            if (n > 0) 
            {
                real_path[n] = '\0';
                strncpy(info->serial_info.device, real_path, 256 - 1);
            }
            #endif
            
            // 获取串口参数
            struct termios tios;
            if (tcgetattr(fd, &tios) == 0) 
            {
                // 波特率
                speed_t speed = cfgetispeed(&tios);
                info->serial_info.baud_rate = speed;
                
                // 数据位
                info->serial_info.data_bits = 
                    (tios.c_cflag & CSIZE) == CS8 ? 8 :
                    (tios.c_cflag & CSIZE) == CS7 ? 7 :
                    (tios.c_cflag & CSIZE) == CS6 ? 6 : 5;
                
                // 停止位
                info->serial_info.stop_bits = (tios.c_cflag & CSTOPB) ? 2 : 1;
                
                // 校验位
                if (tios.c_cflag & PARENB) 
                {
                    strcpy(info->serial_info.parity, 
                           (tios.c_cflag & PARODD) ? "Odd" : "Even");
                } else 
                {
                    strcpy(info->serial_info.parity, "None");
                }
            }
            break;
        }

        case CommFdType::file: 
        {
            struct stat st;
            if (fstat(fd, &st) == 0) 
            {
                info->file_info.size = st.st_size;
            }
            
            #ifdef __linux__
            char path[64];
            snprintf(path, sizeof(path), "/proc/self/fd/%d", fd);
            
            char real_path[256];
            ssize_t n = readlink(path, real_path, sizeof(real_path) - 1);
            if (n > 0) 
            {
                real_path[n] = '\0';
                strncpy(info->file_info.filename, real_path, 256 - 1);
            }
            #endif
            break;
        }
        
        default:
            break;
    }
    
    return 0;
}