#include "httpd.h"
#include "httpd-fs.h"
#include "httpd-fsdata.h"
#include "httpd-cgi.h"
#include "http-strings.h"
#include "fram_map.h"
#include "fram.h"
#include "mode.h"
#include "app_serv.h"
#include "version.h"
#include <string.h>
#include <stdio.h>
#include <stddef.h>

HttpdState httpd_state;
extern char message_lines[2][128];

extern CPU_INT32U SystemTime;
extern CPU_INT32U http_last_status_time;

#define STATE_WAITING 0
#define STATE_OUTPUT  1

#define ISO_nl      0x0a
#define ISO_space   0x20
#define ISO_bang    0x21
#define ISO_percent 0x25
#define ISO_period  0x2e
#define ISO_slash   0x2f
#define ISO_colon   0x3a

int net_reconfigure_flag = 0;

static TDataDescStruct const* param_ptr = NULL;
static CPU_INT32U param_ind = -1;


static int http_replace_tag(char** template_ptr, char* buf, char* tag, char* value)
{
    int len = 0;
    char *tag_ptr = strstr(*template_ptr, tag);
    if (tag_ptr)
    {
        len = tag_ptr - *template_ptr;
        memcpy(buf, *template_ptr, len);
        strcpy(&buf[len], value);
        len += strlen(value);
    }
    *template_ptr = &tag_ptr[strlen(tag)];
    return len;
}

///
static int http_prepare_file(char *filename)
{
    if (strncmp(filename, "/index.html", strlen("/index.html")) == 0)
    {
        char *page_template = (char*)&data_index_html_template[strlen(data_index_html_template) + 1];
        char *page_buffer = (char*)&data_html_page_buffer;

        memset(data_html_page_buffer, 0, sizeof(data_html_page_buffer));
        
        page_buffer += http_replace_tag(&page_template, page_buffer, "{$version}", DEVICE_FW_VERSION);
        strcpy(page_buffer, page_template);
        
        ((struct httpd_fsdata_file_noconst*)&file_index_html)->len = strlen(data_html_page_buffer);
        httpd_state.file.len = ((struct httpd_fsdata_file_noconst*)&file_index_html)->len;
    }
    else if (strncmp(filename, "/network.html", strlen("/network.html")) == 0)
    {
        NET_ERR err;
        char *page_template = (char*)&data_network_html_template[strlen(data_network_html_template) + 1];
        char *page_buffer = (char*)&data_html_page_buffer;
        NET_IP_ADDR ip_addr;
        CPU_INT08U  mac_addr[6];
        char str_value[32];

        memset(data_html_page_buffer, 0, sizeof(data_html_page_buffer));

        page_buffer += http_replace_tag(&page_template, page_buffer, "{$version}", DEVICE_FW_VERSION);
        
        ReadArrayFram(offsetof(TFramMap, ip), sizeof(CPU_INT32U), (unsigned char*)&ip_addr);
        NetASCII_IP_to_Str(ip_addr, (CPU_CHAR*)str_value, DEF_NO, &err);
        page_buffer += http_replace_tag(&page_template, page_buffer, "{$ip_addr}", str_value);
                             
        ReadArrayFram(offsetof(TFramMap, netmask), sizeof(CPU_INT32U), (unsigned char*)&ip_addr);
        NetASCII_IP_to_Str(ip_addr, (CPU_CHAR*)str_value, DEF_NO, &err);
        page_buffer += http_replace_tag(&page_template, page_buffer, "{$net_mask}", str_value);

        ReadArrayFram(offsetof(TFramMap, gateway), sizeof(CPU_INT32U), (unsigned char*)&ip_addr);
        NetASCII_IP_to_Str(ip_addr, (CPU_CHAR*)str_value, DEF_NO, &err);
        page_buffer += http_replace_tag(&page_template, page_buffer, "{$gateway}", str_value);

        ReadArrayFram(offsetof(TFramMap, mac_addr), 6, (unsigned char*)&mac_addr[0]);
        NetASCII_MAC_to_Str((CPU_INT08U*)&mac_addr[0], (CPU_CHAR*)str_value, DEF_NO, &err);
        page_buffer += http_replace_tag(&page_template, page_buffer, "{$mac}", str_value);
        
        strcpy(page_buffer, page_template);
        ((struct httpd_fsdata_file_noconst*)&file_network_html)->len = strlen(data_html_page_buffer);
        httpd_state.file.len = ((struct httpd_fsdata_file_noconst*)&file_network_html)->len;
    }
    else if (strncmp(filename, "/reboot.html", strlen("/reboot.html")) == 0)
    {
        NET_ERR err;
        char *page_template = (char*)&data_reboot_html_template[strlen(data_reboot_html_template) + 1];
        char *page_buffer = (char*)&data_html_page_buffer;
        NET_IP_ADDR ip_addr;
        char str_value[32];

        memset(data_html_page_buffer, 0, sizeof(data_html_page_buffer));

        ReadArrayFram(offsetof(TFramMap, ip), sizeof(CPU_INT32U), (unsigned char*)&ip_addr);
        NetASCII_IP_to_Str(ip_addr, (CPU_CHAR*)str_value, DEF_NO, &err);
        page_buffer += http_replace_tag(&page_template, page_buffer, "{$ip_addr}", str_value);
        
        strcpy(page_buffer, page_template);
        ((struct httpd_fsdata_file_noconst*)&file_reboot_html)->len = strlen(data_html_page_buffer);
        httpd_state.file.len = ((struct httpd_fsdata_file_noconst*)&file_reboot_html)->len;
    }
    else if (strncmp(filename, "/status.csv", strlen("/status.csv")) == 0)
    {
        char *page_template = (char*)&data_status_csv_template[strlen(data_status_csv_template) + 1];
        char *page_buffer = (char*)&data_html_page_buffer;
        char str[32];

        memset(data_html_page_buffer, 0, sizeof(data_html_page_buffer));

        strcpy(str, AppStatusStr(app_state.state));
        page_buffer += http_replace_tag(&page_template, page_buffer, "{$state}", str);
        sprintf(str, "%d", app_state.timetoend);
        page_buffer += http_replace_tag(&page_template, page_buffer, "{$timetoend}", str);
        sprintf(str, "%d", app_state.userinputtime);
        page_buffer += http_replace_tag(&page_template, page_buffer, "{$timeinput}", str);
        sprintf(str, "%d", app_state.userinputmoney);
        page_buffer += http_replace_tag(&page_template, page_buffer, "{$moneyinput}", str);
        
        strcpy(page_buffer, page_template);
        ((struct httpd_fsdata_file_noconst*)&file_status_csv)->len = strlen(data_html_page_buffer);
        httpd_state.file.len = ((struct httpd_fsdata_file_noconst*)&file_status_csv)->len;
        
        http_last_status_time = SystemTime;
    }
    else if (strncmp(filename, "/param.csv", strlen("/param.csv")) == 0)
    {
        char *page_template = (char*)&data_param_csv_template[strlen(data_param_csv_template) + 1];
        char *page_buffer = (char*)&data_html_page_buffer;
        char str[48];

        memset(data_html_page_buffer, 0, sizeof(data_html_page_buffer));

        if (param_ptr != NULL)
        {
            GetDescIdStr((TDataDescStruct const*)param_ptr, str);
            page_buffer += http_replace_tag(&page_template, page_buffer, "{$param}", str);
            sprintf(str, "%d", param_ind);
            page_buffer += http_replace_tag(&page_template, page_buffer, "{$index}", str);
            GetDataStr((TDataDescStruct const*)param_ptr, (CPU_INT08U*)str, param_ind, DATA_FLAG_DIRECT_INDEX);
            page_buffer += http_replace_tag(&page_template, page_buffer, "{$value}", str);
        }
        else
        {
            page_buffer += http_replace_tag(&page_template, page_buffer, "{$param}", "NULL");
            page_buffer += http_replace_tag(&page_template, page_buffer, "{$index}", "0");
            page_buffer += http_replace_tag(&page_template, page_buffer, "{$value}", "0");
        }
            
        strcpy(page_buffer, page_template);
        ((struct httpd_fsdata_file_noconst*)&file_param_csv)->len = strlen(data_html_page_buffer);
        httpd_state.file.len = ((struct httpd_fsdata_file_noconst*)&file_param_csv)->len;
    }

    return 0;
}

///
static int http_process_post_request(char* path, char *post_data, int len)
{
    static const char cmd_header1[] = "Command;Password;User_ID;BalanceTime;BalanceMoney\r\n";
    static const char cmd_header2[] = "Command;Password;User_ID;Line1;Line2\r\n";
    static const char cmd_header2_[] = "Command;Password;User_ID\r\n";
    static const char cmd_header3[] = "Command;Password;Param;Index;Value\r\n";
    static const char cmd_header4[] = "Command;Password;Param;Index\r\n";
    static const char cmd_header5[] = "Command;Password\r\n";
    static const char user_req_time_cmd[] = "user_req_time";
    static const char start_cmd[] = "start";
    static const char cansel_cmd[] = "cansel";
    static const char abort_cmd[] = "abort";
    static const char get_param_cmd[] = "get_param";
    static const char set_param_cmd[] = "set_param";
            
    if ((strcmp(path, "/command") == 0) || (strcmp(path, "/command ") == 0))
    {
        char *ptr;
        
        // "Command;Password;User_ID;BalanceTime;BalanceMoney\r\n"
        ptr = strstr(post_data, cmd_header1);
        if (ptr == post_data)
        {
            ptr = post_data + strlen(cmd_header1);
            
            if (strncmp(ptr, user_req_time_cmd, strlen(user_req_time_cmd)) == 0)
            {
                // перейти к запросу времени у пользователя
                int pass, user_id, time, money;
                
                if ((GetMode() != MODE_WORK) && (app_state.state != APP_STATE_IDLE))
                {
                    return -1;
                }
                    
                if (sscanf(ptr, "user_req_time;%d;%d;%d;%d\r\n", &pass, &user_id, &time, &money) == 4)
                {
                    CPU_INT32U pass_ee;
                    GetData(&PassDesc, &pass_ee, 0, DATA_FLAG_SYSTEM_INDEX);
                    
                    if (pass_ee != pass)
                    {
                        return -1;
                    }
                    
                    if (time > 0 || money > 0)
                    {
                        app_state.user_id = user_id;
                        app_state.timefromserver = time;
                        app_state.moneyfromserver = money;
                        PostUserEvent(EVENT_REQUEST_USER_INPUT);
                        OSTimeDly(10);
                        strcpy(path, "/status");
                        return 0;
                    }
                    else
                    {
                        PostUserEvent(EVENT_SHOW_ZERO_MONEY);
                        return -1;
                    }
                }
            }
            return -1;
        }
        
        // "Command;Password;User_ID;Line1;Line2\r\n"
        ptr = strstr(post_data, cmd_header2);
        if (ptr == post_data)
        {
            ptr = post_data + strlen(cmd_header2);
                    
            if (strncmp(ptr, cansel_cmd, strlen(cansel_cmd)) == 0)
            {
                int pass, user_id;
  
                if (app_state.state != APP_STATE_WAITING_ACK)
                {
                    return -1;
                }

                if (strlen(ptr) > 128)
                {
                    return -1;
                }
                
                if (sscanf(ptr, "cansel;%d;%d;%s\r\n", &pass, &user_id, message_lines[0]) == 3)
                {
                    CPU_INT32U pass_ee;
                    GetData(&PassDesc, &pass_ee, 0, DATA_FLAG_SYSTEM_INDEX);
                    
                    if (pass_ee != pass)
                    {
                        return -1;
                    }
                    
                    ptr = strstr(message_lines[0], ";");
                    if (!ptr)
                    {
                        return -1;
                    }
                    
                    *ptr++ = '\0';
                    strncpy(message_lines[1], ptr, 32);

                    app_state.user_id = user_id;
                    PostUserEvent(EVENT_NACK_USER_INPUT);
                    OSTimeDly(10);
                    strcpy(path, "/status");
                    return 0;
                }
            }
            return -1;
        }
        
        // "Command;Password;User_ID\r\n"
        ptr = strstr(post_data, cmd_header2_);
        if (ptr == post_data)
        {
            ptr = post_data + strlen(cmd_header2_);
            
            if (strncmp(ptr, start_cmd, strlen(start_cmd)) == 0)
            {
                int pass, user_id;
                
                if (app_state.state != APP_STATE_WAITING_ACK)
                {
                    return -1;
                }
                    
                if (sscanf(ptr, "start;%d;%d\r\n", &pass, &user_id) == 2)
                {
                    CPU_INT32U pass_ee;
                    GetData(&PassDesc, &pass_ee, 0, DATA_FLAG_SYSTEM_INDEX);
                    
                    if (pass_ee != pass)
                    {
                        return -1;
                    }
                    
                    app_state.user_id = user_id;
                    PostUserEvent(EVENT_ACK_USER_INPUT);
                    OSTimeDly(10);
                    strcpy(path, "/status");
                    return 0;
                }
            }
            return -1;
        }

        // "Command;Password;Param;Index;Value\r\n"
        ptr = strstr(post_data, cmd_header3);
        if (ptr == post_data)
        {
            ptr = post_data + strlen(cmd_header3);
            
            if (strncmp(ptr, set_param_cmd, strlen(set_param_cmd)) == 0)
            {
                int pass;
                int p_ind;
                char p_str[64];
                char value_str[32];
                char *ptmp;
                if (sscanf(ptr, "set_param;%d;%s\r\n", &pass, p_str) == 2)
                {
                    CPU_INT32U pass_ee;
                    GetData(&PassDesc, &pass_ee, 0, DATA_FLAG_SYSTEM_INDEX);
                    
                    if (pass_ee != pass)
                    {
                        return -1;
                    }
                    
                    ptmp = strchr(p_str, ';');
                    if (!ptmp)
                    {
                        return -1;
                    }
                    
                    if (sscanf(ptmp, ";%d;%s", &p_ind, value_str) != 2)
                    {
                        return -1;
                    }
                    
                    TDataDescStruct const* desc;
                    ptmp = strchr(p_str, ';');
                    if (!ptmp)
                    {
                        return -1;
                    }
                    *ptmp = '\0';
                    GetDescByIdStr(p_str, (TDataDescStruct const**)&desc);
                    if (desc != NULL)
                    {
                        CPU_INT32U tmp_ind = GetDataValidIndex(desc, p_ind);
                        if (tmp_ind == p_ind)
                        {
                            if (SetDataFromStr(desc, value_str, p_ind, DATA_FLAG_DIRECT_INDEX) != DATA_OK)
                            {
                                param_ptr = NULL;
                                param_ind = 0;
                                return -1;
                            }
                        }
                        param_ptr = desc;
                        param_ind = tmp_ind;
                    }
                    else
                    {
                        param_ptr = NULL;
                        param_ind = 0;
                    }
                        
                    strcpy(path, "/param.csv");
                    return 0;
                }
            }
            
            return -1;
        }

        // "Command;Password;Param;Index\r\n"
        ptr = strstr(post_data, cmd_header4);
        if (ptr == post_data)
        {
            ptr = post_data + strlen(cmd_header4);
            
            if (strncmp(ptr, get_param_cmd, strlen(get_param_cmd)) == 0)
            {
                int pass;
                int p_ind;
                char p_str[64];
                char *ptmp;
                
                if (sscanf(ptr, "get_param;%d;%s", &pass, p_str) == 2)
                {
                    CPU_INT32U pass_ee;
                    GetData(&PassDesc, &pass_ee, 0, DATA_FLAG_SYSTEM_INDEX);
                    
                    if (pass_ee != pass)
                    {
                        return -1;
                    }
                    
                    ptmp = strchr(p_str, ';');
                    if (!ptmp)
                    {
                        return -1;
                    }

                    if (sscanf(ptmp, ";%d", &p_ind) != 1)
                    {
                        return -1;
                    }
                    
                    *ptmp = '\0';
                    
                    TDataDescStruct const* desc;
                    GetDescByIdStr(p_str, (TDataDescStruct const**)&desc);
                    if (desc != NULL)
                    {
                        param_ptr = desc;
                        param_ind = GetDataValidIndex(desc, p_ind);
                    }
                    else
                    {
                        param_ptr = NULL;
                        param_ind = 0;
                    }
                        
                    strcpy(path, "/param.csv");
                    return 0;
                }
            }
            
            return -1;
        }
        
        // "Command;Password\r\n"
        ptr = strstr(post_data, cmd_header5);
        if (ptr == post_data)
        {
            ptr = post_data + strlen(cmd_header5);
            
            if (strncmp(ptr, abort_cmd, strlen(abort_cmd)) == 0)
            {
                int pass;
                
                if (sscanf(ptr, "abort;%d\r\n", &pass) == 1)
                {
                    CPU_INT32U pass_ee;
                    GetData(&PassDesc, &pass_ee, 0, DATA_FLAG_SYSTEM_INDEX);
                    
                    if (pass_ee != pass)
                    {
                        return -1;
                    }
                    
                    PostUserEvent(EVENT_ABORT_OPERATIONS);
                    OSTimeDly(10);
                    strcpy(path, "/status");
                    return 0;
                }
            }
            
            return -1;
        }
        
        return -1;
    }
    if ((strcmp(path, "/netsettings") == 0) || (strcmp(path, "/netsettings ") == 0))
    {
        int ip0, ip1, ip2, ip3;
        CPU_INT32U new_ip, new_mask, new_gw;
        CPU_INT08U new_mac[6];
        
        if (strstr(post_data, "ApplyButton=") == NULL)
        {
            return -1;
        }
        
        ReadArrayFram(offsetof(TFramMap, mac_addr), 6, (unsigned char*)&new_mac[0]);
        ReadArrayFram(offsetof(TFramMap, ip), sizeof(CPU_INT32U), (unsigned char*)&new_ip);
        ReadArrayFram(offsetof(TFramMap, netmask), sizeof(CPU_INT32U), (unsigned char*)&new_mask);
        ReadArrayFram(offsetof(TFramMap, gateway), sizeof(CPU_INT32U), (unsigned char*)&new_gw);
        
        char *pch = strstr(post_data, "ipAddrField=");
        if (pch)
        {
            if (sscanf(&pch[strlen("ipAddrField=")], "%d.%d.%d.%d", &ip0, &ip1, &ip2, &ip3) != 4)
            {
                return -1;
            }
            if ((ip0 >= 0) && (ip0 <= 255) && (ip1 >= 0) && (ip1 <= 255) && (ip2 >= 0) && (ip2 <= 255) && (ip3 >= 0) && (ip3 <= 255))
            {
                new_ip = ((CPU_INT32U)ip0 << 24) | ((CPU_INT32U)ip1 << 16) | ((CPU_INT32U)ip2 << 8) | ((CPU_INT32U)ip3 << 0);
            }
            else
            {
                return -1;
            }
        }
        pch = strstr(post_data, "NetMask=");
        if (pch)
        {
            if (sscanf(&pch[strlen("NetMask=")], "%d.%d.%d.%d", &ip0, &ip1, &ip2, &ip3) != 4)
            {
                return -1;
            }
            if ((ip0 >= 0) && (ip0 <= 255) && (ip1 >= 0) && (ip1 <= 255) && (ip2 >= 0) && (ip2 <= 255) && (ip3 >= 0) && (ip3 <= 255))
            {
                new_mask = ((CPU_INT32U)ip0 << 24) | ((CPU_INT32U)ip1 << 16) | ((CPU_INT32U)ip2 << 8) | ((CPU_INT32U)ip3 << 0);
            }
            else
            {
                return -1;
            }
        }
        pch = strstr(post_data, "Gateway=");
        if (pch)
        {
            if (sscanf(&pch[strlen("Gateway=")], "%d.%d.%d.%d", &ip0, &ip1, &ip2, &ip3) != 4)
            {
                return -1;
            }
            if ((ip0 >= 0) && (ip0 <= 255) && (ip1 >= 0) && (ip1 <= 255) && (ip2 >= 0) && (ip2 <= 255) && (ip3 >= 0) && (ip3 <= 255))
            {
                new_gw = ((CPU_INT32U)ip0 << 24) | ((CPU_INT32U)ip1 << 16) | ((CPU_INT32U)ip2 << 8) | ((CPU_INT32U)ip3 << 0);
            }
            else
            {
                return -1;
            }
        }
        
        pch = strstr(post_data, "MAC=");
        if (pch)
        {
            NET_ERR   err;
            char mac_str[48];
            strncpy(mac_str, &pch[strlen("MAC=")], 48);
            char *ptmp = strstr(mac_str, "%3A");
            while (ptmp)
            {
                *ptmp = ':';
                strcpy(ptmp + 1, ptmp + strlen("%3A"));
                ptmp = strstr(ptmp, "%3A");
            }
            mac_str[17] = '\0';
            NetASCII_Str_to_MAC((CPU_CHAR*)mac_str, &new_mac[0], &err);
            if (err != NET_ASCII_ERR_NONE)
            {
                return -1;
            }
        }
        
        WriteArrayFram(offsetof(TFramMap, ip), sizeof(CPU_INT32U), (unsigned char*)&new_ip);
        WriteArrayFram(offsetof(TFramMap, netmask), sizeof(CPU_INT32U), (unsigned char*)&new_mask);
        WriteArrayFram(offsetof(TFramMap, gateway), sizeof(CPU_INT32U), (unsigned char*)&new_gw);
        WriteArrayFram(offsetof(TFramMap, mac_addr), 6, (unsigned char*)&new_mac[0]);
        
        net_reconfigure_flag = 1;
        strcpy(path, "/reboot.html");
        return 0;
    }
    return -1;
}

///
int httpd_write_socket_data(void *state, char* data, int len)
{
    CPU_INT32U time_stamp = OSTimeGet();
    HttpdState *s = (HttpdState*)state;
    int tx_ctr = 0;
    while (tx_ctr < len)
    {
        NET_ERR err;
        int tx_size = NetSock_TxData(s->sock_req, &data[tx_ctr], len - tx_ctr, NET_SOCK_FLAG_TX_NO_BLOCK, &err);
        switch (err)
        {
            case NET_SOCK_ERR_NONE:
                tx_ctr += tx_size;
                time_stamp = OSTimeGet();
                break;
            default:
                break;
        }
        OSTimeDly(1);
        if (OSTimeGet() - time_stamp > 1000)
        {
            break;
        }
    }
    return tx_ctr;
}

///
static unsigned short generate_part_of_file(void *state)
{
    HttpdState *s = (HttpdState*)state;
    
    if (s->file.len > HTTPD_OUTPUT_BUF_SIZE)
    {
        s->len = HTTPD_OUTPUT_BUF_SIZE;
    } 
    else 
    {
        s->len = s->file.len;
    }
    memcpy(&s->outputbuf, s->file.data, s->len);
    
    return s->len;
}

///
static char send_file(void *state)
{
    HttpdState *s = (HttpdState*)state;
    do 
    {
        if (generate_part_of_file(state) <= 0) break;
        if (httpd_write_socket_data(s, s->outputbuf, s->len) != s->len) break;
        s->file.len -= s->len;
        s->file.data += s->len;
    } while(s->file.len > 0);
    return 0;
}

///
static char send_part_of_file(void *state)
{
    HttpdState *s = (HttpdState*)state;
    httpd_write_socket_data(s, s->file.data, s->len);
    return 0;
}

///
static void next_scriptstate(HttpdState *s)
{
    char *p;
    p = strchr(s->scriptptr, ISO_nl) + 1;
    s->scriptlen -= (unsigned short)(p - s->scriptptr);
    s->scriptptr = p;
}

///
static char handle_script(HttpdState *s)
{
    char *ptr;
    
    while(s->file.len > 0)
    {
    
        // Check if we should start executing a script.
        if (*s->file.data == ISO_percent && *(s->file.data + 1) == ISO_bang)
        {
            s->scriptptr = s->file.data + 3;
            s->scriptlen = s->file.len - 3;
            if(*(s->scriptptr - 1) == ISO_colon)
            {
                httpd_fs_open(s->scriptptr + 1, &s->file);
                send_file(s);
            }
            else
            {
                //httpd_cgi(s->scriptptr)(s, s->scriptptr);
            }
            next_scriptstate(s);
            
            // The script is over, so we reset the pointers and continue sending the rest of the file.
            s->file.data = s->scriptptr;
            s->file.len = s->scriptlen;
        }
        else
        {
            // See if we find the start of script marker in the block of HTML to be sent.
            if(s->file.len > HTTPD_OUTPUT_BUF_SIZE)
            {
                s->len = HTTPD_OUTPUT_BUF_SIZE;
            }
            else
            {
                s->len = s->file.len;
            }
            
            if (*s->file.data == ISO_percent)
            {
                ptr = strchr(s->file.data + 1, ISO_percent);
            }
            else
            {
                ptr = strchr(s->file.data, ISO_percent);
            }

            if (ptr != NULL && ptr != s->file.data)
            {
                s->len = (int)(ptr - s->file.data);
                if (s->len >= HTTPD_OUTPUT_BUF_SIZE)
                {
                    s->len = HTTPD_OUTPUT_BUF_SIZE;
                }
            }
            send_part_of_file(s);
            s->file.data += s->len;
            s->file.len -= s->len;
        }
    }
    return 0;
}


static char send_headers(HttpdState *s, const char *statushdr)
{
    char *ptr;
    
    httpd_write_socket_data(s, (char *)statushdr, strlen(statushdr));
    
    ptr = strrchr(s->filename, ISO_period);
    if (ptr == NULL) 
    {
        httpd_write_socket_data(s, (char *)http_content_type_binary, strlen(http_content_type_binary));
    }
    else if (strncmp(http_html, ptr, 5) == 0 || strncmp(http_shtml, ptr, 6) == 0)
    {
        httpd_write_socket_data(s, (char *)http_content_type_html, strlen(http_content_type_html));
    }
    else if (strncmp(http_css, ptr, 4) == 0) 
    {
        httpd_write_socket_data(s, (char *)http_content_type_css, strlen(http_content_type_css));
    }
    else if (strncmp(http_png, ptr, 4) == 0) 
    {
        httpd_write_socket_data(s, (char *)http_content_type_png, strlen(http_content_type_png));
    } 
    else if (strncmp(http_gif, ptr, 4) == 0) 
    {
        httpd_write_socket_data(s, (char *)http_content_type_gif, strlen(http_content_type_gif));
    }
    else if (strncmp(http_csv, ptr, 4) == 0) 
    {
        httpd_write_socket_data(s, (char *)http_content_type_csv, strlen(http_content_type_csv));
    }
    else if (strncmp(http_jpg, ptr, 4) == 0) 
    {
        httpd_write_socket_data(s, (char *)http_content_type_jpg, strlen(http_content_type_jpg));
    } 
    else 
    {
        httpd_write_socket_data(s, (char *)http_content_type_plain, strlen(http_content_type_plain));
    }
    return 0;
}

///
void httpd_close_socket(HttpdState *s)
{
    NET_ERR             err;
    //OSTimeDly(10);
    NetSock_Close(s->sock_req, &err);
    NetSock_Close(s->sock_listen, &err);
}

///
static char handle_output(HttpdState *s)
{
    char *ptr;

    if ((strcmp(s->filename, "/status") == 0) || (strcmp(s->filename, "/status ") == 0))
    {
        strcpy(s->filename, "/status.csv");
    }
    
    if (!httpd_fs_open(s->filename, &s->file)) 
    {
        httpd_fs_open(http_404_html, &s->file);
        strcpy(s->filename, http_404_html);
        send_headers(s, http_header_404);
        send_file(s);
    }
    else
    {
        send_headers(s, http_header_200);
        http_prepare_file(s->filename);
        ptr = strchr(s->filename, ISO_period);
        if (ptr != NULL && strncmp(ptr, http_shtml, 6) == 0)
        {
            handle_script(s);
        }
        else
        {
            send_file(s);
        }
    }
    OSTimeDly(5);
    httpd_close_socket(s);
    return 0;
}

///
static NET_ERR socket_read_char(HttpdState *s, char* c, int timeout)
{
    NET_ERR err = NET_SOCK_ERR_NONE;
    CPU_INT32U time_stamp = OSTimeGet();
    
    do
    {
        NET_SOCK_RTN_CODE ret_code;

        if (NetNIC_ConnStatusGet() != DEF_ON)
        {
            return NET_SOCK_ERR_FAULT;
        }
        
        if (s->inputbuf_ctr > 0)
        {
            *c = s->inputbuf_raw[s->inputbuf_ptr++];
            s->inputbuf_ctr--;
            return NET_SOCK_ERR_NONE;
        }
        
        ret_code = NetSock_RxData(s->sock_req, s->inputbuf_raw, HTTPD_INPUT_BUF_SIZE, NET_SOCK_FLAG_RX_NO_BLOCK, &err);
        switch (err)
        {
            case NET_SOCK_ERR_NONE:
                if ((ret_code > 0) &&(ret_code <= HTTPD_INPUT_BUF_SIZE))
                {
                    s->inputbuf_ctr = ret_code;
                    s->inputbuf_ptr = 0;
                }
                break;
            case NET_SOCK_ERR_RX_Q_EMPTY:
                if (OSTimeGet() - time_stamp < timeout)
                {
                    err = NET_SOCK_ERR_NONE;
                }
                OSTimeDly(1);
                break;
            case NET_SOCK_ERR_INVALID_DATA_SIZE:
            case NET_ERR_INIT_INCOMPLETE:
            case NET_SOCK_ERR_NULL_PTR:
            case NET_SOCK_ERR_NULL_SIZE:
            case NET_SOCK_ERR_NOT_USED:
            case NET_SOCK_ERR_CLOSED:
            case NET_SOCK_ERR_FAULT:
            case NET_SOCK_ERR_INVALID_SOCK:
            case NET_SOCK_ERR_INVALID_FAMILY:
            case NET_SOCK_ERR_INVALID_PROTOCOL:
            case NET_SOCK_ERR_INVALID_TYPE:
            case NET_SOCK_ERR_INVALID_STATE:
            case NET_SOCK_ERR_INVALID_OP:
            case NET_SOCK_ERR_INVALID_FLAG:
            case NET_SOCK_ERR_INVALID_ADDR_LEN:
            case NET_SOCK_ERR_RX_Q_CLOSED:
            case NET_ERR_RX:
            case NET_CONN_ERR_INVALID_CONN:
            case NET_CONN_ERR_NOT_USED:
            case NET_CONN_ERR_NULL_PTR:
            case NET_CONN_ERR_INVALID_ADDR_LEN:
            case NET_CONN_ERR_ADDR_NOT_USED:
            case NET_OS_ERR_LOCK:
                break;
            default:
                err = NET_ERR_RX;
                break;
        }

        if (OSTimeGet() - time_stamp >= timeout)
        {
            return NET_SOCK_ERR_RX_Q_EMPTY;
        }
                           
    } while (err == NET_SOCK_ERR_NONE);
    
    return err;
}

///
static int socket_read_to(HttpdState *s, char sym, int timeout)
{
    char c;
    int ctr = 0;
    while (socket_read_char(s, &c, timeout) == NET_SOCK_ERR_NONE)
    {
        s->inputbuf[ctr++] = c;
        if (c == sym)
        {
            s->inputbuf[ctr] = 0;
            return 0;
        }
        if (ctr >= HTTPD_INPUT_BUF_SIZE - 1)
        {
            return -1;
        }
    }
    return -1;
}

///
static int socket_read(HttpdState *s, int len, int timeout)
{
    char c;
    int ctr = 0;
    while (socket_read_char(s, &c, timeout) == NET_SOCK_ERR_NONE)
    {
        s->inputbuf[ctr++] = c;
        if (ctr >= len)
        {
            break;
        }
    }
    return ctr;
}

///
static char handle_input(HttpdState *s)
{
    int post_req = 0;
    int has_content = 0;
    int content_len = 0;

    socket_read_to(s, ISO_space, HTTPS_RX_TIMEOUT);
    
    if (strncmp(s->inputbuf, http_post, 5) == 0)
    {
        post_req = 1;
    }
    
    if ((!post_req) && (strncmp(s->inputbuf, http_get, 4) != 0))
    {
        httpd_close_socket(s);
        return -1;
    }
    
    socket_read_to(s, ISO_space, 0);
    
    if (s->inputbuf[0] != ISO_slash)
    {
        httpd_close_socket(s);
        return -1;
    }
    
    if (s->inputbuf[1] == ISO_space)
    {
        strncpy(s->filename, http_index_html, sizeof(s->filename));
    }
    else 
    {
        strncpy(s->filename, &s->inputbuf[0], sizeof(s->filename));
    }
    
    s->state = STATE_OUTPUT;
    
    while (1)
    {
        if (socket_read_to(s, ISO_nl, 0) != 0)
        {
            break;
        }
        
        if (post_req)
        {
            if (strncmp(s->inputbuf, http_content_lenght, 16) == 0) 
            {
                if (sscanf(&s->inputbuf[16], "%d\r\n", &content_len) == 1)
                {
                    has_content = 1;
                    if (content_len >= HTTPD_INPUT_BUF_SIZE - 1)
                    {
                        httpd_close_socket(s);
                        return -1;
                    }
                }
            }
            else if (strncmp(s->inputbuf, "\r\n", 2) == 0)
            {
                if (has_content && content_len)
                {
                    if (socket_read(s, content_len, HTTPS_RX_TIMEOUT) != content_len)
                    {
                        httpd_close_socket(s);
                        return -1;                    
                    }
                    s->inputbuf[content_len] = 0;
                    if (http_process_post_request(s->filename, s->inputbuf, content_len) == 0)
                    {
                    
                    }
                    else
                    {
                        strncpy(s->filename, "error", sizeof(s->filename));
                    }
                }
            }
        }
    }
    return 0;
}

///
static void handle_connection(HttpdState *s)
{
    handle_input(s);
    if (s->state == STATE_OUTPUT)
    {
        handle_output(s);
    }
}

///
void  HttpdTask(void *p_arg)
{
    NET_SOCK_ADDR_IP    server_sock_addr_ip;
    NET_SOCK_ADDR_LEN   server_sock_addr_ip_size;
    NET_SOCK_ADDR_IP    client_sock_addr_ip;
    NET_SOCK_ADDR_LEN   client_sock_addr_ip_size;
    CPU_BOOLEAN         attempt_conn;
    NET_ERR             err;
    
    while (1)
    {
        OSTimeDly(1);
    
        if (NetNIC_ConnStatusGet() != DEF_ON)
        {
            continue;
        }
    
        httpd_state.sock_listen = NetSock_Open( NET_SOCK_ADDR_FAMILY_IP_V4, NET_SOCK_TYPE_STREAM, NET_SOCK_PROTOCOL_TCP, &err);
        if (err != NET_SOCK_ERR_NONE)
        {
            continue;
        }
  
        server_sock_addr_ip_size = sizeof(server_sock_addr_ip);
        Mem_Clr((void*)&server_sock_addr_ip, (CPU_SIZE_T)server_sock_addr_ip_size);
        server_sock_addr_ip.Family = NET_SOCK_ADDR_FAMILY_IP_V4;
        server_sock_addr_ip.Addr       = NET_UTIL_HOST_TO_NET_32(NET_SOCK_ADDR_IP_WILD_CARD);
        server_sock_addr_ip.Port       = NET_UTIL_HOST_TO_NET_16(80);
        
        NetSock_Bind((NET_SOCK_ID )httpd_state.sock_listen, (NET_SOCK_ADDR *)&server_sock_addr_ip, (NET_SOCK_ADDR_LEN)NET_SOCK_ADDR_SIZE, (NET_ERR *)&err);
        if (err != NET_SOCK_ERR_NONE) 
        {
            NetSock_Close(httpd_state.sock_listen, &err);
            httpd_state.sock_listen = -1;
            continue;
        }
  
        NetSock_Listen(httpd_state.sock_listen, 1, &err);
        if (err != NET_SOCK_ERR_NONE) 
        {
            NetSock_Close(httpd_state.sock_listen, &err);
            httpd_state.sock_listen = -1;
            continue;
        }
  
        do 
        {
            client_sock_addr_ip_size = sizeof(client_sock_addr_ip);
  
            if (NetNIC_ConnStatusGet() != DEF_ON)
            {
                err = NET_SOCK_ERR_NONE_AVAIL;
                break;
            }

            OSTimeDly(1);
            
            httpd_state.sock_req = NetSock_Accept((NET_SOCK_ID )httpd_state.sock_listen, (NET_SOCK_ADDR *)&client_sock_addr_ip, (NET_SOCK_ADDR_LEN *)&client_sock_addr_ip_size, (NET_ERR *)&err);
            switch (err) 
            {
                case NET_SOCK_ERR_NONE:
                    attempt_conn = DEF_NO;
                    break;
                case NET_ERR_INIT_INCOMPLETE:
                case NET_SOCK_ERR_NULL_PTR:
                case NET_SOCK_ERR_NONE_AVAIL:
                case NET_SOCK_ERR_CONN_ACCEPT_Q_NONE_AVAIL:
                case NET_SOCK_ERR_CONN_SIGNAL_TIMEOUT:
                case NET_OS_ERR_LOCK:
                    attempt_conn = DEF_YES;
                    break;
      
                default:
                    attempt_conn = DEF_NO;
                    break;
            }
        } while (attempt_conn == DEF_YES);
  
        if (err != NET_SOCK_ERR_NONE) 
        {
            httpd_close_socket(&httpd_state);
            memset(&httpd_state, 0 , sizeof(httpd_state));
            httpd_state.sock_req = -1;
            httpd_state.sock_listen = -1;
            continue;
        }
        
        httpd_state.state = STATE_WAITING;
        handle_connection(&httpd_state);
        memset(&httpd_state, 0 , sizeof(httpd_state));
        httpd_state.sock_req = -1;
        httpd_state.sock_listen = -1;
        
        if (net_reconfigure_flag)
        {
            PostUserEvent(EVENT_SYSTEM_REBOOT);   
        }
    }
}

/// 
void httpd_init(void)
{
    INT8U err;
    static  OS_STK     HttpdTaskStk[HTTPD_TASK_STK_SIZE];

    memset(&httpd_state, 0 , sizeof(httpd_state));
    httpd_state.sock_req = -1;
    httpd_state.sock_listen = -1;
        
    OSTaskCreate(HttpdTask, (void *)0, (OS_STK *)&HttpdTaskStk[HTTPD_TASK_STK_SIZE-1], HTTPD_TASK_PRIO);
    OSTaskNameSet(HTTPD_TASK_PRIO, "HTTPD Task", &err);
}
