#define private public
#include "emc/rs274ngc/rs274ngc_interp.hh"
#undef private

#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <string>
#include <unistd.h>

namespace {

std::filesystem::path make_temp_dir()
{
    std::string pattern = "/tmp/linuxcnc-params-XXXXXX";
    char *path = mkdtemp(pattern.data());
    if (path == nullptr) {
        std::perror("mkdtemp");
        std::exit(2);
    }
    return path;
}

void write_file(const std::filesystem::path &path, const std::string &content)
{
    std::ofstream out(path);
    if (!out) {
        std::cerr << "failed to write " << path << "\n";
        std::exit(2);
    }
    out << content;
}

std::string read_file(const std::filesystem::path &path)
{
    std::ifstream in(path);
    if (!in) {
        std::cerr << "failed to read " << path << "\n";
        std::exit(2);
    }
    return std::string(std::istreambuf_iterator<char>(in),
                       std::istreambuf_iterator<char>());
}

bool contains(const std::string &haystack, const std::string &needle)
{
    return haystack.find(needle) != std::string::npos;
}

void print_error_text(Interp &interp, const char *prefix, int status)
{
    char error_buf[LINELEN] = {0};
    interp.error_text(status, error_buf, sizeof(error_buf));
    std::cout << prefix << error_buf << "\n";
}

}  // namespace

int main()
{
    const auto temp_dir = make_temp_dir();
    const auto parameter_file = temp_dir / "rs274ngc.var";
    const auto out_of_order_file = temp_dir / "out-of-order.var";
    const auto missing_required_file = temp_dir / "missing-required.var";
    const auto missing_file = temp_dir / "does-not-exist.var";

    write_file(parameter_file,
               "5161 10.5\n"
               "5162 20.25\n"
               "5220 1\n"
               "5221 2.25\n"
               "5399 44\n"
               "<_named_param> 123\n");

    write_file(out_of_order_file,
               "5220 1\n"
               "5161 2\n");

    write_file(missing_required_file,
               "5161 3.5\n"
               "5220 1\n");

    Interp interp;
    int rc = interp.restore_parameters(parameter_file.c_str());
    std::cout << "restore_existing=" << rc << "\n";
    std::cout << "parameter_5161=" << interp._setup.parameters[5161] << "\n";
    std::cout << "parameter_5162=" << interp._setup.parameters[5162] << "\n";
    std::cout << "parameter_5220=" << interp._setup.parameters[5220] << "\n";
    std::cout << "parameter_5221=" << interp._setup.parameters[5221] << "\n";
    std::cout << "parameter_5399=" << interp._setup.parameters[5399] << "\n";

    rc = interp.restore_parameters(missing_file.c_str());
    std::cout << "restore_missing_file=" << rc << "\n";

    rc = interp.restore_parameters(out_of_order_file.c_str());
    std::cout << "restore_out_of_order=" << rc << "\n";
    if (rc > INTERP_MIN_ERROR) {
        print_error_text(interp, "restore_out_of_order_error=", rc);
    }

    Interp missing_required_interp;
    rc = missing_required_interp.restore_parameters(missing_required_file.c_str());
    std::cout << "restore_missing_required=" << rc << "\n";
    std::cout << "missing_required_5161=" << missing_required_interp._setup.parameters[5161] << "\n";
    std::cout << "missing_required_5162=" << missing_required_interp._setup.parameters[5162] << "\n";

    interp._setup.parameters[5161] = 12.34;
    interp._setup.parameters[5162] = 56.78;
    interp._setup.parameters[5220] = 1.0;
    interp._setup.parameters[5221] = 9.87;
    interp._setup.parameters[5399] = 66.6;

    rc = interp.save_parameters(parameter_file.c_str(), interp._setup.parameters);
    std::cout << "save_parameters=" << rc << "\n";

    const std::string saved = read_file(parameter_file);
    const std::string backup = read_file(parameter_file.string() + RS274NGC_PARAMETER_FILE_BACKUP_SUFFIX);
    std::cout << "saved_has_5161=" << contains(saved, "5161\t12.340000") << "\n";
    std::cout << "saved_has_5162=" << contains(saved, "5162\t56.780000") << "\n";
    std::cout << "saved_has_5221=" << contains(saved, "5221\t9.870000") << "\n";
    std::cout << "saved_has_5399=" << contains(saved, "5399\t66.600000") << "\n";
    std::cout << "saved_has_named_param=" << contains(saved, "_named_param") << "\n";
    std::cout << "backup_has_original_5161=" << contains(backup, "5161 10.5") << "\n";

    std::filesystem::remove_all(temp_dir);
    return 0;
}
