/*
 * Copyright 2017 Rockchip Electronics Co. LTD
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>
#include <string.h>

#include "mpp_parse_cfg.h"

#include "mpp_log.h"

#define ARRAY_SIZE(x)   (sizeof(x) / sizeof(x[0]))

enum CONFIG_TYPE {
    CONFIG_TYPE_OPTION,
    CONFIG_TYPE_EVENT,

    OPT_TYPE_INDEX_TYPE,
    OPT_TYPE_LOOP_NUM,

    IDX_TYPE_FRM_NUM,
    IDX_TYPE_MSEC,
};

struct options_table {
    int type;
    const char *type_str;
};

/* table for converting string to type */
static struct options_table op_tbl[] = {
    {CONFIG_TYPE_OPTION,    "[CONFIG]"},
    {CONFIG_TYPE_EVENT,     "[EVENT]"},

    {OPT_TYPE_INDEX_TYPE,   "index"},
    {OPT_TYPE_LOOP_NUM,     "loop"},

    {IDX_TYPE_FRM_NUM,      "frm"},
    {IDX_TYPE_MSEC,         "msec"},
};

struct cfg_file {
    FILE *file;
    char cache[128];
    RK_S32 cache_on;
};

/* get rid of leading and following space from string */
static char *string_trim(char *string)
{
    char *p = string;
    long len;

    if (string == NULL)
        return NULL;

    while (*p == ' ')
        p++;

    len = strlen(p);

    while (p[len - 1] == ' ')
        len--;
    p[len] = '\0';

    return p;
}

static char *read_cfg_line(struct cfg_file *cfg)
{
    int ch;
    int i;

    while (!cfg->cache_on) {
        i = 0;

        while (1) {
            ch = fgetc(cfg->file);
            if (i == 0 && ch == EOF)
                return NULL;
            else if (ch == EOF || feof(cfg->file) || ch == '\n')
                break;

            cfg->cache[i++] = ch;
        }
        cfg->cache[i] = '\0';

        /* a note, ignore and get the next line */
        if (cfg->cache[0] != '#')
            break;
    }

    cfg->cache_on = 1;
    return string_trim(cfg->cache);
}

static inline void invalid_cfg_cache(struct cfg_file *cfg)
{
    cfg->cache_on = 0;
}

static char *get_opt_value(char *line)
{
    size_t i;

    for (i = 0; i < strlen(line); ++i) {
        if (line[i] == ':')
            return string_trim(&line[i + 1]);
    }

    return NULL;
}

/* convert string to index by look up map table */
static int lookup_opt_type(char *line)
{
    size_t i;

    for (i = 0; i < ARRAY_SIZE(op_tbl); ++i) {
        if (!strncmp(op_tbl[i].type_str, line,
                     strlen(op_tbl[i].type_str))) {
            mpp_log("option type %s find\n", op_tbl[i].type_str);
            return op_tbl[i].type;
        }
    }

    return -1;
}

static int scan_event_line(struct cfg_file *cfg, struct rc_event *event)
{
    char *line = read_cfg_line(cfg);

    if (line != NULL && line[0] != '\n' && line[0] != '[') {
        sscanf(line, "%d\t%d\t%f",
               &event->idx, &event->bps, &event->fps);
        mpp_log("idx: %d, bps %u, fps: %f\n",
                event->idx, event->bps, event->fps);
        invalid_cfg_cache(cfg);
    } else {
        return -1;
    }

    return 0;
}

static int parse_events(struct cfg_file *cfg, struct rc_test_config *ea)
{
    int ret;
    int i;

    for (i = 0; i < 128; ++i) {
        ret = scan_event_line(cfg, &ea->event[i]);
        if (ret < 0) {
            ea->event_cnt = i;
            break;
        }
    }

    return 0;
}

static int parse_options(struct cfg_file *cfg, struct rc_test_config *ea)
{
    char *opt;
    int type;

    while (1) {
        opt = read_cfg_line(cfg);

        if (opt && opt[0] != '\n' && opt[0] != '[') {
            type = lookup_opt_type(opt);

            switch (type) {
            case OPT_TYPE_INDEX_TYPE:
                ea->idx_type = lookup_opt_type(get_opt_value(opt));
                break;
            case OPT_TYPE_LOOP_NUM:
                sscanf(get_opt_value(opt), "%d", &ea->loop);
                mpp_log("loop num: %d\n", ea->loop);
                break;
            default:
                break;
            }

            invalid_cfg_cache(cfg);
        } else {
            break;
        }
    }
    return 0;
}

int mpp_parse_config(char *cfg_url, struct rc_test_config *ea)
{
    struct cfg_file cfg;

    if (cfg_url == NULL || strlen(cfg_url) == 0) {
        mpp_err("invalid input config url\n");
        return -1;
    }

    cfg.file = fopen(cfg_url, "rb");
    if (cfg.file == NULL) {
        mpp_err("fopen %s failed\n", cfg_url);
        return -1;
    }
    cfg.cache_on = 0;

    while (1) {
        char *line = read_cfg_line(&cfg);

        if (!line)
            break;

        invalid_cfg_cache(&cfg);
        if (line[0] == '[') {
            int type = lookup_opt_type(line);

            switch (type) {
            case CONFIG_TYPE_EVENT:
                parse_events(&cfg, ea);
                break;
            case CONFIG_TYPE_OPTION:
                parse_options(&cfg, ea);
                break;
            default:
                mpp_err("invalid config type find\n");
                fclose(cfg.file);
                return -1;
            }
        }
    }

    fclose(cfg.file);

    return 0;
}

#ifdef PARSE_CONFIG_TEST
int main(int argc, char **argv)
{
    struct rc_test_config event_array;

    if (argc < 2) {
        mpp_err("invalid input argument\n");
        return -1;
    }

    mpp_parse_config(argv[1], &event_array);

    return 0;
}
#endif
