#include "DirectWriteFontEngine.h"

#include "FontGlyph.h"
#include "UnicodeUtils.h"

#include "DirectWriteHelpers.h"
#include "FileLogger.h"

#include <dwrite.h>
#include <cmath>

void DirectWriteFontEngine::initialize()
{
	mIsValid = SUCCEEDED(::DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), &mDWriteFactory));

    loadFontFaceFromName("Verdana", 16);
}

void DirectWriteFontEngine::initializeOffScreenRenderer()
{
    if (mOffScreenRenderer)
    {
        return;
    }
    auto pInterface = OffScreenTextRenderer::Create();
    pInterface->QueryInterface(IID_IOffScreenTextRenderer, &mOffScreenRenderer);
}

void DirectWriteFontEngine::loadFontFaceFromName(const std::string& fontName, float penSize)
{
    mDWriteFactory->CreateTextFormat(UnicodeUtils::utf8ToUtf16WString(fontName).c_str(), nullptr, DWRITE_FONT_WEIGHT_NORMAL, 
        DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, penSize, L"en-us", &mTextFormat);
}

std::unique_ptr<FontGlyph> DirectWriteFontEngine::loadGlyph(uint32_t charCode)
{
	return nullptr;
}

IDWriteFontFamily* DirectWriteFontEngine::getSystemFontFamily(const std::string& fontName)
{
    if (auto iter = mSystemFontFamilies.find(fontName); iter != mSystemFontFamilies.end())
    {
        return iter->second.Get();
    }
    else
    {
        loadSystemFontFamily(fontName);
        if (auto iter = mSystemFontFamilies.find(fontName); iter != mSystemFontFamilies.end())
        {
            return iter->second.Get();
        }
        else
        {
            return nullptr;
        }
    }
}

bool DirectWriteFontEngine::loadSystemFontFamily(const std::string& fontName)
{
    Microsoft::WRL::ComPtr<IDWriteFontCollection> system_collection;
    if (SUCCEEDED(mDWriteFactory->GetSystemFontCollection(&system_collection, FALSE)))
    {
        UINT32 index{ 0 };
        BOOL exists{ false };
        system_collection->FindFamilyName(UnicodeUtils::utf8ToUtf16WString(fontName).c_str(), &index, &exists);
        if (exists)
        {
            Microsoft::WRL::ComPtr<IDWriteFontFamily> font_family;
            if (SUCCEEDED(system_collection->GetFontFamily(index, &font_family)))
            {
                mSystemFontFamilies[fontName] = font_family;
                return true;
            }
        }
    }
    return false;
};

IDWriteFontFace* DirectWriteFontEngine::getDefaultFontFace(const std::string& fontName)
{
    if (auto iter = mDefaultFontFaces.find(fontName); iter != mDefaultFontFaces.end())
    {
        return iter->second.Get();
    }
    else
    {
        loadDefaultFontFace(fontName);
        if (auto iter = mDefaultFontFaces.find(fontName); iter != mDefaultFontFaces.end())
        {
            return iter->second.Get();
        }
        else
        {
            return nullptr;
        }
    }
}

bool DirectWriteFontEngine::loadDefaultFontFace(const std::string& fontName)
{
    if (auto family = getSystemFontFamily(fontName); family)
    {
        Microsoft::WRL::ComPtr<IDWriteFont> font;
        if (SUCCEEDED(family->GetFirstMatchingFont(DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STRETCH_NORMAL, DWRITE_FONT_STYLE_NORMAL, &font)))
        {
            Microsoft::WRL::ComPtr<IDWriteFontFace> font_face;
            font->CreateFontFace(&font_face);
            mDefaultFontFaces[fontName] = font_face;
            return true;
        }
    };
    return false;
}

std::unique_ptr<GlyphRunOutlines> DirectWriteFontEngine::getGlyphRunOutlines(const std::vector<uint32_t>& charCodes)
{
    initializeOffScreenRenderer();

    std::wstring text;
    for (auto code : charCodes)
    {
        text.push_back(code);
    }

    auto hr = mDWriteFactory->CreateTextLayout(text.c_str(), static_cast<UINT32>(text.size()), mTextFormat.Get(), 50.0, 50.0, &mTextLayout);

    mTextLayout->Draw(nullptr, mOffScreenRenderer.Get(), 0.0, 0.0);

    return mOffScreenRenderer->getGlypyRunOutline();
}

double DirectWriteFontEngine::getHorizontalAdvance(const FontItem& fontItem, const std::string& content)
{
    auto font_face = getDefaultFontFace(fontItem.getFaceName());
    if (!font_face)
    {
        return 0;
    }

    DWRITE_FONT_METRICS font_metrics;
    font_face->GetMetrics(&font_metrics);

    const auto design_per_em = font_metrics.designUnitsPerEm;
    MLOG_INFO("Design units: " << design_per_em);

    const auto utf32_content = UnicodeUtils::utf8ToUtf32(content);

    std::vector<UINT16> glyph_indices(utf32_content.size());
    if (SUCCEEDED(font_face->GetGlyphIndices(utf32_content.data(), static_cast<UINT32>(utf32_content.size()), &glyph_indices[0])))
    {
        std::vector<DWRITE_GLYPH_METRICS> metrics(utf32_content.size());
        double total_advance{ 0 };
        if (SUCCEEDED(font_face->GetDesignGlyphMetrics(glyph_indices.data(), static_cast<UINT32>(glyph_indices.size()), &metrics[0], false)))
        {
            for (std::size_t idx = 0; idx < utf32_content.size(); idx++)
            {
                const auto design_width = metrics[idx].advanceWidth;
                // https://learn.microsoft.com/en-us/windows/win32/gdi/device-vs--design-units
                double device_width = (design_width / static_cast<double>(design_per_em)) * (fontItem.getSize() / 72.0) * 96.0 * 0.8;

                MLOG_INFO("Got advance width: " << device_width);
                //total_advance += std::floor(device_width);
                total_advance += device_width;
            }
            return std::round(total_advance);
        }
    }
    return 0;
}

