#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" };

const std::string black("\033[0;30m");
const std::string boldblack("\033[1;30m");
const std::string green("\033[0;32m");
const std::string boldgreen("\033[1;32m");
const std::string cyan("\033[0;36m");
const std::string blue("\033[0;34m");
const std::string boldblue("\033[1;34m");
const std::string magenta("\033[0;35m");
const std::string red("\033[31m");
const std::string yellow("\033[0;33m");
const std::string 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);
