#include "WebsiteGenerator.h"

#include "TomlReader.h"
#include "Directory.h"
#include "ContentFile.h"
#include "ContentPage.h"
#include "TemplateFile.h"
#include "TemplatingEngine.h"
#include "TemplateSubstitutionContext.h"
#include "SiteGeneratorConfig.h"
#include "PathUtils.h"

#include "MarkdownConverter.h"
#include "HtmlElement.h"
#include "HtmlWriter.h"

#include "FileLogger.h"

#include <iostream>

WebsiteGenerator::WebsiteGenerator()
    : mConfig(std::make_unique<SiteGeneratorConfig>()),
      mTemplateEngine()
{

}

WebsiteGenerator::~WebsiteGenerator()
{

}

void WebsiteGenerator::findProject(const std::string& searchPath)
{
    const auto config_path = std::filesystem::path(searchPath) / "config.toml";
    if (std::filesystem::exists(config_path))
    {
        mProjectPath = searchPath;
        MLOG_INFO("Found project config in: " << mProjectPath);
    }
}

void WebsiteGenerator::findContentFiles()
{
    const bool is_recursive{true};
    const auto pages_files = Directory::getFilesWithExtension(getPagesPath(), ".md", is_recursive);
    for (const auto& path : pages_files)
    {
        mPages.push_back(std::make_unique<ContentPage>(path));
    }

    const auto article_files = Directory::getFilesWithExtension(getArticlesPath(), ".md", is_recursive);
    for (const auto& path : article_files)
    {
        mArticles.push_back(std::make_unique<ContentArticle>(path));
    }
    MLOG_INFO("Found: " << mArticles.size() << " articles");
}

Path WebsiteGenerator::getContentPath() const
{
    return mProjectPath / "content";
}

Path WebsiteGenerator::getPagesPath() const
{
    return getContentPath() / "pages";
}

Path WebsiteGenerator::getArticlesPath() const
{
    return getContentPath() / "articles";
}

Path WebsiteGenerator::getConfigPath() const
{
    return mProjectPath / "config.toml";
}

void WebsiteGenerator::readConfig()
{
    auto reader = TomlReader();
    reader.read(getConfigPath());

    if (auto theme_table = reader.getContent()->getTable("themes"))
    {
        auto items = theme_table->getKeyValuePairs();
        auto location = items["location"];
        auto active_theme = items["active_theme"];

        mConfig->setThemePath(location);
        mConfig->setActiveTheme(active_theme);
    }
}

void WebsiteGenerator::parseContentFiles()
{
    findContentFiles();
    for (auto& page : mPages)
    {
        page->load();
    }

    for (auto& article : mArticles)
    {
        article->load();
    }
}

void WebsiteGenerator::parseTemplateFiles()
{
    const auto template_path = mProjectPath / mConfig->getThemePath() / mConfig->getActiveTheme();
    mTemplateEngine = std::make_unique<TemplatingEngine>(template_path);
}

void WebsiteGenerator::doSubstitutions()
{
    MarkdownConverter converter;

    for (auto& article : mArticles)
    {
        article->doLinkTagSubstitution(getArticlesPath());

        auto containing_div = std::make_unique<HtmlDivElement>();
        converter.convert(article->getContentBody(), containing_div.get());

        HtmlWriter writer;
        auto md_html_content = writer.toString(containing_div.get());

        TemplateSubstitutionContext sub_context;
        sub_context.addSubstitution("content", md_html_content);

        auto content = mTemplateEngine->renderTemplate("article", &sub_context);
        article->setProcessedOutput(content);
    }
}

Path WebsiteGenerator::getOutputPath() const
{
    return mProjectPath / "output";
}

void WebsiteGenerator::write()
{
    // Setup output dir
    const auto output_dir = mProjectPath / "output";
    std::filesystem::create_directory(output_dir);

    // Write each page
    for (auto& article : mArticles)
    {
        auto article_path = article->getFilename();
        auto relative_path = PathUtils::getRelativePath(article_path, getArticlesPath());
        auto updated_filename = PathUtils::getPathDelimited(relative_path);
        auto final_path = output_dir / Path(updated_filename).replace_extension(".html");
        article->write(final_path);
    }
}

