#include "file.h"
#include "kutil.h"
#include "util.h"
#include "log.h"
#include "show.h"
#include "font_store.h"

namespace latex {

    std::string tex_style()
    {
        (void)K::log(3);
        std::stringstream ss {};
        for (auto filename : sks_files_of_type("sty")) {
            //for (auto filename : find_files_with_extension(klammertext_dir() + "/sks", "sty")) {
            // msg() << "File: " << filename << "\n";
            ss << "\\input{" << filename << "}\n";
            /*
            //ss << "% " << filename << "\n\n" << read_file(filename) << "\n";
            std::string pname = regex_split(filename, std::regex("/sks/"))[1];
            ss << "% sks/" << pname << "\n\n" << string_from_file(filename) << "\n";
            //ss << "\\usepackage{" << pname << "}\n";
            */
        }
        std::string result = ss.str();
        /*
          result = std::regex_replace(result, std::regex(R"(\n\n+)"), "\n");
          result = std::regex_replace(result, std::regex(R"(#)"), "^#");
          //result = std::regex_replace(result, std::regex(R"(%+ *[^/].*?\n)"), "");
          */
        return result;
    }


    std::string title_line(
        const std::string& text, const std::string& font="1.3", const std::string& vskip="4pt",
        bool vskip_if_missing=false)
    {
        std::stringstream ss {};
        if (text.empty()) {
            if (vskip_if_missing) {
                ss << "\\vspace*{" << vskip << "}\n";
            }
        } else {
            ss << "{\\sfont{" << font << "}" << text << "}\\\\[" << vskip << "]\n";
        }
        return ss.str();
    }

    std::string default_cover(
        const std::string& title, const std::string& subtitle,
        const std::string& author, const std::string& date, const std::string& version)
    {
        std::string test = trim(title + subtitle + author + date + version);
        std::stringstream ss {};
        if (!test.empty()) {
            ss << "\\thispagestyle{empty}\\vspace*{1in}\n";
            ss << title_line(title, "2.0", "10pt")
               << title_line(subtitle, "1.7", "60pt", true)
               << title_line(author)
               << title_line(date)
               << title_line(version);
        }
        return ss.str();
    }

    std::string nonbook_title(
        const std::string& title, const std::string& subtitle,
        const std::string& author, const std::string& date, const std::string& version)
    {
        std::string test = trim(title + subtitle + author + date + version);
        std::stringstream ss {};
        if (!test.empty()) {
            ss << "\\thispagestyle{empty}\n";
            ss << title_line(title, "1.8", "6pt")
               << title_line(subtitle, "1.5", "16pt", true)
               << title_line(author)
               << title_line(date)
               << title_line(version)
               << "\n";
        }
        return ss.str();
    }

    std::string pagenumber(int n, const std::string& style="arabic")
    {
        (void)K::log(3);
        std::stringstream ss {};
        ss << "\\pagenumbering{" << style << "}\\setcounter{page}{" << n << "}\n";
        return ss.str();
    }

    std::string book_verso_page(const std::string& copyright)
    {
        (void)K::log(3);
        std::stringstream ss {};
        ss << "\n% Verso page (page ii)\n";
        ss << "\\newpage\\thispagestyle{empty}\n";
        if (!copyright.empty()) {
            ss << "\\leavevmode\\vfill\n"
               << copyright << "\n"
               << "\\vspace*{8pt}\n";
        } else {
            ss << "\\leavevmode\n";  // blank verso page
        }
        ss << "\\newpage\n";
        return ss.str();
    }
    
    std::string font_command(const std::string& command, const Resolved_font& font, float scale)
    {
        std::stringstream ss {};
        if (font.family_name.empty())
            return "";

        // Format scale option if needed
        std::string scale_opt {};
        if (scale > 0.0f && scale != 1.0f) {
            char buf[16];
            std::snprintf(buf, sizeof(buf), "%.4f", scale);
            scale_opt = std::string(",Scale=") + buf;
        }

        if (!font.regular.empty()) {
            // Use Path= to point at the .ttf files directly
            // fontspec syntax: \setmainfont[options]{filename}
            // Wrap in \ExplSyntaxOn to handle underscores in paths
            ss << "\\ExplSyntaxOn\n"
               << "\\" << command << "[Path={" << font.font_dir << "/}"
               << scale_opt;
            if (!font.bold.empty())
                ss << ",BoldFont=" << font.bold;
            if (!font.italic.empty())
                ss << ",ItalicFont=" << font.italic;
            if (!font.bold_italic.empty())
                ss << ",BoldItalicFont=" << font.bold_italic;
            ss << "]{" << font.regular << "}\n"
               << "\\ExplSyntaxOff\n";
        } else {
            // No .ttf files — use font name (fontspec finds by name)
            ss << "\\" << command;
            if (!scale_opt.empty())
                ss << "[" << scale_opt.substr(1) << "]";  // strip leading comma
            ss << "{" << font.family_name << "}\n";
        }
        return ss.str();
    }

    std::string page(const std::string& structure,
                     const std::string& title,
                     const std::string& subtitle,
                     const std::string& authors,
                     const std::string& date,
                     const std::string& version,
                     const std::string& copyright,
                     const std::string& bottom,
                     const std::string& prolog,
                     const std::string& paper_size,
                     bool two_column,
                     float leading,
                     int pointsize,
                     bool ragged_right,
                     const std::string& cover,
                     bool landscape,
                     const std::string& body,
                     Resolved_font serif_font,
                     Resolved_font sans_font,
                     Resolved_font mono_font,
                     float font_scale)
    {
        (void)K::log(3);
        bool book_format = (structure == "book");
        bool toc = (structure != "plain");
        std::string document_class = book_format ? "book" : "article";
        std::string page_side = book_format ? "twoside" : "oneside";
        std::stringstream ss {};
        ss << "\\documentclass[" << paper_size << ","
           << pointsize << "pt";
        if (landscape) {
            ss << ",landscape";
        }
        if (two_column) {
            ss << ",twocolumn";
        }
        ss << "]{" << document_class << "}\n";
        ss << "\\newcommand{\\documentclassname}{" << document_class << "}\n";
        ss << "\\newcommand{\\documentside}{" << page_side << "}\n";
        ss << "\\newcommand{\\documenttwocolumn}{" << (two_column ? "true" : "false") << "}\n";
        ss << "\\newcommand{\\documentlandscape}{" << (landscape ? "true" : "false") << "}\n";
        ss << "\\newcommand{\\documentisbook}{" << (book_format ? "true" : "false") << "}\n";
        // ":bottom none" suppresses the footer entirely: no \documentbottom,
        // and \pagestyle{empty} replaces the sks page styles below.
        bool no_footer = (bottom == "none");
        if (!bottom.empty() && !no_footer) {
            ss << "\\newcommand{\\documentbottom}{" << bottom << "}\n";
        }

        ss << tex_style();

        // Compute font scale factors: serif is the reference.
        // Three methods (uncomment the desired one):
        //   x-height:   serif_xh / other_xh   (matches lowercase)
        //   cap-height:  serif_ch / other_ch   (matches capitals)
        //   average:    mean(serif_xh,serif_ch) / mean(other_xh,other_ch)
        float serif_xh = serif_font.xheight_ratio;
        float serif_ch = serif_font.capheight_ratio;
        float serif_avg = (serif_xh + serif_ch) / 2.0f;
        auto font_scale_fn = [&](const Resolved_font& other) -> float {
            float other_avg = (other.xheight_ratio + other.capheight_ratio) / 2.0f;
            if (serif_avg > 0.0f && other_avg > 0.0f)
                // return serif_xh / other.xheight_ratio;      // x-height
                // return serif_ch / other.capheight_ratio;     // cap-height
                return serif_avg / other_avg;                   // average
            return 0.0f;
        };
        // Global font_scale multiplies all font sizes uniformly
        float gs = (font_scale != 1.0f) ? font_scale : 0.0f;
        float sans_s = font_scale_fn(sans_font);
        float mono_s = font_scale_fn(mono_font);
        // Apply global scale: multiply into per-font scale, or use alone
        if (gs > 0.0f) {
            sans_s = (sans_s > 0.0f) ? sans_s * font_scale : font_scale;
            mono_s = (mono_s > 0.0f) ? mono_s * font_scale : font_scale;
        }
        ss << font_command("setmainfont", serif_font, gs);
        ss << font_command("setsansfont", sans_font, sans_s);
        ss << font_command("setmonofont", mono_font, mono_s);

        // Scale leading proportionally with font_scale
        float effective_leading = leading * ((font_scale != 1.0f) ? font_scale : 1.0f);
        ss << "\n\\setstretch{" << effective_leading << "}\n";
        if (ragged_right)
            ss << "\\raggedright\n";

        if (!prolog.empty()) {
            ss << prolog << "\n";
        }

        ss << "\\begin{document}\n";

        if (book_format) {
            // Front matter in roman numerals
            ss << pagenumber(1, "roman");

            // Cover page (page i, no displayed number)
            if (!cover.empty()) {
                ss << cover << "\n";
            } else {
                ss << default_cover(title, subtitle, authors, date, version);
            }

            // Verso page (page ii): copyright at bottom, or blank
            // Ensures TOC starts on recto (page iii)
            ss << book_verso_page(copyright);

            // Table of contents (starts on page iii, recto)
            ss << "\\pagestyle{" << (no_footer ? "empty" : "noheader") << "}\n"
               << "\\tableofcontents\n";

            // Advance to the next recto page for the body.
            // After the TOC (on an odd page like iii), we need a blank verso
            // so the first chapter starts on recto.  \cleardoublepage and
            // \mainmatter fail here because \pagenumbering{arabic} resets the
            // counter to 1 (odd), and \chapter's internal \cleardoublepage
            // then sees an odd page and skips the blank.
            //
            // Solution: explicitly emit a blank verso page after the TOC,
            // then switch to arabic numbering starting at page 1.
            ss << "\\clearpage\n"
               << "\\thispagestyle{empty}\\mbox{}\\clearpage\n"
               << "\\pagenumbering{arabic}\n"
               << "\\pagestyle{" << (no_footer ? "empty" : "sksbook") << "}\n";
        } else {
            if (no_footer) {
                ss << "\\pagestyle{empty}\n\n";
            } else {
                ss << "\\pagestyle{sks" << structure << "}\n\n";
            }
            // Plain or article: title block with optional copyright footnote
            if (!copyright.empty()) {
                ss << "\\renewcommand{\\thefootnote}{}\n";
                ss << "\\footnotetext{" << copyright << "}\n";
                ss << "\\renewcommand{\\thefootnote}{\\arabic{footnote}}\n";
            }
            ss << nonbook_title(title, subtitle, authors, date, version);
            if (toc) {
                ss << "\\tableofcontents\n";
                ss << "\\vspace*{\\baselineskip}\n";
            }
            /*
            if (!landscape) {
                ss << "\\vspace*{\\baselineskip}\n";
            }
            */
        }

        ss << body << "\n";
        ss << "\\end{document}\n";
        return ss.str();
    }
}

void check_for_xelatex()
{
    std::string command = "which xelatex";
    std::string xelatex_path = trim(exec(command.c_str()));
    if (trim(xelatex_path).empty()) {
        throw Environment_error(
            "The \"xelatex\" command required to make PDF files is not installed",
            Locator());
    }
    (void)K::log(2, "xelatex path is " + xelatex_path);
}

std::vector<std::string> find_latex_error_lines(const std::string& log_content)
{
    std::vector<std::string> errors;                                                                                          
    std::istringstream stream(log_content);                                                                                   
    std::string line;                                                                                                         
    int i = 1;
    while (std::getline(stream, line)) {
        if (!line.empty() && line[0] == '!') {
            errors.push_back(std::to_string(i) + ": " + line);
        }
        i++;
    }
    return errors;
}
