#include "config.hpp"
#include "common.hpp"
#include <getopt.h>


static bool add_proxy(const char* p, std::vector<proxy_t>& proxies) {
    proxy_t proxy;

    if (strncmp(p, "http://", 7) == 0) {
        p += 7;
        proxy.type = proxy_t::PROXY_TYPE_HTTP;
    } else if (strncmp(p, "socks4://", 9) == 0) {
        p += 9;
        proxy.type = proxy_t::PROXY_TYPE_SOCKS;
    } else {
        LOG("unsupported type for '%s'", p);
        return false;
    }

    if (!str2addr(p, &proxy.addr, true)) {
        LOG("cannot parse address '%s'", p);
        return false;
    }
    strcpy(proxy.addrstr, addr2str(&proxy.addr, true));

    for (std::vector<proxy_t>::iterator it = proxies.begin(); it != proxies.end(); ++it) {
        if (it->type == proxy.type && strcmp(it->addrstr, proxy.addrstr) == 0) {
            LOG("skipping duplicate '%s'", p);
            return true;
        }
    }

    static unsigned id = 0;
    proxy.id = id++;
    proxies.push_back(proxy);
    return true;
}


static bool load_proxies(const char* fn, std::vector<proxy_t>& proxies) {
    FILE* fp = fopen(fn, "r");
    if (!fp) {
        LOG_ERRNO("cannot open '%s'", fn);
        return false;
    }

    char* line = NULL;
    size_t len = 0;
    while (getline(&line, &len, fp) != -1) {
        char* p = line;
        while (*p == ' ' || *p == '\t') ++p;
        if (*p == '\0' || *p == '#' || *p == '\n') continue;
        char* e = strchrnul(p, '\n');
        while (e != p && (*e == '\n' || *e == '\r' || *e == ' ')) { *e = '\0'; --e; }
        if (!add_proxy(p, proxies)) {
            free(line);
            fclose(fp);
            return false;
        }
    }
    free(line);

    fclose(fp);
    return true;
}


bool parse_config(config_t& config, in_port_t& port, int argc, char** argv) {
    static struct option long_options[] = {
        {"port",    required_argument, 0, 'p'},
        {"min",     required_argument, 0, 'm'},
        {"pool",    required_argument, 0, 'o'},
        {"proxies", required_argument, 0, 'f'},
        {0, 0, 0, 0}
    };

    port = 0;
    config.mode = config_t::MODE_CHAIN;
    int min_proxies = -1;
    const char* conffile = NULL;

    while (1) {
        int i;
        int c = getopt_long(argc, argv, "p:m:o:f:", long_options, &i);
        if (c == -1) break;
        switch (c) {
            case 'p':
                i = atoi(optarg);
                if (i < 1 || i > 0xffff) return false;
                port = i;
                break;
            case 'o':
                config.mode = config_t::MODE_POOL;
                // fall thru
            case 'm':
                if (min_proxies >= 0) return false;
                min_proxies = atoi(optarg);
                if (min_proxies < 0 || (!min_proxies && strcmp(optarg, "0"))) return false;
                break;
            case 'f':
                if (conffile) return false;
                conffile = optarg;
                break;
            default:
                return false;
                break;
        }
    }

    if (conffile != NULL) {
        if (!load_proxies(conffile, config.proxies)) {
            return false;
        }
    }

    while (optind < argc) {
        if (!add_proxy(argv[optind++], config.proxies)) {
            return false;
        }
    }

    if (min_proxies < 0) {
        config.min_proxies = (config.mode == config_t::MODE_CHAIN)? config.proxies.size(): 1; // default
    } else {
        config.min_proxies = (unsigned)min_proxies;
    }

    if (!port) return false;
    if (config.min_proxies > config.proxies.size()) return false;
    if (!config.min_proxies && config.mode == config_t::MODE_POOL) return false;
    if (config.proxies.empty()) {
        LOG("WARNING: no proxies configured!");
    } else if (config.mode == config_t::MODE_CHAIN) {
        LOG("Will use at least %u proxies of %zu", config.min_proxies, config.proxies.size());
    } else {
        LOG("Will use at least %u proxies out of %zu", config.min_proxies, config.proxies.size());
    }
    return true;
}