#include "config.hpp"
#include "imap.hpp"
#include "main.hpp"
#include <fstream>


static std::string str_pop(const char*& s) { // optionally quoted
    while (*s == ' ' || *s == '\t') ++s;
    if (*s == '"') {
        s++;
        const char* e = s;
        while (*e != '"' && *e != '\0') ++e;
        std::string rv(s, e-s);
        s = (*e == '"')? e+1: e;
        return rv;
    } else {
        const char* e = s;
        while (*e != ' ' && *e != '\t' && *e != '\0') ++e;
        std::string rv(s, e-s);
        s = e;
        return rv;
    }
}


bool Config::parse_match(const char* str) { // match: match_id ...
    std::string id = str_pop(str);
    if (id.empty()) return false;
    while (*str == ' ' || *str == '\t') ++str;
    std::string match(str);
    if (match.empty()) return false;

    unsigned num = 0;
    const char* mp = match.c_str();
    if (!IMAP::check_match(mp, num)) {
        LOG("cannot parse at match %u around pos %lu: '%s'", num+1, (uintptr_t)(mp-match.c_str()), match.c_str());
        return false;
    }
    assert(num > 0);
    if (num > 1) {
        match = "(" + match + ")";
    }

    std::map<std::string, std::string>::iterator it = matches.find(id);
    if (it != matches.end()) {
        it->second = "OR " + it->second + " " + match;
    } else {
        matches.insert(std::pair<std::string, std::string>(id, match));
    }
    return true;
}


bool Config::parse_server(const char* str) { // server: server_id host port user pass
    std::string id = str_pop(str);
    if (id.empty() || servers.find(id) != servers.end() || matches.find(id) != matches.end()) {
        return false;
    }

    server_t s;
    s.host = str_pop(str);
    s.port = str_pop(str);
    s.user = str_pop(str);
    s.pass = str_pop(str);
    if (s.host.empty() || s.port.empty() || s.user.empty()) return false;
    if (!IMAP::is_quotable(s.user.c_str()) || !IMAP::is_quotable(s.pass.c_str())) return false;

    servers.insert(std::pair<std::string,server_t>(id, s));
    return true;
}


bool Config::parse_task(const char* str) { // server_id src match_id dst
    std::string id = str_pop(str);
    if (id.empty() || servers.find(id) == servers.end()) {
        return false;
    }

    agenda_t a;
    a.from = str_pop(str);
    a.match_id = str_pop(str);
    a.to = str_pop(str);
    if (a.match_id.empty() || matches.find(a.match_id) == matches.end()) return false;
    if (a.from.empty() || !IMAP::is_quotable(a.from.c_str())) return false;
    if (a.to.empty() || !IMAP::is_quotable(a.to.c_str())) return false;

    servers.find(id)->second.tasks.push_back(a); // TODO: check for from/to duplicates? (could break ordering)
    return true;
}


/*
server server-id1 host port user pass
match match-id1 ...
match match-id1 ...
match match-id2 ...
server-id1 Inbox match-id1 "Junk Mail"
*/
bool Config::parse_line(std::string& s) {
    const char* c = s.c_str();
    while (*c == ' ' || *c == '\t') c++;
    if (!*c || *c == '#') return true;

    if (strncasecmp(c, "server ", 7) == 0) return parse_server(c+7);
    if (strncasecmp(c, "match ", 6) == 0) return parse_match(c+6);
    return parse_task(c);
}


bool Config::parse(const char* fn) {
    std::ifstream fs(fn);
    if (!fs.is_open()) {
        LOG_ERRNO("cannot open '%s' for reading", fn);
        return false;
    }
    unsigned lno = 0;
    std::string line;
    while (getline(fs, line)) {
        lno++;
        if (!parse_line(line)) {
            LOG("cannot parse line %u", lno);
            fs.close();
            servers.clear();
            matches.clear();
            return false;
        }
    }
    if (fs.bad()) {
        LOG_ERRNO("cannot read from '%s'", fn);
        servers.clear();
        matches.clear();
        return false;
    }
    fs.close();

    for (std::map<std::string,server_t>::const_iterator it = servers.begin(); it != servers.end(); ++it) {
        if (!it->second.tasks.empty()) {
            return true;
        }
    }

    LOG("nothing to do");
    servers.clear();
    matches.clear();
    return false;
}