#include "ftpd.h"
#include "avltree.h"
#include "bsp_fatfs.h"
#include "ff.h"
#include "fly_param.h"
#include "ftp_server.h"
#include "os_cpu.h"
#include "sockets.h"
#include "ucos_ii.h"
#include <stdint.h>
#include <stdio.h>

static struct avl_root ftp_root = {.avl_node = NULL}; // 命令匹配的平衡二叉树树根

/* Private variables ------------------------------------------------------------*/

static const char  normal_format[]    = "-rw-rw-rw-   1 user     ftp  %11ld %s %02i %5i %s\r\n";
static const char  this_year_format[] = "-rw-rw-rw-   1 user     ftp  %11ld %s %02i %02i:%02i %s\r\n";
static const char *month_list[]       = { // 月份从 1 到 12 ，0 填充 NULL
    NULL,
    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
    "Jul", "Aug", "Sep", "Oct", "Nov", "Dez"};

// ftp 命令树构建
#define FTP_REGISTER_COMMAND(CMD)        \
    do                                   \
    {                                    \
        static ftp_cmd_t CmdBuf;         \
        CmdBuf.index = FTP_STR2ID(#CMD); \
        CmdBuf.func  = cmd_##CMD;        \
        ftp_insert_command(&CmdBuf);     \
    } while (0)

// ftp 文件列表格式
#define NORMAL_LIST(listbuf, filesize, month, day, year, filename) \
    sprintf(listbuf, normal_format, (filesize), month_list[(month)], (day), (year), (filename))

#define THIS_YEAR_LIST(listbuf, filesize, month, day, hour, min, filename) \
    sprintf(listbuf, this_year_format, (filesize), month_list[(month)], (day), (hour), (min), (filename))

// ftp 格式一般为 xxxx /dirx/diry/\r\n ,去掉 /\r\n 提取可用路径
#define FTP_PATH_GET(path, pathend) \
    do                              \
    {                               \
        while (*path == ' ')        \
            ++path;                 \
        if (*pathend == '\n')       \
            *pathend-- = 0;         \
        if (*pathend == '\r')       \
            *pathend-- = 0;         \
        if (*pathend == '/')        \
            *pathend = 0;           \
    } while (0)

/**
 * @brief    ftp_insert_command
 *           命令树插入
 * @param    pCmd        命令控制块
 * @return   成功返回 0
 */
int ftp_insert_command(ftp_cmd_t *p_cmd)
{
    struct avl_node **tmp    = &ftp_root.avl_node;
    struct avl_node  *parent = NULL;

    while (*tmp)
    {
        ftp_cmd_t *this = container_of(*tmp, ftp_cmd_t, cmd_node);

        parent = *tmp;
        if (p_cmd->index < this->index)
            tmp = &((*tmp)->avl_left);
        else if (p_cmd->index > this->index)
            tmp = &((*tmp)->avl_right);
        else
            return 1;
    }

    avl_insert(&ftp_root, &p_cmd->cmd_node, parent, tmp);
    return 0;
}

/**
 * @brief    ftp_search_command
 *           命令树查找，根据 Index 号找到对应的控制块
 * @param    index        命令号
 * @return   成功 Index 号对应的控制块
 */
ftp_cmd_t *ftp_search_command(int i_ctrl_cmd)
{
    struct avl_node *node = ftp_root.avl_node;

    while (node)
    {
        ftp_cmd_t *p_cmd = container_of(node, ftp_cmd_t, cmd_node);

        if (i_ctrl_cmd < p_cmd->index)
            node = node->avl_left;
        else if (i_ctrl_cmd > p_cmd->index)
            node = node->avl_right;
        else
            return p_cmd;
    }

    return NULL;
}
// 登录系统用户名
static void cmd_user(ftp_mbox_t *msgbox)
{
    if (strcasecmp(msgbox->arg, "root") == 0)
    {
        send(msgbox->cmd_socket, msg331, sizeof(msg331) - 1, 0);
    }
    else
    {
        send(msgbox->cmd_socket, msg331, sizeof(msg331) - 1, 0);
    }
}

// 登录系统密码
static void cmd_pass(ftp_mbox_t *msgbox)
{
    // printf(msgbox->arg);
    // if (strcasecmp(msgbox->arg, "ASASAS") == 0)
    // {
    send(msgbox->cmd_socket, msg230, sizeof(msg230) - 1, 0);
    // }
    // else
    // {
    //     send(msgbox->cmd_socket, msg503, sizeof(msg503) - 1, 0);
    // }
}

// 操作系统类型
static void cmd_syst(ftp_mbox_t *msgbox)
{
    static const char reply_msg[] = "215 UNIX Type: L8\r\n"; // 215 系统类型回复
    send(msgbox->cmd_socket, reply_msg, sizeof(reply_msg) - 1, 0);
}

// 显示当前工作目录
static void cmd_pwd(ftp_mbox_t *msgbox)
{
#if 1
    char reply_msg[128]; // 257 路径名建立
    // sprintf(reply_msg, "257 \"%s/\"\r\n", msgbox->current_dir);
    sprintf(reply_msg, "257 \"%s/\"\r\n", "LINUX");
#else
    static const char reply_msg[] = "257 \"/\"\r\n";
#endif
    send(msgbox->cmd_socket, reply_msg, strlen(reply_msg), 0);
}

/**
 * @brief    cmd_noop
 *           ftp 命令端口输入 noop
 * @param    arg 命令所跟参数
 * @return   NULL
 */
static void cmd_noop(ftp_mbox_t *msgbox)
{
    static const char reply_msg[] = "200 Operation successful\r\n";
    send(msgbox->cmd_socket, reply_msg, sizeof(reply_msg) - 1, 0);
}

// 路径名建立
static void cmd_cwd(ftp_mbox_t *msgbox)
{
    static const char reply_msg[] = "250 Operation successful\r\n";

    DIR   fsdir;
    char *pcFilePath = msgbox->arg;
    char *pcPathEnd  = msgbox->arg + msgbox->arglen - 1;

    FTP_PATH_GET(pcFilePath, pcPathEnd);

    if (FR_OK != f_opendir(&fsdir, pcFilePath))
    {
        goto cmddone;
    }

    f_closedir(&fsdir);

    if (pcPathEnd != pcFilePath)
        memcpy(msgbox->current_dir, pcFilePath, pcPathEnd - pcFilePath);

    msgbox->current_dir[pcPathEnd - pcFilePath] = 0;

cmddone:
    send(msgbox->cmd_socket, reply_msg, sizeof(reply_msg) - 1, 0);
}

// static void cmd_feat(ftp_mbox_t *msgbox)
// {
//     static const char reply_msg[] = "211-feature:\r\n";
//     static const char msgtprt[]   = "EPRT\r\n";
//     send(msgbox->cmd_socket, reply_msg, sizeof(reply_msg) - 1, 0);

//     send(msgbox->cmd_socket, msgtprt, sizeof(msgtprt) - 1, 0);
// }

// 被动模式
static void cmd_pasv(ftp_mbox_t *msgbox)
{
    static char reply_msg[64] = {0};

    INT32S stor = strlen(reply_msg);

    if (0 == stor) // 未初始化信息
    {
        sprintf(reply_msg, "227 pasv ok(%d,%d,%d,%d,%d,%d)\r\n",
                192, 168, 1, 40, (FTP_DATA_PORT >> 8), FTP_DATA_PORT & 0x00ff);

        stor = strlen(reply_msg);
    }

    send(msgbox->cmd_socket, reply_msg, stor, 0);
}

/**
 * @brief    cmd_nlst
 *           ftp 命令端口输入 nlst ，被动模式
 *
 * @param    arg 命令所跟参数
 * @return   NULL
 */
static void cmd_nlst(ftp_mbox_t *msgbox)
{
    static const char reply_msg[] = "150 Directory listing\r\n"; // 150 打开连接

    // 1.在控制端口对 list 命令进行回复
    // 2.在数据端口发送 "total 0"，这个貌似可以没有
    // 3.在数据端口发送文件列表
    // 4.关闭数据端口

    send(msgbox->cmd_socket, reply_msg, sizeof(reply_msg) - 1, 0);
    msgbox->event = FTP_LIST; // 事件为列表事件

    OSMboxPost(ftp_mbox, msgbox);
}

/**
 * @brief    cmd_list
 *           ftp 命令端口输入 list , 获取当前文件列表
 * @param    arg 命令所跟参数
 * @return   NULL
 */
static void cmd_list(ftp_mbox_t *msgbox)
{
    static const char reply_msg[] = "150 Directory listing\r\n"; // 150 打开连接

    // 1.在控制端口对 list 命令进行回复
    // 2.在数据端口发送 "total 0"，这个貌似可以没有
    // 3.在数据端口发送文件列表
    // 4.关闭数据端口

    send(msgbox->cmd_socket, reply_msg, sizeof(reply_msg) - 1, 0);
    msgbox->event = FTP_LIST; // 事件为列表事件
    OSMboxPost(ftp_mbox, msgbox);
}

/**
 * @brief    cmd_size
 *           ftp 命令端口输入 size , 获取当前文件列表
 * @param    arg 命令所跟参数
 * @return   NULL
 */
static void cmd_size(ftp_mbox_t *msgbox)
{
    char     acFtpBuf[128];
    uint32_t iFilesize;
    char    *pcFilePath = msgbox->arg;
    char    *pcPathEnd  = msgbox->arg + msgbox->arglen - 1;

    FTP_PATH_GET(pcFilePath, pcPathEnd);

    if (*pcFilePath != '/') // 相对路径补全为绝对路径
    {
        pcFilePath = acFtpBuf;
    }

    if (FR_OK != f_open(&file_object, pcFilePath, FA_READ))
    {
        goto sizedone;
    }

    iFilesize = f_size(&file_object);
    f_close(&file_object);

sizedone:
    send(msgbox->cmd_socket, acFtpBuf, strlen(acFtpBuf), 0);
}

/**
 * @brief    cmd_retr
 *           ftp 命令端口输入 retr
 * @param    arg 命令所跟参数
 * @return   NULL
 */
static void cmd_retr(ftp_mbox_t *msgbox)
{
    static const char reply_msg[] = "108 Operation successful\r\n";

    send(msgbox->cmd_socket, reply_msg, sizeof(reply_msg) - 1, 0);

    msgbox->event = FTP_SEND_FILE_DATA;

    OSMboxPost(ftp_mbox, msgbox);
}

/**
 * @brief    cmd_dele
 *           ftp 命令端口输入 dele
 * @param    arg 命令所跟参数
 * @return   NULL
 */
static void cmd_dele(ftp_mbox_t *msgbox)
{
    FRESULT res;
    char    databuf[128];
    char   *pcFilePath = msgbox->arg;
    char   *pcPathEnd  = msgbox->arg + msgbox->arglen - 1;
    FTP_PATH_GET(pcFilePath, pcPathEnd);

    if (*pcFilePath != '/') // 相对路径
    {
        sprintf(databuf, "%s/%s", msgbox->current_dir, pcFilePath);
        pcFilePath = databuf;
    }

    res = f_unlink(pcFilePath);
    if (FR_OK != res)
        goto deleError;

    send(msgbox->cmd_socket, msg250, sizeof(msg250) - 1, 0);
    return;

deleError:
    send(msgbox->cmd_socket, msg450, sizeof(msg450) - 1, 0);

    return;
}

/**
 * @brief    cmd_stor
 *           ftp 命令端口输入 stor
 * @param    arg 命令所跟参数
 * @return   NULL
 */
static void cmd_stor(ftp_mbox_t *msgbox)
{
    static const char reply_msgOK[] = "125 Waiting\r\n";

    send(msgbox->cmd_socket, reply_msgOK, sizeof(reply_msgOK) - 1, 0);

    msgbox->event = FTP_RECV_FILE;

    OSMboxPost(ftp_mbox, msgbox);
}

static const char ftp_msg_451[] = "451 errors";
static const char ftp_msg_226[] = "226 transfer complete\r\n";

/* Start node to be scanned (***also used as work area***) */
char *data_port_list_file(ftp_mbox_t *msgbox)
{
    char   *ctrl_msg = (char *)ftp_msg_226;
    char    list_buf[128];
    DIR     dir;
    FILINFO fno;

    if (FR_OK != f_opendir(&dir, msgbox->current_dir))
    {
        goto ScanDirDone;
    }

    for (;;)
    {
        struct FileDate *pStDate;
        struct FileTime *pStTime;
        FRESULT          res = f_readdir(&dir, &fno); /* Read a directory item */

        if (res != FR_OK || fno.fname[0] == 0) /* Break on error or end of dir */
            break;

        if ((fno.fattrib & AM_DIR) && (fno.fattrib != AM_DIR)) // 不显示只读/系统/隐藏文件夹
            continue;

        pStDate = (struct FileDate *)(&fno.fdate);
        pStTime = (struct FileTime *)(&fno.ftime);

        if (fno.fdate == 0 || fno.ftime == 0) // 没有日期的文件
            NORMAL_LIST(list_buf, fno.fsize, 1, 1, 1980, fno.fname);
        else if (pStDate->Year + 1980 == 2018) // 同一年的文件
            THIS_YEAR_LIST(list_buf, fno.fsize, pStDate->Month, pStDate->Day, pStTime->Hour, pStTime->Min, fno.fname);
        else
            NORMAL_LIST(list_buf, fno.fsize, pStDate->Month, pStDate->Day, pStDate->Year + 1980, fno.fname);

        if (fno.fattrib & AM_DIR) /* It is a directory */
            list_buf[0] = 'd';

        send(g_ftp_data_socket, list_buf, strlen(list_buf), 0);
    }

    f_closedir(&dir); // 把路径关闭

ScanDirDone:
    return ctrl_msg;
}

// char *data_port_recv_file(ftp_mbox_t *msgbox)
// {
//     // static __align(4) char recv_buf[TCP_MSS]; // fatfs 写文件的时候，buf要地址对齐，否则容易出错

//     char          *ctrl_msg = (char *)ftp_msg_451;
//     FIL            RecvFile; /* File object */
//     FRESULT        res;
//     char          *pcFile    = msgbox->arg;
//     char          *pcPathEnd = msgbox->arg + msgbox->arglen - 1;
//     char           databuf[128];
//     uint16_t       sRxlen;
//     uint32_t       byteswritten;
//     struct netbuf *data_netbuf;
//     int            ret;

//     // vFtp_GetLegalPath(pcFile, pcPathEnd);

//     if (*pcFile != '/') // 相对路径
//     {
//         sprintf(databuf, "%s/%s", msgbox->current_dir, pcFile);
//         pcFile = databuf;
//     }

//     res = f_open(&RecvFile, pcFile, FA_CREATE_ALWAYS | FA_WRITE);
//     if (res != FR_OK)
//     {
//         Errors("cannot open/create \"%s\",error code = %d\r\n", pcFile, res);
//         goto RecvEnd;
//     }
//     ret = recv(g_ftp_data_socket, data_netbuf, sizeof(data_netbuf) - 1, 0);

//     //     while (ERR_OK == recv(g_ftp_data_socket, &data_netbuf)) // 阻塞直到收到数据
//     //     {
//     //         do
//     //         {
//     //             netbuf_data(data_netbuf, (void **)&pcFile, &sRxlen); // 提取数据指针

//     // #if 1
//     //             memcpy(recv_buf, pcFile, sRxlen); // 把数据拷出来，否则容易出错
//     //             pcFile = recv_buf;
//     // #endif

//     //             res = f_write(&RecvFile, (void *)pcFile, sRxlen, &byteswritten);

//     //             printk(".");
//     //             if ((byteswritten == 0) || (res != FR_OK))
//     //             {
//     //                 f_close(&RecvFile);
//     //                 Errors("write file error\r\n");
//     //                 goto RecvEnd;
//     //             }
//     //         } while (netbuf_next(data_netbuf) >= 0);

//     //         netbuf_delete(data_netbuf);
//     //     }

//     ctrl_msg = (char *)ftp_msg_226;
//     f_close(&RecvFile);

// RecvEnd:

//     return ctrl_msg;
// }

// void ftp_data_deal(void)
// {
//     INT8U       err = 0;
//     ftp_mbox_t *msgbox;

//     char *ctrl_msg;
//     while (g_ftp_data_socket != -1)
//     {
//         msgbox = (ftp_mbox_t *)OSMboxPend(ftp_mbox, 50, &err);
//         printf((char *)msgbox->event);
//         if ((err == OS_ERR_NONE) && (msgbox->arglen >= 2))
//         {
//             switch (msgbox->event) // 根据不同的操作命令进行操作
//             {
//             case FTP_LIST:
//                 ctrl_msg = data_port_list_file(msgbox);
//                 break;

//                 // case FTP_SEND_FILE_DATA:
//                 //     ctrl_msg = data_port_send_file(data_port_conn, msgbox);
//                 //     break;

//                 // case FTP_RECV_FILE:
//                 //     ctrl_msg = data_port_recv_file(data_port_conn, msgbox);
//                 //     break;

//             default:;
//             }
//             send(msgbox->cmd_socket, ctrl_msg, strlen(ctrl_msg), 0); // 控制端口反馈
//             lwip_close(g_ftp_data_socket);                           // 关闭链接
//             g_ftp_data_socket = -1;                                  // 清空释放连接的内存
//         }
//         else
//         {
//             lwip_close(g_ftp_data_socket); // 关闭链接
//             g_ftp_data_socket = -1;        // 清空释放连接的内存
//         }
//     }
// }

void ftp_server_init(void)
{
    ftp_cmd_func cmd_type = cmd_noop;

    // 生成相关的命令二叉树
    FTP_REGISTER_COMMAND(user);
    FTP_REGISTER_COMMAND(pass);
    FTP_REGISTER_COMMAND(syst);
    FTP_REGISTER_COMMAND(pwd);
    FTP_REGISTER_COMMAND(cwd);
    // FTP_REGISTER_COMMAND(feat);
    FTP_REGISTER_COMMAND(pasv);
    FTP_REGISTER_COMMAND(nlst);
    FTP_REGISTER_COMMAND(list);
    FTP_REGISTER_COMMAND(noop);
    FTP_REGISTER_COMMAND(type);
    FTP_REGISTER_COMMAND(size);
    FTP_REGISTER_COMMAND(retr);
    FTP_REGISTER_COMMAND(dele);
    FTP_REGISTER_COMMAND(stor);
}