// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

#include "pch.h"
#include "UiaTextRange.hpp"
#include "TermControlUiaProvider.hpp"

using namespace Microsoft::Terminal;
using namespace Microsoft::Console::Types;

std::deque<UiaTextRange*> UiaTextRange::GetSelectionRanges(_In_ IUiaData* pData,
                                                           _In_ IRawElementProviderSimple* pProvider)
{
    std::deque<UiaTextRange*> ranges;

    // get the selection rects
    const auto rectangles = pData->GetSelectionRects();

    // create a range for each row
    for (const auto& rect : rectangles)
    {
        ScreenInfoRow currentRow = rect.Top();
        Endpoint start = _screenInfoRowToEndpoint(pData, currentRow) + rect.Left();
        Endpoint end = _screenInfoRowToEndpoint(pData, currentRow) + rect.RightInclusive();
        UiaTextRange* range = UiaTextRange::Create(pData,
                                                   pProvider,
                                                   start,
                                                   end,
                                                   false);
        if (range == nullptr)
        {
            // something went wrong, clean up and throw
            while (!ranges.empty())
            {
                UiaTextRangeBase* temp = ranges[0];
                ranges.pop_front();
            }
            THROW_HR(E_INVALIDARG);
        }
        else
        {
            ranges.push_back(range);
        }
    }
    return ranges;
}

UiaTextRange* UiaTextRange::Create(_In_ IUiaData* pData,
                                   _In_ IRawElementProviderSimple* const pProvider)
{
    try
    {
        return new UiaTextRange(pData, pProvider);
    }
    catch (...)
    {
        return nullptr;
    }
}

UiaTextRange* UiaTextRange::Create(_In_ IUiaData* pData,
                                   _In_ IRawElementProviderSimple* const pProvider,
                                   const Cursor& cursor)
{
    try
    {
        return new UiaTextRange(pData, pProvider, cursor);
    }
    catch (...)
    {
        return nullptr;
    }
}

UiaTextRange* UiaTextRange::Create(_In_ IUiaData* pData,
                                   _In_ IRawElementProviderSimple* const pProvider,
                                   const Endpoint start,
                                   const Endpoint end,
                                   const bool degenerate)
{
    try
    {
        return new UiaTextRange(pData,
                                pProvider,
                                start,
                                end,
                                degenerate);
    }
    catch (...)
    {
        return nullptr;
    }
}

UiaTextRange* UiaTextRange::Create(_In_ IUiaData* pData,
                                   _In_ IRawElementProviderSimple* const pProvider,
                                   const UiaPoint point)
{
    try
    {
        return new UiaTextRange(pData, pProvider, point);
    }
    catch (...)
    {
        return nullptr;
    }
}

// degenerate range constructor.
UiaTextRange::UiaTextRange(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider) :
    UiaTextRangeBase(pData, pProvider)
{
}

UiaTextRange::UiaTextRange(_In_ IUiaData* pData,
                           _In_ IRawElementProviderSimple* const pProvider,
                           const Cursor& cursor) :
    UiaTextRangeBase(pData, pProvider, cursor)
{
}

UiaTextRange::UiaTextRange(_In_ IUiaData* pData,
                           _In_ IRawElementProviderSimple* const pProvider,
                           const Endpoint start,
                           const Endpoint end,
                           const bool degenerate) :
    UiaTextRangeBase(pData, pProvider, start, end, degenerate)
{
}

// returns a degenerate text range of the start of the row closest to the y value of point
UiaTextRange::UiaTextRange(_In_ IUiaData* pData,
                           _In_ IRawElementProviderSimple* const pProvider,
                           const UiaPoint point) :
    UiaTextRangeBase(pData, pProvider)
{
    Initialize(point);
}

IFACEMETHODIMP UiaTextRange::Clone(_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal)
{
    RETURN_HR_IF(E_INVALIDARG, ppRetVal == nullptr);
    *ppRetVal = nullptr;
    try
    {
        *ppRetVal = new UiaTextRange(*this);
    }
    catch (...)
    {
        *ppRetVal = nullptr;
        return wil::ResultFromCaughtException();
    }
    if (*ppRetVal == nullptr)
    {
        return E_OUTOFMEMORY;
    }

#if defined(_DEBUG) && defined(UiaTextRangeBase_DEBUG_MSGS)
    OutputDebugString(L"Clone\n");
    std::wstringstream ss;
    ss << _id << L" cloned to " << (static_cast<UiaTextRangeBase*>(*ppRetVal))->_id;
    std::wstring str = ss.str();
    OutputDebugString(str.c_str());
    OutputDebugString(L"\n");
#endif
    // TODO GitHub #1914: Re-attach Tracing to UIA Tree
    // tracing
    /*ApiMsgClone apiMsg;
    apiMsg.CloneId = static_cast<UiaTextRangeBase*>(*ppRetVal)->GetId();
    Tracing::s_TraceUia(this, ApiCall::Clone, &apiMsg);*/

    return S_OK;
}

IFACEMETHODIMP UiaTextRange::FindText(_In_ BSTR text,
                                      _In_ BOOL searchBackward,
                                      _In_ BOOL ignoreCase,
                                      _Outptr_result_maybenull_ ITextRangeProvider** ppRetVal)
{
    // TODO GitHub #605: Search functionality
    return E_NOTIMPL;
}

void UiaTextRange::_ChangeViewport(const SMALL_RECT /*NewWindow*/)
{
    // TODO GitHub #2361: Update viewport when calling UiaTextRangeBase::ScrollIntoView()
}

// Method Description:
// - Transform coordinates relative to the client to relative to the screen
// Arguments:
// - clientPoint: coordinates relative to the client where
//                (0,0) is the top-left of the app window
// Return Value:
// - <none>
void UiaTextRange::_TranslatePointToScreen(LPPOINT clientPoint) const
{
    auto provider = static_cast<TermControlUiaProvider*>(_pProvider.get());

    // update based on TermControl location (important for Panes)
    UiaRect boundingRect;
    THROW_IF_FAILED(provider->get_BoundingRectangle(&boundingRect));
    clientPoint->x += gsl::narrow<LONG>(boundingRect.left);
    clientPoint->y += gsl::narrow<LONG>(boundingRect.top);

    // update based on TermControl padding
    auto padding = provider->GetPadding();
    clientPoint->x += gsl::narrow<LONG>(padding.Left);
    clientPoint->y += gsl::narrow<LONG>(padding.Top);
}

void UiaTextRange::_TranslatePointFromScreen(LPPOINT /*screenPoint*/) const
{
    // TODO GitHub #2103: NON-HWND IMPLEMENTATION OF SCREENTOCLIENT()
}

const COORD UiaTextRange::_getScreenFontSize() const
{
    // Do NOT get the font info from IRenderData. It is a dummy font info.
    // Instead, the font info is saved in the TermControl. So we have to
    // ask our parent to get it for us.
    auto provider = static_cast<TermControlUiaProvider*>(_pProvider.get());
    return provider->GetFontSize();
}
