#ifdef __KERNEL__
#include "debug_info.h"
#include "log.h"

#define TOLOWER(x) ((x) | 0x20)

static const struct file_operations g_DbgSeq_fops;

static struct proc_dir_entry*  g_DebugProcFS;

int InitDebugInfoProc(const char* proc_dir_name)
{
    if(proc_dir_name)
    {
        g_DebugProcFS = proc_mkdir(proc_dir_name, NULL);

        if(g_DebugProcFS != NULL)
        {
            return ERR_DBGFS_NO_ERROR;
        }
    }

    return ERR_DBGFS_INIT;
}

void DeInitDebugInfoProc(void)
{
    remove_proc_entry(g_DebugProcFS->name, NULL);
}

int DebugFsRegister(PDBGFS_PRIV pv)
{
    struct proc_dir_entry* file;

    if(pv == NULL)
    {
        return ERR_PARA_OUTOFRANGE;
    }

    file = create_proc_entry(pv->name, 0, g_DebugProcFS);

    if(!file)
    {
        return ERR_DBGFS_REGISTER;
    }
    else
    {
        file->proc_fops = &g_DbgSeq_fops;
        file->data = pv;
    }

    return ERR_DBGFS_NO_ERROR;
}

int DebugFsUnRegister(PDBGFS_PRIV pv)
{

    if(strlen(pv->name) > 0)
    {
        remove_proc_entry(pv->name, g_DebugProcFS);
        return ERR_DBGFS_NO_ERROR;
    }

    return ERR_DBGFS_UNREGISTER;
}

static int DbgSeqOpen(struct inode* inode, struct file* file)
{
    PDBGFS_PRIV priv = (PDBGFS_PRIV)(PDE(inode)->data);
    
    return single_open(file, priv->show, priv);
}

static int DbgSeqRelease(struct inode* inode, struct file* file)
{
    return single_release(inode, file);
}

static int GetValueBase(unsigned char* pBuf)
{
    int i = 0;
    int strLen = strlen(pBuf);

    if(pBuf)
    {
        if(pBuf[0] == '0' && pBuf[1] == 'x')
        {
            return 16;
        }
        
        for(i = 0; i < strLen; i++)
        {
            if(TOLOWER(pBuf[i]) >= 'a' && TOLOWER(pBuf[i]) <= 'f')
            {
                return 16;
            }
        }
    }

    return 10;
}

int GetStringParamValue(unsigned char* pBuf, int index, unsigned char* pValue, unsigned int maxBytes)
{
    char tmpBuf[128];
    char* token;
    char* s = tmpBuf;
    int paramIndex = 0;

    if(pBuf == NULL || index < 0 || pValue == NULL)
    {
        return ERR_PARA_OUTOFRANGE;
    }

    memset(pValue, 0, maxBytes);

    strcpy(tmpBuf, pBuf);

    for(token = strsep(&s, " ");
        token != NULL;
        token = strsep(&s, " "))
    {
        if(token != NULL)
        {
            if(index != paramIndex)
            {
                paramIndex += 1;
                continue;
            }

            strcpy(pValue, token);

            return ERR_DBGFS_NO_ERROR;
        }
    }

    return ERR_DBGFS_WRITEOPTS;
}


int GetIntParamValue(unsigned char* pBuf, int index)
{
    char tmpBuf[128];
    char* token;
    char* s = tmpBuf;
    int paramIndex = 0;
    int paramValue = -1;

    if(pBuf == NULL || index < 0)
    {
        return -1;
    }

    strcpy(tmpBuf, pBuf);

    for(token = strsep(&s, " ");
        token != NULL;
        token = strsep(&s, " "))
    {
        if(token != NULL)
        {
            if(index != paramIndex)
            {
                paramIndex += 1;
                continue;
            }

            if(strict_strtol(token, GetValueBase(token), (long*)&paramValue))
            {
                LOG_EX(LOG_Error, "strict_strtol [%s] Error\n", token);
                return -1;
            }
            else
            {
                return paramValue;
            }
        }
    }

    return -1;
}

static int ProcessInputContent(struct seq_file* seq, unsigned char* pBuf, PDBGFS_PRIV pv)
{
    int strLen = strlen(pBuf) - 1;
    char tmpBuf[MAX_CMD_LEN], cmdBuf[32], paramBuf[64];
    int i = 0;
    int bIsCmd = FALSE;

    memset(tmpBuf, 0, MAX_CMD_LEN);
    memset(cmdBuf, 0, 32);
    memset(paramBuf, 0, 64);

    strcpy(tmpBuf, pBuf);

    if(pBuf == NULL && pv == NULL)
    {
        return ERR_PARA_OUTOFRANGE;
    }

    for(i = 0; i < strLen; i++)
    {
        if(tmpBuf[i] == ' ')
        {
            strncpy(cmdBuf, tmpBuf, i);
            strncpy(paramBuf, &tmpBuf[i + 1], MAX(strLen - i - 1, 0));
            bIsCmd = TRUE;
            break;
        }
    }

    if(!bIsCmd)
    {
        if(strict_strtoul(pBuf, GetValueBase(pBuf), (long unsigned int*)&pv->params))
        {
            LOG_EX(LOG_Error, "strict_strtoul [%s] base %d Error\n", pBuf, GetValueBase(pBuf));
            return -ERR_DBGFS_WRITEOPTS;
        }
    }
    else
    {
        if(pv->ioctl != NULL)
        {
            pv->ioctl(seq, cmdBuf, paramBuf);
        }
    }

    return ERR_PARA_OUTOFRANGE;
}


static ssize_t DgbSeqOptionsWrite(struct file* file, const char __user* userbuf,
                                  size_t count, loff_t* data)
{
    char buf[64];
    struct seq_file* seq = (struct seq_file*)file->private_data;
    PDBGFS_PRIV pv = (PDBGFS_PRIV)seq->private;

    if(count >= sizeof(buf))
    {
        LOG_EX(LOG_Error, "Input Params Error:count = %d, max size = %d\n", count,
               sizeof(buf));
        return -ERR_PARA_OUTOFRANGE;
    }

    if(copy_from_user(buf, userbuf, count))
    {
        LOG_EX(LOG_Error, "Copy Data To Kernel Error\n");
        return -ERR_DBGFS_WRITEOPTS;
    }

    if(buf[0] == 'h' &&
       buf[1] == 'e' &&
       buf[2] == 'l' &&
       buf[3] == 'p')
    {
        if(pv->help != NULL)
        {
            pv->help((struct seq_file*)file->private_data, pv);
        }

        return count;
    }

    buf[count] = 0x00;

    if(ProcessInputContent(seq, buf, pv) == ERR_PARA_OUTOFRANGE)
    {
//        LOG_EX(LOG_Debug, "Process [%s] Options Cmd[%s] Params [%u(hex:%08X)]\n",
//            pv->name,
//            (strlen(pv->cmd) <= 0) ? "NA" : pv->cmd,
//            pv->params, pv->params);
    }
    else
    {
        LOG_EX(LOG_Error, "Input [%s] Process Error\n", buf);
    }

    return count;
}

static const struct file_operations g_DbgSeq_fops =
{
    .owner   = THIS_MODULE,
    .open    = DbgSeqOpen,
    .read    = seq_read,
    .write   = DgbSeqOptionsWrite,
    .llseek  = seq_lseek,
    .release = DbgSeqRelease,
};

#endif