/*
 *  asbots
 *  Copyright (C) 2021  niansa
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
#include "config.hpp"
#include "uid.hpp"
#include "utility.hpp"

#include <string>
#include <string_view>
#include <tuple>
#include <fstream>
#include <filesystem>



inline void noop() {}

Config parseConfig(const std::filesystem::path& path) {
    Config fres;
    auto file = std::ifstream(path);
    // Read through all sections
    while (!file.eof()) {
        std::string head;
        std::string_view section;
        file >> head;
        // Skip some emptynes
        if (head.empty()) {
            continue;
        }
        // Check section head syntax
        if (head.size() < 3 || !head.starts_with('[') || !head.ends_with(']')) {
            throw Config::ParseError("Invalid head (section identifier) syntax");
        }
        // Extract actual section name
        section = {head.data()+1, head.size()-2};
        while (!file.eof()) {
            // Get key and value
            std::string line;
            std::getline(file, line);
            // Check for empty line
            if (line.empty()) {
                continue;
            }
            // Split into key and value
            auto [key, value] = Utility::splitOnce(line, ": ");
            // Check for "end"
            if (key == "end") {
                break;
            }
            // Do stuff depending on current section using macro magic
    #       define SECTION(name, ...) if (section == #name) {auto &d = fres.name; __VA_ARGS__; continue;;} noop()
    #       define ASSIGN(e_key, val) if (key == #e_key) {d.e_key = val; continue;} noop()
            SECTION(connection, {
                        ASSIGN(addr, value);
                        ASSIGN(port, std::stoi(std::string(value)));
                    });
            SECTION(auth, {
                        ASSIGN(send_password, value);
                        ASSIGN(accept_password, value);
                    });
            SECTION(server, {
                        ASSIGN(name, value);
                        ASSIGN(description, value);
                        ASSIGN(uid, SUID(value));
                        ASSIGN(hidden, value=="true");
                    });
            // Misc keys
            auto s_res = fres.misc.find(std::string(section));
            if (s_res == fres.misc.end()) {
                fres.misc[std::string(section)] = {{std::string(key), std::string(value)}};
            } else {
                s_res->second[std::string(key)] = value;
            }
        }
    }
    // Return resulting Config object
    return fres;
}