#pragma once

#include <limits>
#include <memory>

#include "ktype.h"
#include "locator.h"
#include "util.h"

inline bool show_rewrite_rules = false;

class Katom
{
public:
    static size_t index;
    Katom(const std::string& src, katom_t type, Locator loc);

    // Copy constructor
    Katom(const Katom& other)
        : m_index(other.m_index)
        , m_text(other.m_text)
        , m_src(other.m_src)
        , m_loc(other.m_loc)
        , m_type(other.m_type)
        , m_initial_type(other.m_initial_type)
        , m_display(other.m_display)
        , m_unparsed(other.m_unparsed)
        , m_deferred(other.m_deferred)
        {}

    // Copy assignment operator
    Katom& operator=(const Katom& other) {
        if (this != &other) {
            m_index = other.m_index;
            m_text = other.m_text;
            m_src = other.m_src;
            m_loc = other.m_loc;
            m_type = other.m_type;
            m_initial_type = other.m_initial_type;
            m_display = other.m_display;
            m_unparsed = other.m_unparsed;
            m_deferred = other.m_deferred;
        }
        return *this;
    }

    static inline bool show_index;
    static inline bool show_type;
    static inline bool show_id;
    static inline bool show_whitespace;
    static inline bool show_all;
    static inline bool show_replaced;
    static inline bool show_ignored;

    bool is_whitespace() const {
        return m_initial_type == katom_t::space
            || m_initial_type == katom_t::newline
            || m_type == katom_t::ws_added; }
    bool is_word() const { return m_initial_type == katom_t::word; }
    bool is_text() const { return m_initial_type == katom_t::text; }
    //bool is_text() { return m_initial_type == katom_t::text; }
    bool is_literal() const { return m_type == katom_t::literal; }
    bool is_active() const {
        return m_type != katom_t::ignored
            && m_type != katom_t::replaced; };
    bool is_nonascii() const {
        return m_type == katom_t::nonascii; };
    size_t m_index;
    std::string m_text{};
    std::string m_src{};
    Locator m_loc;
    katom_t m_type;
    katom_t m_initial_type;
    std::string m_display {};
    // Set when the word matched no katom type and fell back to katom_t::word.
    // The warning is deferred to warn_unparsed_katoms(), after removal and
    // literal marking, so removed text (comments, #[...]# blocks) never warns.
    bool m_unparsed {};
    // Inside an unresolved @cond span, and therefore INERT: the passes with
    // observable effects -- @eval and @read -- skip it, so a branch that is
    // never selected never runs anything.  @cond is a non-strict special form
    // (doc/cond_evaluation_order.md), and this flag is the mechanism; the flag
    // is not the semantics.  Cleared on the selected branch when the @cond is
    // resolved in the apply fold, which then processes that branch normally.
    bool m_deferred {};
};

bool active(const std::vector<Katom>& katoms);
int active_count(const std::vector<Katom>& katoms);
std::vector<Katom> split_into_katoms(std::string s, const std::string& source, int source_line);
void restore_initial_type(std::vector<Katom>::iterator begin, std::vector<Katom>::iterator end);
void modify_type(katom_t new_type, std::vector<Katom>::iterator begin, std::vector<Katom>::iterator end);
void modify_type(katom_t old_type, katom_t new_type,
                 std::vector<Katom>::iterator begin, std::vector<Katom>::iterator end);
void ignore_whitespace(std::vector<Katom>::iterator& begin, std::vector<Katom>& katoms);
std::vector<Katom>::iterator after_whitespace(std::vector<Katom>::iterator begin);
std::vector<Katom> text_katoms(
    std::vector<Katom>::iterator& begin, std::vector<Katom>::iterator& end);

std::string as_string(std::vector<Katom>::const_iterator begin, std::vector<Katom>::const_iterator end, bool strip_whitespace);

std::string as_string(const std::vector<Katom>& katoms, bool strip_whitespace);


std::vector<Katom> trim(std::vector<Katom>& katoms, std::set<katom_t> trim_types = {katom_t::space, katom_t::newline});
std::vector<Katom> trim(const std::vector<Katom>& katoms, bool trim_inactive = false);


std::vector<std::vector<Katom>> bar_split(std::vector<Katom>::iterator kbegin, std::vector<Katom>::iterator kend);

std::vector<std::string> line_split(const std::string& s);
std::pair<std::string, std::vector<std::string>> line_split(const fs::path& pathname);
std::vector<Katom> katomize(const std::vector<std::string>& lines, const std::string& source_desc);

void warn_unparsed_katoms(std::vector<Katom>& katoms, bool warn = true);
void process_whitespace_modifiers(std::vector<Katom>& katoms);
std::vector<Katom> trim_whitespace(std::vector<Katom> katoms);

// escape_backslash() and unescape_backslash() (the ___BS___ hack) were
// removed.  Backslash is now handled by the general target escape mechanism
// via the :escape parameter on @@@target.

inline bool is_bar(const Katom& k) {
    return k.m_type == katom_t::bar;
}

inline bool is_nonascii(const Katom& k) {
    return k.m_type == katom_t::nonascii;
}

inline bool is_newline(const Katom& k) {
    return k.m_type == katom_t::newline;
}

inline bool is_ignore_rest(const Katom& k) {
    return k.m_type == katom_t::ignore_rest;
}

inline bool is_ignore_line(const Katom& k) {
    return k.m_type == katom_t::ignore_line;
}

inline void mark_as_replaced(Katom& k) {
    k.m_type = katom_t::replaced;
}

inline void mark_as_literal(Katom& k) {
    k.m_type = katom_t::literal;
}

inline void mark_as_ignored(Katom& k) {
    k.m_type = katom_t::ignored;
}

inline bool begin_read(Katom& k) {
    return k.m_type == katom_t::read_begin;
}

inline bool begin_ignore(const Katom& k) {
    return k.m_type == katom_t::ignore_begin;
}

inline bool end_ignore(const Katom& k) {
    return k.m_type == katom_t::ignore_end;
}

inline bool begin_literal(const Katom& k) {
    return k.m_type == katom_t::literal_begin;
}

inline bool end_literal(const Katom& k) {
    return k.m_type == katom_t::literal_end;
}

inline bool begin_eval(const Katom& k) {
    return k.m_type == katom_t::eval_begin;
}

inline bool begin_cond(const Katom& k) {
    return k.m_type == katom_t::cond_begin;
}

inline bool begin_klammer_def(const Katom& k) {
    return k.m_type == katom_t::define_begin;
}

inline bool end_klammer_def(const Katom& k) {
    return k.m_type == katom_t::define_end;
}

inline bool begin_klammer_apply(const Katom& k) {
    return k.m_type == katom_t::apply_begin;
}

inline bool end_klammer_apply(const Katom& k) {
    return k.m_type == katom_t::apply_end;
}

inline bool begin_machine_def(const Katom& k) {
    return k.m_type == katom_t::machine_begin;
}

inline bool end_machine_def(const Katom& k) {
    return k.m_type == katom_t::machine_end;
}


inline bool begin_apply(const Katom& k)
{
    const std::set<katom_t> opens {
        katom_t::read_begin,
        katom_t::eval_begin,
        katom_t::cond_begin,
        katom_t::apply_begin};
    return opens.contains(k.m_type);
}

inline bool end_apply(const Katom& k)
{
    return k.m_type == katom_t::apply_end;
}
