/*  plc hle utils project {{{
 *
 * Copyright (C) 2018 MStar Semiconductor
 *
 * <<<Licence>>>
 *
 * }}} */

#include "evse_slac.h"
#include "evse_main.h"
#include "ccu_config.h"
#include "hmac_sha256.h"
#include "v2g_context.h"

static uint8_t HexCode(uint8_t x)
{
    #define IS_DIGIST(x)    ('0'<=(x)&&(x)<='9')
    #define IS_UPPER(x)    ('A'<=(x)&&(x)<='F')
    #define IS_LOWER(x)    ('a'<=(x)&&(x)<='f')
    #define IS_HEX(x)  (IS_DIGIST(x) || IS_UPPER(x)  || IS_LOWER(x))
    if (IS_DIGIST(x))
    {
        return x - '0';
    }
    if (IS_UPPER(x))
    {
        return x - 'A' + 10;
    }
    if (IS_LOWER(x))
    {
        return x - 'a' + 10;
    }
    return x;
    #undef IS_DIGIST
    #undef IS_UPPER
    #undef IS_LOWER
    #undef IS_HEX
}

static int HexString(uint8_t *stream, const char* string)
{
    int i = 0;
    int j = 0;
    uint8_t hi;
    uint8_t lo;
    while(string[i]!='\0')
    {
        if (string[i++]==':')
        {
            hi = HexCode(string[i++]);
            lo = HexCode(string[i++]);
            stream[j++] = (hi<<4)|lo;
        }
    }
    return j;
}

int v2g_slac_init(void* argv, const char* ifname)
{
    v2g_context_t* v2g = (v2g_context_t*)argv;
    secc_port_t *secc = (secc_port_t*)v2g->secc;
    evse_slac_t* slac = (evse_slac_t*)&v2g->evse_slac;
    if (slac)
    {
        slac->quit = 0;
        slac->iname = ifname;
        slac->result = -1;

        // HexString(slac->evse.nid, ":02:6B:CB:A5:35:4E:07");
        // HexString(slac->evse.nmk, ":B5:93:19:D7:E8:15:7B:A0:01:B0:18:66:9C:CE:E3:0D");

        char strbuff[128];
        hpav_generate_nmk_nid((char*)format_UTCTime(strbuff, time(NULL)), 0, slac->evse.nmk, slac->evse.nid);

        int result = 0;

        DEBUG_DISPLAY = LOG_VERBOSE;

        memcpy(other_mac_link_local, mac_link_local, 6);
    
        slac->evse.pev_item_head = NULL;
        slac->evse.pev_item_head = (pev_item *)malloc (sizeof (pev_item));
        slac->evse.pev_item_head->next = NULL;
    
        LOG_PRINT (LOG_DEBUG, "-----  [This is EVSE host] -----\n");
    
        printline(slac->evse.nid, 7, "Print EVSE's NID");
        printline(slac->evse.nmk, 16, "Print EVSE's NMK");
        printline(other_mac_link_local, 6, "other_mac_link_local");
    
        iface_t *iface = (iface_t *)&slac->iface;
    
        if (open_interface (slac->iname, iface) == 0)
        {
            printline(iface->mac, 6, "EVSE.MAC");
            result = evse_cm_set_key_req_cnf (iface, &slac->evse, other_mac_link_local);
            if (result == MME_SUCCESS)
            {
                result = vs_get_status_req_send_cnf_receive (iface);
                if (result == CONNECT_LINK)
                {
                    slac->result = ERROR_LINK;
                    LOG_PRINT (LOG_INFO,"Error: Your EVSE is connected to PEV now.\n");
                    return -4;
                }
                else if (result == ERROR_LINK)
                {
                    slac->result = ERROR_LINK;
                    LOG_PRINT (LOG_INFO, "Error: vs_get_status_req_send_cnf_receive()\n");
                    return -5;
                }
                LOG_PRINT (LOG_INFO, "slac Job start(%d)\n", slac->quit);
                return 0;
            }
            LOG_PRINT (LOG_INFO, "[EVSE] evse_cm_set_key_req_cnf != MME_SUCCESS\n");
            return -3;
        }
        LOG_PRINT (LOG_INFO, "[EVSE] open_interface != 0\n");
        return -2;
    }
    LOG_PRINT (LOG_INFO, "[EVSE] slac==NULL\n");
    return -1;
}

void* v2g_slac_loop(void* argv)
{
    v2g_context_t* v2g = (v2g_context_t*)argv;
    secc_port_t *secc = (secc_port_t*)v2g->secc;
    evse_slac_t* slac = (evse_slac_t*)&v2g->evse_slac;
    iface_t *iface = (iface_t *)&slac->iface;
    mme_ctx_t cnf_ctx;
    uint8_t recv_buff[ETH_DATA_LEN];
    uint32_t  pkt_len = 0, num_len = 0;
    //rewind raw_pkt pointer to begining of the packet holder
    uint8_t pkt_holder[ETH_PACKET_MAX_NOVLAN_SIZE] = {0};
    LOG_PRINT (LOG_INFO, "slac Job running(%d)...\n", slac->quit);
    while(slac->quit == 0)
    {
        // Todo not CM_SLAC_MATCH_CNF
        printd("v2g_slac_loop...\n");
        int result = mme_init(&cnf_ctx, CM_SLAC_MATCH_CNF, recv_buff, ETH_DATA_LEN);
        if ( (mme_receive_timeout (iface, pkt_holder, &pkt_len, 1, 0)) == MME_SUCCESS)
        {
            MME_t *recv_mme = (MME_t *) pkt_holder;
            // Check if destination mac address is same as my own
            if (mme_check_dest_mac (recv_mme->mme_dest, iface->mac) == 0)
            {
                mme_remove_header(recv_mme->mmtype, pkt_holder, pkt_len, &cnf_ctx);

                if (recv_mme->mmtype == CM_SLAC_PARM_REQ)
                {
                    result = evse_cm_slac_parm_req_cnf(iface, &slac->evse, &cnf_ctx, recv_mme->mme_src);
                }else 
                if (recv_mme->mmtype == CM_START_ATTEN_CHAR_IND)
                {
                    result = evse_cm_start_atten_char_ind_receive(&cnf_ctx, recv_mme->mme_src);
                    pev_item *tmp = search_pev_item_list(slac->evse.pev_item_head, recv_mme->mme_src);
                    if ( tmp != NULL)
                    {
                        if (tmp->matching_state == 0)
                        {
                            /**
                             * TT_EVSE_match_MNBC=600ms, TT_match_response=200ms.
                             * Wait 600ms to send first CM_ATTEN_CHAR.IND
                             * and then resend CM_ATTEN_CHAR.IND every 200ms
                             * (C_EV_match retry times) if EVSE
                             *  didnot receive CM_ATTEN_CHAR.RSP.
                             */
                            tmp->matching_state = RECEIVED_CM_START_ATTEN_CHAR_IND;
                            make_evse_timer (tmp, TT_EVSE_match_MNBC * 100, TT_match_response);
                        }else{
                            LOG_PRINT (LOG_VERBOSE, "[EVSE] receives CM_START_ATTEN_CHAR_IND tmp->matching_state == %d.\n", tmp->matching_state);
                        }
                    }else{
                        LOG_PRINT (LOG_VERBOSE, "[EVSE] receives CM_START_ATTEN_CHAR_IND from unknown PEV that didn't send CM_SLAC_PARM.REQ.\n");
                    }
                }else 
                if (recv_mme->mmtype == CM_MNBC_SOUND_IND)
                {
                    result = evse_cm_mnbc_sound_ind_receive (&cnf_ctx);
                }else 
                if (recv_mme->mmtype == CM_ATTEN_PROFILE_IND)
                {
                    result = evse_cm_atten_profile_ind_receive (&slac->evse, &cnf_ctx);
                }else 
                if (recv_mme->mmtype == CM_ATTEN_CHAR_RSP)
                {
                    result = evse_cm_atten_char_rsp_receive (&slac->evse, &cnf_ctx);
                }else 
                if (recv_mme->mmtype == CM_SLAC_MATCH_REQ)
                {
                    pev_item *tmp = search_pev_item_list(slac->evse.pev_item_head, recv_mme->mme_src);
                    if (tmp != NULL)
                    {
                        result = evse_cm_slac_match_req_receive_cnf_send(iface, &cnf_ctx, &slac->evse, tmp);
                        if (!result)
                        {
                            tmp->matching_state = SENT_CM_SLAC_MATCH_CNF;
                            /**
                             * TT_match_join is the max time between
                             * CM_SLAC_MATCH.CNF and link establishment.
                             * This timer is to send VS_GET_STATUS.REQ.
                             */
                            make_evse_timer (tmp, TT_match_join, 0);
                        }
                    }
                    else
                    {
                        LOG_PRINT (LOG_VERBOSE, "[EVSE] receives CM_SLAC_MATCH.REQ from unknown PEV.\n");
                    }
                }
                else if (recv_mme->mmtype == VS_GET_STATUS_CNF)
                {
                    LOG_PRINT (LOG_DEBUG, "[EVSE] receives VS_GET_STATUS.CNF\n");
                    vs_get_status_cnf vs_cnf;
                    memset (&vs_cnf, 0, sizeof (vs_cnf));
                    result = mme_pull (&cnf_ctx, &vs_cnf, sizeof (vs_cnf), &num_len);
                    if (result != MME_SUCCESS)
                    {
                        LOG_PRINT (LOG_VERBOSE, "mme_pull() error, result=%d\n", result);
                        return NULL;
                    }
                    LOG_PRINT (LOG_DEBUG, "VS_GET_STATUS_CNF.Link_State = %d (1:connected,  0:disconnected)\n", vs_cnf.link_state);
                    if (vs_cnf.link_state)
                    {
                        slac->result = CONNECT_LINK;
                        LOG_PRINT (LOG_DEBUG, "-----------------------------------\n");
                        LOG_PRINT (LOG_DEBUG, "[EVSE] is connected to PEV.\n");
                        LOG_PRINT (LOG_DEBUG, "[EVSE] Switched to Matched state.\n");
                        LOG_PRINT (LOG_DEBUG, "-----------------------------------\n");
                        LOG_PRINT (LOG_DEBUG, "[EVSE] Charging...(%d)\n\n",slac->result);

                        secc_set_state(secc, GUN_SLACOK);
                        
                        sleep_ms (CHARGING_TIME);
                        result = vs_get_status_cnf_receive (iface);
                        if (result == ERROR_LINK)
                        {
                            slac->result = ERROR_LINK;
                            LOG_PRINT (LOG_VERBOSE, "After charging, VS_GET_STATUS_CNF received timeout!\n");
                        }
                        if (result == DISCONNECT_LINK)
                        {
                            slac->result = DISCONNECT_LINK;
                            LOG_PRINT (LOG_DEBUG, "[EVSE] Charging complete!\n");
                            LOG_PRINT (LOG_DEBUG, "[EVSE] was disconnected from PEV.\n");
                        }
                        LOG_PRINT (LOG_INFO, "slac Job done.\n");
                    }
                }
                else
                {
                    LOG_PRINT (LOG_VERBOSE, "received the wrong mmtype mme, mmtype = 0x%x\n", recv_mme->mmtype);
                }
            }
        }
    }
    LOG_PRINT (LOG_INFO, "slac Job done.\n");
    v2g_slac_exit(v2g);
    return NULL;
}

void v2g_slac_exit(void* argv)
{
    v2g_context_t* v2g = (v2g_context_t*)argv;
    secc_port_t *secc = (secc_port_t*)v2g->secc;
    evse_slac_t* slac = (evse_slac_t*)&v2g->evse_slac;
    if (slac)
    {
        LOG_PRINT (LOG_INFO, "slac quit.\n");
        if (slac->quit == 0)
        {
            slac->quit = 1;
            mthread_exit(&slac->thread);
        }
        slac->result = 0;
        pev_item *current = slac->evse.pev_item_head;
        while (current->next != NULL)
        {
            current = current->next;
            switch (current->matching_state)
            {
            case 1:
                timer_delete(&current->cm_start_atten_char_ind_timer_id);
                break;
            case 5:
                timer_delete(&current->cm_slac_match_cnf_timer_id);
                break;
            }
            {
                struct sigaction sa;
                sa.sa_handler = SIG_DFL; // 设置为默认处理
                sigaction(SIGRTMIN+1, &sa, NULL);
            }
        }
        close_interface(&slac->iface);
    }
}
