#pragma once

#include <iostream>
#include <vector>

#include "locator.h"
#include "katom.h"
#include "argtype.h"
#include "argument.h"
#include "klammer.h"
#include "klammer_registry.h"
#include "klammerset_registry.h"
#include "state.h"
#include "target_registry.h"
#include "machine.h"
#include "file.h"

using namespace std::string_literals;

const std::string middle_dot { "\uFF65" };
const std::string broken_bar { "\u00A6" };
const std::string right_arrow { "\uFFEB" };
const std::string sp_arrow { " \u2192 " };
const std::string right_bracket { "\u27E9" };
const std::string left_bracket { "\u27E8" };
const std::string bbar { "¦" };
const std::string left_double_bracket { "\u27E6" };
const std::string right_double_bracket { "\u27E7" };
const std::string left_square_bracket { "\u2045" };
const std::string right_square_bracket { "\u2046" };
const std::string check { "\u2713" };

// A colour is a STREAM-AWARE object, not a string, because the same name is
// written to two streams whose destinations differ: "ktext -d > doc.txt" has a
// file on stdout and a terminal on stderr at the same time, so one global
// on/off cannot be right for both.  Inserting a Color emits its escape
// sequence only when THAT stream is a terminal, so escape codes can never
// reach a redirected document, a pipe, or a captured log.
//
// Streams other than std::cout/std::cerr -- a stringstream building an error
// message, a file -- are never coloured.  That is the conservative reading and
// it also stops escapes from being baked into strings whose length is then
// measured (abbreviate() in util.cpp counted them).
class Color {
public:
    explicit Color(const std::string& code) : m_code(code) {}
    // The raw sequence, for the rare place that needs a string rather than an
    // insertion.  Prefer inserting the Color: this form cannot be suppressed.
    const std::string& code() const { return m_code; }
private:
    std::string m_code {};
};

// True when colour should be emitted on this stream: it is a terminal and the
// NO_COLOR environment variable is unset.  NO_COLOR (https://no-color.org) is
// honoured for ANY non-empty value, which is what the convention specifies; it
// is the escape hatch for the case isatty cannot see, such as a pager or a CI
// capture that does want, or does not want, colour.
bool color_enabled(const std::ostream& os);
std::ostream& operator<<(std::ostream& os, const Color& color);

const Color black("\033[0;30m");
const Color boldblack("\033[1;30m");
const Color green("\033[0;32m");
const Color boldgreen("\033[1;32m");
const Color cyan("\033[0;36m");
const Color blue("\033[0;34m");
const Color boldblue("\033[1;34m");
const Color magenta("\033[0;35m");
const Color red("\033[31m");
const Color yellow("\033[0;33m");
const Color reset("\033[0m");

const auto seqout = [](auto x) { std::cout << "seq: " << x << "\n"; };
//const auto mapout = [](auto m) { auto const& key std::cout << m.first << sp_arrow << m.second << "\n"; };

const auto mapout = [](auto const& kv){
    auto const& [k, v] = kv;
    std::cout << k << sp_arrow << v << "\n";
};

inline const
std::set sks_commands = {
    "ktext"s,
    "kdesc"s,
    "kdiag"s,
    "argument_test"s,
    "argtype_test"s
};

std::ostream& msg(Locator loc=Locator());
std::ostream& fmsg(Locator loc=Locator());

std::vector<Katom> abbrev(const std::vector<Katom>& ks, unsigned int max_length=16);

// std::vector<int>

std::ostream& operator<<(std::ostream& os, const std::vector<int>& ns);

// std::vector<std::string>

std::ostream& operator<<(std::ostream& os, const std::vector<std::string>& ss);

// std::vector<fs::path>

std::ostream& operator<<(std::ostream& os, const std::vector<fs::path>& pp);

// std::map<std::string,std::string>

std::ostream& operator<<(std::ostream& os, const std::map<std::string,std::string>& sm);

// Katom iterator pair

std::ostream& operator<<(
    std::ostream& os,
    const std::pair<std::vector<Katom>::iterator, std::vector<Katom>::iterator>& iters);

// Locator

std::ostream& operator<<(std::ostream& os, const Locator& loc);

// Katom

std::ostream& operator<<(std::ostream& os, const Katom& k);
std::ostream& operator<<(std::ostream& os, const std::vector<std::vector<Katom>>& ks);
std::ostream& operator<<(std::ostream& os, const std::shared_ptr<Katom>& k);
std::ostream& operator<<(std::ostream& os, const std::vector<Katom>::iterator& k);
std::ostream& operator<<(std::ostream& os, const std::vector<Katom>& ks);
std::ostream& operator<<(std::ostream& os, const std::vector<std::vector<Katom>::iterator>& ks);

std::ostream& kindex(std::ostream& os);
std::ostream& ktype(std::ostream& os);
std::ostream& klevel(std::ostream& os);
// std::ostream& kspans(std::ostream& os);
std::ostream& kws(std::ostream& os);
std::ostream& kall(std::ostream& os);
std::ostream& kreplaced(std::ostream& os);
std::ostream& kignored(std::ostream& os);
std::ostream& kreset(std::ostream& os);

inline static std::string newline_symbol = "/";

std::ostream& operator<<(std::ostream& os, const std::vector<Katom>& ks);
std::ostream& operator<<(std::ostream& os, const std::vector<Katom>& ks);

// Argtype
std::ostream& operator<<(std::ostream& os, const Argtype& a);

// Parameter (aliased as Argument at application sites)
std::ostream& operator<<(std::ostream& os, const Parameter& p);

// Parameter_set (aliased as Argument_set at application sites)
std::ostream& operator<<(std::ostream& os, const Parameter_set& as);

// std::vector<Parameter>
std::ostream& operator<<(std::ostream& os, const std::vector<Parameter>& as);

// Var
std::ostream& operator<<(std::ostream& os, const Var& v);

// Frame
std::ostream& operator<<(std::ostream& os, const Frame& f);

// State
std::ostream& operator<<(std::ostream& os, const State& s);

// Klammer
std::ostream& operator<<(std::ostream& os, const Klammer& k);

// Klammer::variable_map_t
std::ostream& operator<<(std::ostream& os, const Klammer::variable_map_t& vm);

// Klammer::components
std::ostream& operator<<(std::ostream& os, const Klammer::components& kc);

// Klammer_registry
std::ostream& operator<<(std::ostream& os, const Klammer_registry& ks);

// Target
std::ostream& operator<<(std::ostream& os, const Target& t);

// Target_registry
std::ostream& operator<<(std::ostream& os, const Target_registry& ts);

// Klammerset
std::ostream& operator<<(std::ostream& os, const Klammerset& ks);

// Klammerset_registry
std::ostream& operator<<(std::ostream& os, const Klammerset_registry& kr);

// Machine
std::ostream& operator<<(std::ostream& os, const Machine& m);

void modify_stream(const std::string& name);
