#include "reference.h"
#include "target.h"
#include "util.h"
#include "show.h"

// Escape marker for backslash in pre-processed text
static const std::string BS = Target::escape_marker("\\");

std::ostream& operator<<(std::ostream& os, const numbered_element_spec& spec)
{
    os << "[" << std::get<0>(spec) << "|"
       << std::get<1>(spec) << "|"
       << std::get<2>(spec) << "]";
    return os;
}

std::ostream& operator<<(std::ostream& os, const numbered_elements& line_states)
{
    bool found = false;
    for (long unsigned i = 0; i < line_states.size(); i++) {
        if (std::get<1>(line_states[i]) != "") {
            os << i << ": " << line_states[i] << "\n";
            found = true;
        }
    }
    if (!found) {
        os << "[No matching lines]";
    }
    return os;
}

std::string  add_latex_caption_numbers(const std::string& latex_text)
{
    std::string caption_delimiter { "__CAPTION__" }; // But also in kutil.py
    std::regex caption_delimiter_rgx(caption_delimiter);
    // Accept either the escape-marker form (from the :files path, where
    // M.apply("tex", false) leaves markers in place) or the resolved
    // literal-backslash form (from the :text path, already processed by
    // the outer Machine).
    std::regex section_rgx("(?:" + BS + R"(|\\)sA\{.*)");

    std::map<std::string, int> index_map {};
    bool section_seen = false;
    std::string result {};
    std::smatch match {};
    for (std::string line : regex_split(latex_text, std::regex(R"(\n)"), false)) {
        if (regex_match(line, match, section_rgx)) {
            index_map.clear();
            section_seen = true;
        }
        if (line.find(caption_delimiter) != std::string::npos) {
            std::vector<std::string> parts = regex_split(line, caption_delimiter_rgx);
            std::string before = parts[0];
            std::string caption_type = parts[1];
            std::string caption = parts[2];
            std::string after = parts.size() == 4 ? parts[3] : "";
            if (index_map.count(caption_type) == 0)
                index_map[parts[1]] = 1;
            int n = index_map[caption_type];
            std::stringstream modified {};
            modified << before << " " << caption_type << " ";
            // Suppress the section/chapter prefix before any section has
            // appeared, matching insert_html_caption_numbers behavior
            // ("Figure 1" not "Figure 0.1").
            if (section_seen) {
                modified << "{\\ifthenelse{\\isundefined{\\thechapter}}{\\thesection}{\\thechapter}}.";
            }
            modified << n << " " << caption << after;
            line = modified.str();
            index_map[caption_type]++;
        }
        result += line + "\n";
    }
    return result;
}

numbered_elements latex_line_states(const std::vector<std::string>& lines)
{
    // \hypertarget{Reference-Figure-0}{}\label{Label-Reference-Figure-0}
    std::regex element_container_rgx(BS + R"(hypertarget\{(Reference-(\w+)-\d+)\}.*)");
    std::regex element_caption_rgx(BS + R"(vstrut.*?\}\{.*?\}\{\s*(\w+)\s+(.+\.\d+).*)");

    numbered_elements states(lines.size());
    std::string id {};
    std::string reftype {};
    for (unsigned int i = 0; i < lines.size(); i++) {
        std::smatch container_match {};
        std::smatch caption_match {};
        if (regex_match(lines[i], container_match, element_container_rgx)) {
            id = container_match[1];
            reftype = container_match[2];
        } else if (regex_match(lines[i], caption_match, element_caption_rgx)) {
            states[i] = {id, caption_match[1], caption_match[2]};
        }
    }   
    return states;
}

std::string find_target(const std::string& target,
                        const std::vector<std::string>& lines, int start, const numbered_elements& states,
                        const std::string& target_type, int target_count, const std::string& direction)
{
    // msg() << "find_target: " << target_type << " target_count: " << target_count
    //      << " direction: " << direction << "\n";
    unsigned int offset = direction == "before" ? -1 : 1;
    std::string current_type {};
    int current_count = target_count;
    unsigned int fi = start;
    for (; fi > 0 && fi < lines.size(); fi += offset) {
        // msg() << fi << " : " << states[fi] << "\n";
        current_type = std::get<1>(states[fi]);
        if (current_type == target_type) {
            current_count--;
        }
        if (current_count == 0) {
            break;
        }
    }
    if (current_count > 0) {
        std::stringstream msg {};
        //ss << "No image exists that is \"" << direction << " " << current_count
        //   << "\" from line " << start << " in the HTML file.\n";
        msg << "There is no " << target_type << " that is " << current_count
            << " " << direction << " the reference.\n";
        //throw Argument_error(ss.str());
        return "NOT FOUND: " + target_type + " " + direction;
    }
    std::stringstream ss {};
    auto link_target = std::get<0>(states[fi]);
    auto link_desc = std::get<2>(states[fi]);
    if (target == "html") {
        ss << "<a href=\"^#" << link_target << "\">" << target_type << " " << link_desc << "</a>";
    } else if (target == "tex" || target == "latex") {
        ss << BS << "figureref{" << link_target << "}{" << target_type << " " << link_desc << "}";
    }          
    return ss.str();
}

std::string resolve_caption_references(
    const std::string& target, const std::vector<std::string>& lines, numbered_elements states)
{
    //auto lines = regex_split(text, std::regex(R"(\n)"), false);
    // numbered_elements states = line_states(lines);
    //std::regex reference_rgx(R"((.*?)___([A-Za-z]+) (\w+) (\d+)___([^\n]*))");
    //std::regex reference_rgx(R"((.*?)<a href=\"__(\w+)( \d+)?__\">(\w+)</a>)");

    std::regex reference_rgx(R"((.*?)__REF__(\w+)( \d+)?__(\w+)__)");

    std::string result;
    for (unsigned int i = 0; i < lines.size(); i++) {
        std::string line = lines[i];
        if (line.find("__REF__") == std::string::npos) {
            result += line + "\n";
        } else {
            std::sregex_iterator end {};
            size_t endpos = 0;
            std::string mline {};
            for (std::sregex_iterator p
                     {line.begin(), line.end(), reference_rgx}; p!= end; ++p) {
                std::smatch match = *p;
                std::string prefix = match[1];
                std::string direction = match[2];
                int count = match[3] == "" ? 1 : std::stoi(match[3]);
                std::string target_type = match[4];
                mline += prefix +
                    find_target(target, lines, i, states, target_type, count, direction);
                endpos = match.position() + match.length();
            }
            if (endpos < line.size()) {
                mline += line.substr(endpos);
            }
            // msg() << "   mline: " << mline << "\n";
            result += mline + "\n";
        }
    }
    return result;
}
