//
// Created by xajhuang on 2022/5/11.
//
#include <misc.h>
#include <zlog.h>
#include "user_info.h"
#include "user_errno.h"

static PUSER_INFO_CONTEXT g_pUserByIdList   = NULL;
static PUSER_INFO_CONTEXT g_pUserByTagsList = NULL;
static PUSER_INFO_CONTEXT g_pUserByMacList  = NULL;
static uv_rwlock_t        g_userLock;

static USER_PARAMS g_userInfo[] = {
    {0, 400, 24, 371, {0x00, 0x0C, 0x01, 0x02, 0x00, 0x03}, "user1",    "password1"},
    {1, 300, 16, 12,  {0x00, 0x0C, 0x01, 0x02, 0x00, 0x02}, "user2",    "password1"},
    {2, 300, 14, 15,  {0x00, 0x0C, 0x01, 0x02, 0x00, 0x04}, "user3",    "password1"},
    {3, 400, 24, 17,  {0x00, 0x0C, 0x01, 0x02, 0x00, 0x04}, "xajhuang", "aaaHuang1"},
};

void user_info_init() {
    uv_rwlock_init(&g_userLock);

    //user_info_add(0, &g_userInfo[0]);
    //user_info_add(1, &g_userInfo[1]);
    //user_info_add(2, &g_userInfo[2]);
    //user_info_add(3, &g_userInfo[3]);
}

void user_info_change_status(PUSER_INFO_CONTEXT pInfo, USER_STATUS status) {
    pInfo->user_status = status;
}

int user_info_add(unsigned int userid, PUSER_PARAMS pInfo) {
    PUSER_INFO_CONTEXT pUser;

    uv_rwlock_rdlock(&g_userLock);
    HASH_FIND(hh_id, g_pUserByIdList, &userid, sizeof(unsigned int), pUser);
    uv_rwlock_rdunlock(&g_userLock);

    if (pUser != NULL) {
        return -ERR_ITEM_EXISTS;
    }

    if (pInfo) {
        PUSER_INFO_CONTEXT pList = (PUSER_INFO_CONTEXT)malloc(sizeof(USER_INFO_CONTEXT));

        if (!pList) {
            return ERR_MALLOC_MEMORY;
        }

        memset(pList, 0, sizeof(USER_INFO_CONTEXT));

        pList->user_status            = STATUS_USER_NEW;
        pList->userid                 = userid;
        pList->vxlan.vni              = pInfo->vni;
        pList->vxlan.q1               = pInfo->q1;
        pList->vxlan.q2               = pInfo->q2;
        pList->user_info.pppoe_user   = strdup(pInfo->pppoe_user);
        pList->user_info.pppoe_passwd = strdup(pInfo->pppoe_passwd);
        memcpy(pList->mac_addr, pInfo->mac_addr, 6);
        memcpy(pList->user_info.mac_addr, pInfo->mac_addr, 6);

        pList->session.status = STATUS_TASK_INIT;

        uv_rwlock_wrlock(&g_userLock);
        HASH_ADD(hh_id, g_pUserByIdList, userid, sizeof(unsigned int), pList);
        HASH_ADD(hh_vxlan, g_pUserByTagsList, vxlan, sizeof(VXLAN_TAG), pList);
        HASH_ADD(hh_mac, g_pUserByMacList, mac_addr, 6, pList);
        uv_rwlock_wrunlock(&g_userLock);

        dzlog_debug(
            "Add user: id = %u, vni = %u, q1 = %u, q2 = %u, ppp_user = %s, mac = %02X:%02X:%02X:%02X:%02X:%02X\n",
            userid,
            pInfo->vni,
            pInfo->q1,
            pInfo->q2,
            pInfo->pppoe_user,
            pList->mac_addr[0],
            pList->mac_addr[1],
            pList->mac_addr[2],
            pList->mac_addr[3],
            pList->mac_addr[4],
            pList->mac_addr[5]);
    }

    return ERR_SUCCESS;
}

struct netif *get_user_nic_by_mac(const unsigned char mac[6]) {
    PUSER_INFO_CONTEXT pUser;
    uv_rwlock_rdlock(&g_userLock);
    HASH_FIND(hh_mac, g_pUserByMacList, mac, 6, pUser);
    uv_rwlock_rdunlock(&g_userLock);

    if (pUser) {
        return pUser->session.nicif;
    }

    return NULL;
}

PUSER_INFO_CONTEXT get_user_by_tag(PVXLAN_TAG pTag) {
    if (pTag) {
        PUSER_INFO_CONTEXT pUser;

        uv_rwlock_rdlock(&g_userLock);
        HASH_FIND(hh_vxlan, g_pUserByTagsList, pTag, sizeof(VXLAN_TAG), pUser);
        uv_rwlock_rdunlock(&g_userLock);

        if (pUser) {
            return pUser;
        }
    }

    return NULL;
}

void user_info_remove(unsigned int userid) {
    PUSER_INFO_CONTEXT pInfo;

    uv_rwlock_rdlock(&g_userLock);
    HASH_FIND(hh_id, g_pUserByIdList, &userid, sizeof(unsigned int), pInfo);
    uv_rwlock_rdunlock(&g_userLock);

    if (pInfo) {
        pInfo->session.retry.timeout = 0;
        pInfo->session.status        = STATUS_TASK_DELETE;
    }
}

void user_info_delete(unsigned int userid) {
    PUSER_INFO_CONTEXT pInfo;

    uv_rwlock_rdlock(&g_userLock);
    HASH_FIND(hh_id, g_pUserByIdList, &userid, sizeof(unsigned int), pInfo);
    uv_rwlock_rdunlock(&g_userLock);

    if (pInfo) {
        uv_rwlock_wrlock(&g_userLock);
        HASH_DELETE(hh_id, g_pUserByIdList, pInfo);
        HASH_DELETE(hh_vxlan, g_pUserByTagsList, pInfo);
        uv_rwlock_wrunlock(&g_userLock);
    }
}

int user_info_modify(unsigned int userid, PUSER_INFO pUser) {
    PUSER_INFO_CONTEXT pInfo;

    uv_rwlock_rdlock(&g_userLock);
    HASH_FIND(hh_id, g_pUserByIdList, &userid, sizeof(unsigned int), pInfo);
    uv_rwlock_rdunlock(&g_userLock);

    if (pInfo) {
        int isUpgrade = FALSE;

        if (memcmp(pInfo->user_info.mac_addr, pUser->mac_addr, 6) != 0) {
            memcpy(pInfo->user_info.mac_addr, pUser->mac_addr, 6);
            isUpgrade = TRUE;
        }

        if (strcmp(pInfo->user_info.pppoe_user, pUser->pppoe_user) != 0) {
            free((void *)pInfo->user_info.pppoe_user);
            pInfo->user_info.pppoe_user = strdup(pUser->pppoe_user);
            isUpgrade                   = TRUE;
        }

        if (strcmp(pInfo->user_info.pppoe_passwd, pUser->pppoe_passwd) != 0) {
            free((void *)pInfo->user_info.pppoe_passwd);
            pInfo->user_info.pppoe_passwd = strdup(pUser->pppoe_passwd);
            isUpgrade                     = TRUE;
        }

        if (isUpgrade) {
            pInfo->user_status = STATUS_USER_MODIFY;
        }

        return ERR_SUCCESS;
    } else {
        return -ERR_ITEM_UNEXISTS;
    }
}

PUSER_INFO user_info_get_by_userid(unsigned int userid) {
    PUSER_INFO_CONTEXT pInfo;

    HASH_FIND(hh_id, g_pUserByIdList, &userid, sizeof(unsigned int), pInfo);

    if (pInfo) {
        return &pInfo->user_info;
    }

    return NULL;
}

PUSER_INFO_CONTEXT get_all_user_by_id() {
    return g_pUserByIdList;
}

PUSER_INFO_CONTEXT get_all_user_by_tag() {
    return g_pUserByTagsList;
}

uv_rwlock_t *get_user_lock() {
    return &g_userLock;
}