#include <iostream>
#include "error.h"
#include "command.h"
#include "log.h"
#include "argv.h"
#include "file.h"
#include "util.h"
#include "machine.h"
#include "show.h"
#include "target_registry.h"
#include "klammerset_registry.h"
                
int main(int argc, char* argv[])
{
    try {
        set_verbose_level(argc, argv);
        Argv args {};
        args.req("filenames", "Input files in Klammertext format. ", "'list'");
        args.opt("s", "Text processed before input files.", "input-string", "", "'text'");
        args.opt("t","Output target; default is general (unspecified)", "target",
                 Target_registry::general_name, "'word'");
        args.opt("o", "Output basename; meaning and default defined by target.", "basename",
                 "", "'word'");
        args.flag("d", "Display the output to the screen, rather than writing a file.");
        args.opt("klammersets", "Klammersets to load, as a list of symbols resolved on the "
                 "klammerset search path.  Several combine: they are loaded in the order given "
                 "and share one namespace.  The default is the Standard Klammer Set; "
                 "\"none\" loads none.",
                 "symbols", "", "'list'");
        args.opt("v", "'verbosity'", "degree", "0", "'verbosity'");

        if (show_usage(argc, argv)) {
            // A bare command is a REQUEST for information, not a failure:
            // usage goes to stdout (it is the result being asked for) and the
            // exit status is 0, so "ktext && echo ok" reports what happened.
            args.usage(file_basename(argv[0]));
            exit(0);
        }
        args.parse(argc, argv);
        if (verbose_level > 0) {
            args.describe();
        }
        std::string input_text = args.as_string("s");
        // argv boundaries are authoritative (a shell-quoted "my file.kt" is
        // one element); group_filename_tokens adds the standalone-"/" list
        // form, the existence rescue for unquoted spaces, and ~ expansion.
        std::vector<std::string> input_filenames =
            group_filename_tokens(args.as_vector("filenames"));

        if (input_text.empty() && input_filenames.empty()) {
            throw Argument_error(
                "You must specify input filenames and/or text", Locator::none());
        }

        auto [target, output_dir, output_basename, output_filename,
              write_files, display_only] = 
            parse_args(input_filenames, args.as_string("t"),
                       expand_tilde(args.as_string("o")), args.as_bool("d"));

        Machine M;
        M.m_state.open_frame("ktext");
        M.m_state.set("K_target", target);
        M.m_state.set("K_output_dir", output_dir);
        M.m_state.set("K_output_basename", output_basename);
        M.m_state.set("K_stdout_only", display_only ? "true" : "false");
        // The list form uses the standalone-"/" separator (filenames may
        // contain spaces); K_input_filename is the single root input file.
        M.m_state.set("K_input_filenames", join(input_filenames, " / "));
        M.m_state.set("K_input_filename",
                      input_filenames.empty() ? "" : input_filenames[0]);
        M.m_state.set("K_verbose_level", std::to_string(verbose_level));

        if (!input_filenames.empty()) {
            M.m_state.set(
                "K_input_dir",absolute_pathname(file_directory(input_filenames[0])));
        } else {
            M.m_state.set("K_input_dir", fs::current_path().string());
        }

        load_klammersets(M, word_split(args.as_string("klammersets")));

        if (!input_text.empty()) {
            M.read(input_text + "\n");
        }
        for (auto p : input_filenames) {
            M.read(fs::path(p));
        }

        std::string result = trim(M.apply(target));
        if (display_only && !result.empty()) {
            std::cout << result << "\n";
        } else if (!result.empty()) {
            string_to_file(output_filename, result + "\n");
            (void)K::log(1, "Wrote file: " + output_filename);
        }
    }
    catch (Error& err) {
        err.print_message();
        std::cerr << "\n";
        return 1;
    }
    return 0;
}
