#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/stat.h>
#include <unistd.h>

static void set_defaults(Config *cfg)
{
    memset(cfg, 0, sizeof(*cfg));
    snprintf(cfg->host, sizeof(cfg->host), "127.0.0.1");
    cfg->port = 9091;
    cfg->poll_interval_ms = 2000;
    cfg->show_splash = 1;
}

static int resolve_path(char *out, size_t n)
{
    const char *xdg = getenv("XDG_CONFIG_HOME");
    if (xdg && xdg[0]) {
        snprintf(out, n, "%s/transtui/config.ini", xdg);
        return 0;
    }
    const char *home = getenv("HOME");
    if (!home || !home[0])
        return -1;
    snprintf(out, n, "%s/.config/transtui/config.ini", home);
    return 0;
}

static void mkdir_parents(const char *path)
{
    char tmp[1024];
    snprintf(tmp, sizeof(tmp), "%s", path);
    for (char *p = tmp + 1; *p; p++) {
        if (*p == '/') {
            *p = '\0';
            mkdir(tmp, 0755);
            *p = '/';
        }
    }
}

static int write_default_file(const char *path)
{
    char dir[1024];
    snprintf(dir, sizeof(dir), "%s", path);
    char *slash = strrchr(dir, '/');
    if (slash) {
        *slash = '\0';
        mkdir_parents(dir);
        mkdir(dir, 0755);
    }

    FILE *f = fopen(path, "w");
    if (!f)
        return -1;
    fprintf(f,
            "# transtui config\n"
            "host = 127.0.0.1\n"
            "port = 9091\n"
            "username =\n"
            "password =\n"
            "poll_interval_ms = 2000\n"
            "show_splash = 1\n");
    fclose(f);
    return 0;
}

static char *trim(char *s)
{
    while (*s == ' ' || *s == '\t')
        s++;
    size_t n = strlen(s);
    while (n > 0 && (s[n - 1] == ' ' || s[n - 1] == '\t' || s[n - 1] == '\r' || s[n - 1] == '\n'))
        s[--n] = '\0';
    return s;
}

static void parse_file(Config *cfg, FILE *f)
{
    char line[1024];
    while (fgets(line, sizeof(line), f)) {
        char *l = trim(line);
        if (l[0] == '\0' || l[0] == '#' || l[0] == ';')
            continue;
        char *eq = strchr(l, '=');
        if (!eq)
            continue;
        *eq = '\0';
        char *key = trim(l);
        char *val = trim(eq + 1);

        if (strcmp(key, "host") == 0)
            snprintf(cfg->host, sizeof(cfg->host), "%s", val);
        else if (strcmp(key, "port") == 0)
            cfg->port = atoi(val);
        else if (strcmp(key, "username") == 0)
            snprintf(cfg->username, sizeof(cfg->username), "%s", val);
        else if (strcmp(key, "password") == 0)
            snprintf(cfg->password, sizeof(cfg->password), "%s", val);
        else if (strcmp(key, "poll_interval_ms") == 0)
            cfg->poll_interval_ms = atoi(val);
        else if (strcmp(key, "show_splash") == 0)
            cfg->show_splash = (strcmp(val, "0") != 0 && strcasecmp(val, "false") != 0);
        else if (strcmp(key, "session_cache_set") == 0)
            cfg->session_cache_set = (strcmp(val, "0") != 0 && strcasecmp(val, "false") != 0);
        else if (strcmp(key, "cache_download_dir") == 0)
            snprintf(cfg->cache_download_dir, sizeof(cfg->cache_download_dir), "%s", val);
        else if (strcmp(key, "cache_speed_down") == 0)
            cfg->cache_speed_down = atol(val);
        else if (strcmp(key, "cache_speed_down_enabled") == 0)
            cfg->cache_speed_down_enabled = (strcmp(val, "0") != 0 && strcasecmp(val, "false") != 0);
        else if (strcmp(key, "cache_speed_up") == 0)
            cfg->cache_speed_up = atol(val);
        else if (strcmp(key, "cache_speed_up_enabled") == 0)
            cfg->cache_speed_up_enabled = (strcmp(val, "0") != 0 && strcasecmp(val, "false") != 0);
        else if (strcmp(key, "cache_alt_enabled") == 0)
            cfg->cache_alt_enabled = (strcmp(val, "0") != 0 && strcasecmp(val, "false") != 0);
        else if (strcmp(key, "cache_alt_down") == 0)
            cfg->cache_alt_down = atol(val);
        else if (strcmp(key, "cache_alt_up") == 0)
            cfg->cache_alt_up = atol(val);
        else if (strcmp(key, "cache_alt_time_enabled") == 0)
            cfg->cache_alt_time_enabled = (strcmp(val, "0") != 0 && strcasecmp(val, "false") != 0);
        else if (strcmp(key, "cache_alt_time_begin") == 0)
            cfg->cache_alt_time_begin = atoi(val);
        else if (strcmp(key, "cache_alt_time_end") == 0)
            cfg->cache_alt_time_end = atoi(val);
        else if (strcmp(key, "cache_seed_ratio_limit") == 0)
            cfg->cache_seed_ratio_limit = atof(val);
        else if (strcmp(key, "cache_seed_ratio_enabled") == 0)
            cfg->cache_seed_ratio_enabled = (strcmp(val, "0") != 0 && strcasecmp(val, "false") != 0);
    }
}

int config_load(Config *cfg, char *err, size_t errlen)
{
    set_defaults(cfg);

    if (resolve_path(cfg->path, sizeof(cfg->path)) != 0) {
        snprintf(err, errlen, "could not find HOME to resolve the config file");
        return -1;
    }

    FILE *f = fopen(cfg->path, "r");
    if (!f) {
        if (write_default_file(cfg->path) != 0) {
            snprintf(err, errlen, "could not create %s", cfg->path);
            return -1;
        }
        return 0; /* defaults already set */
    }

    parse_file(cfg, f);
    fclose(f);
    if (cfg->port <= 0 || cfg->port > 65535)
        cfg->port = 9091;
    if (cfg->poll_interval_ms < 250)
        cfg->poll_interval_ms = 250;
    return 0;
}

int config_save(const Config *cfg, char *err, size_t errlen)
{
    if (!cfg->path[0]) {
        snprintf(err, errlen, "no config path known");
        return -1;
    }
    FILE *f = fopen(cfg->path, "w");
    if (!f) {
        snprintf(err, errlen, "could not write %s", cfg->path);
        return -1;
    }
    fprintf(f,
            "# transtui config\n"
            "host = %s\n"
            "port = %d\n"
            "username = %s\n"
            "password = %s\n"
            "poll_interval_ms = %d\n"
            "show_splash = %d\n"
            "session_cache_set = %d\n"
            "cache_download_dir = %s\n"
            "cache_speed_down = %ld\n"
            "cache_speed_down_enabled = %d\n"
            "cache_speed_up = %ld\n"
            "cache_speed_up_enabled = %d\n"
            "cache_alt_enabled = %d\n"
            "cache_alt_down = %ld\n"
            "cache_alt_up = %ld\n"
            "cache_alt_time_enabled = %d\n"
            "cache_alt_time_begin = %d\n"
            "cache_alt_time_end = %d\n"
            "cache_seed_ratio_limit = %.2f\n"
            "cache_seed_ratio_enabled = %d\n",
            cfg->host, cfg->port, cfg->username, cfg->password, cfg->poll_interval_ms,
            cfg->show_splash, cfg->session_cache_set, cfg->cache_download_dir,
            cfg->cache_speed_down, cfg->cache_speed_down_enabled, cfg->cache_speed_up,
            cfg->cache_speed_up_enabled, cfg->cache_alt_enabled, cfg->cache_alt_down,
            cfg->cache_alt_up, cfg->cache_alt_time_enabled, cfg->cache_alt_time_begin,
            cfg->cache_alt_time_end, cfg->cache_seed_ratio_limit, cfg->cache_seed_ratio_enabled);
    fclose(f);
    return 0;
}

static void usage(const char *prog)
{
    fprintf(stderr,
            "Usage: %s [-H host] [-P port] [-u user] [-p pass] [-i ms]\n"
            "       %s --add <magnet|url|file>  [-H host] [-P port] [-u user] [-p pass]\n"
            "  -H host   Transmission daemon address (default 127.0.0.1)\n"
            "  -P port   RPC port (default 9091)\n"
            "  -u user   RPC username\n"
            "  -p pass   RPC password\n"
            "  -i ms     Poll interval in milliseconds (default 2000)\n"
            "  -A, --add <source>\n"
            "            Add a torrent (magnet link, URL, or .torrent file), then open\n"
            "            the UI - for registering transtui as the browser handler for\n"
            "            magnet links/.torrent files.\n"
            "  -h        Show this help\n",
            prog, prog);
}

void config_apply_args(Config *cfg, int argc, char **argv, char *add_source, size_t add_source_size)
{
    if (add_source_size)
        add_source[0] = '\0';

    for (int i = 1; i < argc; i++) {
        const char *a = argv[i];
        if ((strcmp(a, "-H") == 0 || strcmp(a, "--host") == 0) && i + 1 < argc) {
            snprintf(cfg->host, sizeof(cfg->host), "%s", argv[++i]);
        } else if ((strcmp(a, "-P") == 0 || strcmp(a, "--port") == 0) && i + 1 < argc) {
            cfg->port = atoi(argv[++i]);
        } else if ((strcmp(a, "-u") == 0 || strcmp(a, "--user") == 0) && i + 1 < argc) {
            snprintf(cfg->username, sizeof(cfg->username), "%s", argv[++i]);
        } else if ((strcmp(a, "-p") == 0 || strcmp(a, "--pass") == 0) && i + 1 < argc) {
            snprintf(cfg->password, sizeof(cfg->password), "%s", argv[++i]);
        } else if ((strcmp(a, "-i") == 0 || strcmp(a, "--interval") == 0) && i + 1 < argc) {
            cfg->poll_interval_ms = atoi(argv[++i]);
        } else if ((strcmp(a, "-A") == 0 || strcmp(a, "--add") == 0) && i + 1 < argc) {
            snprintf(add_source, add_source_size, "%s", argv[++i]);
        } else if (strcmp(a, "-h") == 0 || strcmp(a, "--help") == 0) {
            usage(argv[0]);
            exit(0);
        } else {
            fprintf(stderr, "Unknown argument: %s\n\n", a);
            usage(argv[0]);
            exit(1);
        }
    }
}
