#include "DirectWriteHelpers.h"

#include "FileLogger.h"

#include <wrl.h>

GlyphOutlineGeometrySink::GlyphOutlineGeometrySink()
    : mGlyphRunOutline(std::make_unique<GlyphRunOutlines>())
{

}

IUnknown* GlyphOutlineGeometrySink::Create()
{
    return static_cast<IGlyphOutlineGeometrySink*>(new GlyphOutlineGeometrySink);
}

HRESULT __stdcall GlyphOutlineGeometrySink::QueryInterface(const IID& iid, void** ppv)
{
    if (IsEqualIID(iid, __uuidof(IUnknown)))
    {
        *ppv = static_cast<IGlyphOutlineGeometrySink*>(this);
    }
    else if (IsEqualIID(iid, IID_ID2D1SimplifiedGeometrySink))
    {
        *ppv = static_cast<IGlyphOutlineGeometrySink*>(this);
    }
    else if (IsEqualIID(iid, IID_IGlyphOutlineGeometrySink))
    {
        *ppv = static_cast<IGlyphOutlineGeometrySink*>(this);
    }
    else
    {
        *ppv = nullptr;
        return E_NOINTERFACE;
    }
    reinterpret_cast<IUnknown*>(*ppv)->AddRef();
    return S_OK;
}

ULONG __stdcall GlyphOutlineGeometrySink::AddRef()
{
    return ::InterlockedIncrement(&mRefCount);
}

ULONG __stdcall GlyphOutlineGeometrySink::Release()
{
    return 0;
    if (::InterlockedDecrement(&mRefCount) == 0)
    {
        delete this;
        return 0;
    }
    return mRefCount;
}

void __stdcall GlyphOutlineGeometrySink::SetFillMode(D2D1_FILL_MODE fillMode)
{
    //MLOG_INFO("SetFillMode: " << bool(fillMode == D2D1_FILL_MODE_ALTERNATE));
}

void __stdcall GlyphOutlineGeometrySink::SetSegmentFlags(D2D1_PATH_SEGMENT vertexFlags)
{
    if (vertexFlags == D2D1_PATH_SEGMENT_NONE)
    {
    }
    else if (vertexFlags == D2D1_PATH_SEGMENT_FORCE_UNSTROKED)
    {
    }
    else if (vertexFlags == D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN)
    {
    }
    else if (vertexFlags == D2D1_PATH_SEGMENT_FORCE_DWORD)
    {
    }
    else
    {
    }
}

void __stdcall GlyphOutlineGeometrySink::BeginFigure(D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin)
{
    mGlyphRunOutline->startFeature(Point(startPoint.x, startPoint.y), figureBegin == D2D1_FIGURE_BEGIN_FILLED);
}

void __stdcall GlyphOutlineGeometrySink::AddLines(_In_reads_(pointsCount) CONST D2D1_POINT_2F* points, UINT32 pointsCount)
{
    GlyphRunSegment segment;
    segment.mType = GlyphRunSegment::Type::LINE;
    for (UINT32 idx = 0; idx < pointsCount; idx++)
    {
        auto point = points[idx];
        segment.mPoints.push_back(Point(point.x, point.y));
    }
    mGlyphRunOutline->addSegment(segment);
}

void __stdcall GlyphOutlineGeometrySink::AddBeziers(_In_reads_(beziersCount) CONST D2D1_BEZIER_SEGMENT* beziers, UINT32 beziersCount)
{
    for (UINT32 idx = 0; idx < beziersCount; idx++)
    {
        GlyphRunSegment segment;
        segment.mType = GlyphRunSegment::Type::BEZIER;

        auto bezier = beziers[idx];
        segment.mPoints.push_back(Point(bezier.point1.x, bezier.point1.y));
        segment.mPoints.push_back(Point(bezier.point2.x, bezier.point2.y));
        segment.mPoints.push_back(Point(bezier.point3.x, bezier.point3.y));

        mGlyphRunOutline->addSegment(segment);
    }
}

void __stdcall GlyphOutlineGeometrySink::EndFigure(D2D1_FIGURE_END figureEnd)
{

}

HRESULT __stdcall GlyphOutlineGeometrySink::Close()
{
    return S_OK;
}

std::unique_ptr<GlyphRunOutlines> GlyphOutlineGeometrySink::getGlypyRunOutline()
{
    return std::move(mGlyphRunOutline);
}

IUnknown* OffScreenTextRenderer::Create()
{
    return static_cast<IOffScreenTextRenderer*>(new OffScreenTextRenderer);
}

HRESULT __stdcall OffScreenTextRenderer::QueryInterface(const IID& iid, void** ppv)
{
    if (IsEqualIID(iid, __uuidof(IUnknown)))
    {
        *ppv = static_cast<IOffScreenTextRenderer*>(this);
    }
    else if (IsEqualIID(iid, __uuidof(IDWritePixelSnapping)))
    {
        *ppv = static_cast<IOffScreenTextRenderer*>(this);
    }
    else if (IsEqualIID(iid, __uuidof(IDWriteTextRenderer)))
    {
        *ppv = static_cast<IOffScreenTextRenderer*>(this);
    }
    else if (IsEqualIID(iid, IID_IOffScreenTextRenderer))
    {
        *ppv = static_cast<IOffScreenTextRenderer*>(this);
    }
    else
    {
        *ppv = nullptr;
        return E_NOINTERFACE;
    }
    reinterpret_cast<IUnknown*>(*ppv)->AddRef();
    return S_OK;
}

ULONG __stdcall OffScreenTextRenderer::AddRef()
{
    return ::InterlockedIncrement(&mRefCount);
}

ULONG __stdcall OffScreenTextRenderer::Release()
{
    return 0;
    if (::InterlockedDecrement(&mRefCount) == 0)
    {
        delete this;
        return 0;
    }
    return mRefCount;
}

HRESULT __stdcall OffScreenTextRenderer::DrawGlyphRun(_In_opt_ void* clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, DWRITE_MEASURING_MODE measuringMode,
    _In_ DWRITE_GLYPH_RUN const* glyphRun, _In_ DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription, _In_opt_ IUnknown* clientDrawingEffect)
{
    auto pInterface = GlyphOutlineGeometrySink::Create();
    Microsoft::WRL::ComPtr<GlyphOutlineGeometrySink> sink;
    pInterface->QueryInterface(IID_IGlyphOutlineGeometrySink, &sink);

    auto hr = glyphRun->fontFace->GetGlyphRunOutline(
        glyphRun->fontEmSize,
        glyphRun->glyphIndices,
        glyphRun->glyphAdvances,
        glyphRun->glyphOffsets,
        glyphRun->glyphCount,
        glyphRun->isSideways,
        glyphRun->bidiLevel % 2,
        sink.Get()
    );
    sink->Close();

    mGlyphRunOutline = sink->getGlypyRunOutline();

    return S_OK;
}

HRESULT __stdcall OffScreenTextRenderer::DrawUnderline(_In_opt_ void* clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, _In_ DWRITE_UNDERLINE const* underline,
    _In_opt_ IUnknown* clientDrawingEffect)
{
    return S_OK;
}

HRESULT __stdcall OffScreenTextRenderer::DrawStrikethrough(_In_opt_ void* clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, _In_ DWRITE_STRIKETHROUGH const* strikethrough,
    _In_opt_ IUnknown* clientDrawingEffect)
{
    return S_OK;
}

HRESULT __stdcall OffScreenTextRenderer::DrawInlineObject(_In_opt_ void* clientDrawingContext, FLOAT originX, FLOAT originY, _In_ IDWriteInlineObject* inlineObject,
    BOOL isSideways, BOOL isRightToLeft, _In_opt_ IUnknown* clientDrawingEffect)
{
    return S_OK;
}

HRESULT __stdcall OffScreenTextRenderer::IsPixelSnappingDisabled(_In_opt_ void* clientDrawingContext, _Out_ BOOL* isDisabled)
{
    *isDisabled = FALSE;
    return S_OK;
}

HRESULT __stdcall OffScreenTextRenderer::GetCurrentTransform(_In_opt_ void* clientDrawingContext, _Out_ DWRITE_MATRIX* transform)
{
    *transform = DWRITE_MATRIX(1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
    return S_OK;
}

HRESULT __stdcall OffScreenTextRenderer::GetPixelsPerDip(_In_opt_ void* clientDrawingContext, _Out_ FLOAT* pixelsPerDip)
{
    *pixelsPerDip = 1.0;
    return S_OK;
}

std::unique_ptr<GlyphRunOutlines> OffScreenTextRenderer::getGlypyRunOutline()
{
    return std::move(mGlyphRunOutline);
}