#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <stdint.h>

#define prinf_info printf
#if 0
#define debug printf
#else
#define debug(...)
#endif

#define SET_BIT (0x01 << 0)
#define GET_BIT (0x01 << 1)
#define LEVEL_BIT (0x01 << 2)
#define SET_ALL_BIT (0x01 << 16)
#define HELP_BIT (0x01 << 17)

static const char *const level_string[] = {"emerg", "alert", "crit", "err", "warning", "notice", "info", "debug"};

static void print_help(void)
{
    char *help_info = "Usage:\n"
                      "\tset log level: ulog -s \"process1 process2 ...\" -l level\n"
                      "\t               ulog --allset level\n"
                      "\tget log level: ulog -g process\n\n"
                      "level options: emerg alert crit err warning notice info debug\n"
                      "                 0     1    2    3     4      5     6     7\n";

    prinf_info("%s\n\n", help_info);
}

static void print_err(void)
{
    prinf_info("command format err!!!\n");
    prinf_info("Usage: ulog [-s \"process1 process2 ...\"] [-l level] [-g process] [setall level]\n");
    prinf_info("Try \'wget --help\' for more info.\n");
}

static int32_t exe_cmd(char *cmd, char *buff, int buff_len)
{
    FILE *file;
    file = popen(cmd, "r");
    debug("cmd = %s\n", cmd);
    if (NULL != file)
    {
        buff_len = fread(buff, 1, buff_len, file);
        pclose(file);
        return buff_len;
    }
    else
    {
        return 0;
    }
}

static int32_t set_single_log_level(char *name, int lv)
{
    char cmd[200] = {0};
    char return_buff[200] = {0};

    snprintf(cmd, sizeof(cmd), "ubus call %s loglevel_set \'{\"loglevel\":%d}\'", name, lv);

    if ((exe_cmd(cmd, return_buff, sizeof(return_buff) - 1) > 0) && (strstr(return_buff, "\"result\": 0") != NULL))
    {
        return 0;
    }
    else
    {
        return -1;
    }
}

static int32_t set_multi_log_level(char *name, int lv)
{
    if (name == NULL)
    {
        return -1;
    }
    char *temp = strtok(name, " ");
    if (temp != NULL)
    {
        do
        {
            if (0 != set_single_log_level(temp, lv))
            {
                prinf_info("set %s level = %d err\n", temp, lv);
            }
            else
            {
                debug("set %s level = %d ok\n", temp, lv);
            }
            temp = strtok(NULL, " ");
        } while (temp != NULL);
    }
    return 0;
}

static int32_t set_all_log_level(int lv)
{
    char cmd[200] = {0};
    snprintf(cmd, sizeof(cmd), "ubus send loglevel_setall \'{\"loglevel\":%d}\'", lv);
    debug("%s\n", cmd);

    return system(cmd);
}

static int32_t get_log_level(char *name)
{
    int re = -1;
    char cmd[100] = {0};
    char return_buff[200] = {0};
    const char *target = "\"loglevel\": ";
    char *tmp = NULL;
    snprintf(cmd, sizeof(cmd), "ubus call %s loglevel_get", name);

    if ((exe_cmd(cmd, return_buff, sizeof(return_buff) - 1) > 0) && ((tmp = strstr(return_buff, target)) != NULL))
    {
        tmp = tmp + strlen(target);
        if ((*tmp >= '0') && (*tmp <= '9'))
        {
            re = atoi(tmp);
            if ((re < 0) || (re >= sizeof(level_string) / sizeof(level_string[0])))
            {
                re = -1;
            }
            else
            {
                prinf_info("%s->loglevel:%s[%d]\n", name, level_string[re], re);
            }
        }
    }
    else
    {
        prinf_info("%s get err\n", name);
    }

    return re;
}

static int ls_to_lv(char *ls)
{
    int re = -1;
    if (ls != NULL)
    {
        int i;
        for (i = 0; i < sizeof(level_string) / sizeof(level_string[0]); i++)
        {
            if (strcmp(ls, level_string[i]) == 0)
            {
                re = i;
                break;
            }
        }

        if ((re < 0) && (ls[0] >= '0' && ls[0] <= '9'))
        {
            re = atoi(ls);
            if ((re < 0) || (re >= sizeof(level_string) / sizeof(level_string[0])))
            {
                re = -1;
            }
        }
    }

    return re;
}

int main(int argc, char *argv[])
{

    int opt = 0;
    int opt_index = 0;
    char argv_buff[300] = {0};
    uint32_t par_flag = 0;
    int lv = -1;

    char *optstr = "s:g:l:";
    static struct option long_options[] = {
        {"allset", required_argument, NULL, 1},
        {"help", no_argument, NULL, 2},
        {NULL, 0, NULL, 0},
    };

    while ((opt = getopt_long_only(argc, argv, optstr, long_options, &opt_index)) != -1)
    {
        switch (opt)
        {
        case 1:
            memset(argv_buff, 0, sizeof(argv_buff));
            memcpy(argv_buff, optarg, strlen(optarg));
            lv = ls_to_lv(optarg);
            par_flag = par_flag | SET_ALL_BIT;
            break;

        case 2:
            par_flag = par_flag | HELP_BIT;
            break;

        case 'g':
            memset(argv_buff, 0, sizeof(argv_buff));
            memcpy(argv_buff, optarg, strlen(optarg));
            par_flag = par_flag | GET_BIT;
            break;

        case 's':
            memset(argv_buff, 0, sizeof(argv_buff));
            memcpy(argv_buff, optarg, strlen(optarg));
            par_flag = par_flag | SET_BIT;
            break;

        case 'l':
            lv = ls_to_lv(optarg);
            par_flag = par_flag | LEVEL_BIT;
            break;

        default:
            break;
        }
        debug("opt = %d, par = %s optarg = %x\n", opt, optarg, par_flag);
    }

    switch (par_flag)
    {
    case SET_BIT | LEVEL_BIT: // set
        set_multi_log_level(argv_buff, lv);
        break;

    case GET_BIT: // get
        get_log_level(argv_buff);
        break;

    case SET_ALL_BIT: // set all
        set_all_log_level(lv);
        break;

    case HELP_BIT: // help
        print_help();
        break;

    default: // err
        print_err();
        break;
    }
    return 0;
}
