// Copyright 2014 PDFium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
 
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com

#include <algorithm>

#include "reflowedtextpage.h"

IPDF_TextPage*	IPDF_TextPage::CreateReflowTextPage(IPDF_ReflowedPage* pRefPage)
{
    return new CRF_TextPage(pRefPage);
}
CRF_TextPage::CRF_TextPage(IPDF_ReflowedPage* pRefPage)
{
    m_pRefPage = (CPDF_ReflowedPage*)(pRefPage);
    m_pDataList = NULL;
    m_CountBSArray = NULL;
}
CRF_TextPage::~CRF_TextPage()
{
    if(m_pDataList) {
        delete m_pDataList;
        m_pDataList = NULL;
    }
    if(m_CountBSArray) {
        delete m_CountBSArray;
        m_CountBSArray = NULL;
    }
}
FX_BOOL CRF_TextPage::ParseTextPage()
{
    if(!m_pRefPage) {
        return FALSE;
    }
    int count = m_pRefPage->m_pReflowed->GetSize();
    m_pDataList = new CRF_CharDataPtrArray(std::min(count, 500));
    for(int i = 0; i < count; i++) {
        CRF_Data* pData = (*(m_pRefPage->m_pReflowed))[i];
        if(pData->GetType() == CRF_Data::Text) {
            m_pDataList->Add((CRF_CharData*)pData);
        }
    }
    m_CountBSArray = new CFX_CountBSINT32Array(20);
    return TRUE;
}
FX_BOOL	CRF_TextPage::IsParsered() const
{
    if(m_pDataList) {
        return TRUE;
    }
    return FALSE;
}
int CRF_TextPage::CharIndexFromTextIndex(int TextIndex) const
{
    return TextIndex;
}
int CRF_TextPage::TextIndexFromCharIndex(int CharIndex) const
{
    return CharIndex;
}

int	CRF_TextPage::CountChars() const
{
    if (NULL == m_pDataList) {
        return -1;
    }
    return m_pDataList->GetSize();
}
void CRF_TextPage::GetCharInfo(int index, FPDF_CHAR_INFO & info) const
{
    if(index >= CountChars() || index < 0 || !m_pDataList) {
        return;
    }
    CRF_CharData* pData = (*m_pDataList)[index];
    FX_FLOAT ReltiveCorddDs = pData->m_pCharState->m_fDescent;
    FX_FLOAT ReltiveCorddAs = pData->m_pCharState->m_fAscent;
    info.m_Flag		= CHAR_NORMAL;
    info.m_pTextObj	= pData->m_pCharState->m_pTextObj;
    info.m_OriginX	= pData->m_PosX;
    info.m_OriginY	= pData->m_PosY - ReltiveCorddDs;
    info.m_FontSize	= pData->m_pCharState->m_fFontSize;
    CFX_FloatRect FloatRectTmp(pData->m_PosX, pData->m_PosY, pData->m_PosX + pData->m_Width, pData->m_PosY + ReltiveCorddAs - ReltiveCorddDs);
    info.m_CharBox	= FloatRectTmp;
    CFX_WideString str = pData->m_pCharState->m_pFont->UnicodeFromCharCode(pData->m_CharCode);
    if(!str.IsEmpty()) {
        info.m_Unicode	= str.GetAt(0);
    } else {
        info.m_Unicode = -1;
    }
    info.m_Charcode = (FX_WCHAR)pData->m_CharCode;
    info.m_Matrix = CFX_Matrix(1, 0, 0, 1, 0, 0);
}
extern FX_BOOL GetIntersection(FX_FLOAT low1, FX_FLOAT high1, FX_FLOAT low2, FX_FLOAT high2, FX_FLOAT& interlow, FX_FLOAT& interhigh);
inline FX_BOOL _IsInsameline(const CFX_FloatRect& rectA, const CFX_FloatRect& rectB)
{
    if((rectA.top >= rectB.bottom && rectB.top >= rectA.bottom)) {
        return TRUE;
    } else {
        return FALSE;
    }
}
inline FX_BOOL _IsIntersect(const CFX_FloatRect& rectA, const CFX_FloatRect& rectB)
{
    FX_FLOAT interlow = .0f, interhigh = .0f;
    if(GetIntersection(rectA.bottom, rectA.top, rectB.bottom, rectB.top, interlow, interhigh)) {
        if(GetIntersection(rectA.left, rectA.right, rectB.left, rectB.right, interlow, interhigh)) {
            return TRUE;
        } else {
            return FALSE;
        }
    }
    return FALSE;
}
void CRF_TextPage::GetRectArray(int start, int nCount, CFX_RectArray& rectArray) const
{
    int indexlen = start + nCount;
    FPDF_CHAR_INFO info;
    FX_BOOL bstart = TRUE;
    CFX_FloatRect recttmp;
    int i;
    for(i = start; i < indexlen; i++) {
        GetCharInfo(i, info);
        if(bstart) {
            recttmp = info.m_CharBox;
            bstart = FALSE;
        } else if(_IsInsameline(recttmp, info.m_CharBox)) {
            recttmp.right = info.m_CharBox.right;
            if(info.m_CharBox.top > recttmp.top) {
                recttmp.top = info.m_CharBox.top;
            }
            if(info.m_CharBox.bottom < recttmp.bottom) {
                recttmp.bottom = info.m_CharBox.bottom;
            }
        } else {
            rectArray.Add(recttmp);
            recttmp = info.m_CharBox;
        }
    }
    rectArray.Add(recttmp);
}
inline FX_FLOAT _GetDistance(CFX_FloatRect floatRect, CPDF_Point point)
{
    if(floatRect.right < point.x && floatRect.bottom > point.y) {
        return FXSYS_sqrt(FXSYS_pow(point.x - floatRect.right, 2) + FXSYS_pow(floatRect.bottom - point.y, 2));
    }
    if (floatRect.right < point.x && floatRect.top < point.y) {
        return FXSYS_sqrt(FXSYS_pow(point.x - floatRect.right, 2) + FXSYS_pow(point.y - floatRect.top, 2));
    }
    if(floatRect.left > point.x && floatRect.bottom > point.y) {
        return FXSYS_sqrt(FXSYS_pow(floatRect.bottom - point.y, 2) + FXSYS_pow(floatRect.left - point.x, 2));
    }
    if((floatRect.right > point.x || FXSYS_fabs(floatRect.right - point.x) <= 0.0001f) &&
            (floatRect.left < point.x || FXSYS_fabs(floatRect.left - point.x) <= 0.0001f) && floatRect.bottom > point.y) {
        return FXSYS_fabs(floatRect.bottom - point.y);
    }
    if(floatRect.left > point.x && (floatRect.bottom < point.y || FXSYS_fabs(floatRect.bottom - point.y) <= 0.0001f) &&
            (floatRect.top > point.y || FXSYS_fabs(floatRect.top - point.y) <= 0.0001f)) {
        return FXSYS_fabs(floatRect.left - point.x);
    }
    if(floatRect.left > point.x && floatRect.top < point.y) {
        return FXSYS_sqrt(FXSYS_pow(floatRect.left - point.x, 2) + FXSYS_pow(point.y - floatRect.top, 2));
    }
    if ((floatRect.left < point.x || FXSYS_fabs(floatRect.left - point.x) <= 0.0001f) &&
            (floatRect.right > point.x || FXSYS_fabs(floatRect.right - point.x) <= 0.0001f) && floatRect.top < point.y) {
        return FXSYS_fabs(point.y - floatRect.top);
    }
    if(floatRect.right < point.x && (floatRect.top > point.y || FXSYS_fabs(floatRect.top - point.y) <= 0.0001f) &&
            (floatRect.bottom < point.y || FXSYS_fabs(floatRect.bottom - point.y) <= 0.0001f)) {
        return point.x - floatRect.right;
    }
    return .0f;
}
int CRF_TextPage::GetIndexAtPos(CPDF_Point point, FX_FLOAT xTorelance, FX_FLOAT yTorelance) const
{
    int index = -1, i = 0, j = 0;
    FPDF_CHAR_INFO info;
    CFX_FloatRect rectTmp;
    FX_FLOAT MinDistance = 1000, DistanceTmp = 0;
    FX_FLOAT rect_bottom = point.x - xTorelance;
    CFX_FloatRect TorelanceRect(rect_bottom <= 0 ? 0 : rect_bottom, point.y - yTorelance, point.x + xTorelance, point.y + yTorelance);
    int count = CountChars();
    for(i = 0; i < count; i++) {
        GetCharInfo(i, info);
        rectTmp = info.m_CharBox;
        if(rectTmp.Contains(point.x, point.y)) {
            index = i;
            break;
        } else if(_IsIntersect(rectTmp, TorelanceRect)) {
            DistanceTmp = _GetDistance(rectTmp, point);
            if(DistanceTmp < MinDistance) {
                MinDistance = DistanceTmp;
                index = i;
            }
        }
    }
    return index;
}
int CRF_TextPage::GetIndexAtPos(FX_FLOAT x, FX_FLOAT y, FX_FLOAT xTorelance, FX_FLOAT yTorelance) const
{
    int index = 0;
    CPDF_Point point(x, y);
    if((index = GetIndexAtPos(point, xTorelance, yTorelance)) < 0) {
        return -1;
    } else {
        return index;
    }
}
int CRF_TextPage::GetOrderByDirection(int index, int direction) const
{
    return -1;
}
CFX_WideString CRF_TextPage::GetTextByRect(CFX_FloatRect rect) const
{
    int count;
    FPDF_CHAR_INFO info;
    CFX_WideString str;
    CFX_FloatRect  Recttmp;
    FX_BOOL bstart = TRUE;
    count = CountChars();
    if(rect.IsEmpty()) {
        return L"";
    }
    for(int i = 0; i < count; i++) {
        GetCharInfo(i, info);
        if(_IsIntersect(rect, info.m_CharBox)) {
            if(bstart) {
                Recttmp = info.m_CharBox;
                str += info.m_Unicode;
                bstart = FALSE;
            } else if(_IsInsameline(Recttmp, info.m_CharBox)) {
                str += info.m_Unicode;
            } else {
                str += L"\r\n";
                Recttmp = info.m_CharBox;
                str += info.m_Unicode;
            }
        }
    }
    if(str.IsEmpty()) {
        return L"";
    } else {
        return str;
    }
}
void CRF_TextPage::GetRectsArrayByRect(CFX_FloatRect rect, CFX_RectArray& resRectArray) const
{
    int count, i;
    FX_BOOL bstart = TRUE;
    FPDF_CHAR_INFO info;
    CFX_FloatRect recttmp;
    count = CountChars();
    for(i = 0; i < count; i++) {
        GetCharInfo(i, info);
        if(_IsIntersect(rect, info.m_CharBox)) {
            if(bstart) {
                recttmp = info.m_CharBox;
                bstart = FALSE;
            } else if(_IsInsameline(recttmp, info.m_CharBox)) {
                recttmp.right = info.m_CharBox.right;
                if(info.m_CharBox.top > recttmp.top) {
                    recttmp.top = info.m_CharBox.top;
                }
                if(info.m_CharBox.bottom < recttmp.bottom) {
                    recttmp.bottom = info.m_CharBox.bottom;
                }
            } else {
                resRectArray.Add(recttmp);
                recttmp = info.m_CharBox;
            }
        }
    }
    resRectArray.Add(recttmp);
}
int CRF_TextPage::CountRects(int start, int nCount)
{
    m_rectArray.RemoveAll();
    GetRectArray(start, nCount, m_rectArray);
    return m_rectArray.GetSize();
}
void CRF_TextPage::GetRect(int rectIndex, FX_FLOAT& left, FX_FLOAT& top, FX_FLOAT& right, FX_FLOAT &bottom) const
{
    if(m_rectArray.GetSize() <= rectIndex) {
        return;
    }
    left   = m_rectArray[rectIndex].left;
    top    = m_rectArray[rectIndex].top;
    right  = m_rectArray[rectIndex].right;
    bottom = m_rectArray[rectIndex].bottom;
}
FX_BOOL CRF_TextPage::GetBaselineRotate(int rectIndex, int& Rotate)
{
    Rotate = 0;
    return TRUE;
}
FX_BOOL CRF_TextPage::GetBaselineRotate(CFX_FloatRect rect, int& Rotate)
{
    Rotate = 0;
    return TRUE;
}
int CRF_TextPage::CountBoundedSegments(FX_FLOAT left, FX_FLOAT top, FX_FLOAT right, FX_FLOAT bottom, FX_BOOL bContains)
{
    if (!m_CountBSArray) {
        return -1;
    }
    m_CountBSArray->RemoveAll();
    CFX_FloatRect floatrect(left, bottom, right, top);
    int totalcount, i, j = 0, counttmp = 0;
    FX_BOOL bstart = TRUE;
    FPDF_CHAR_INFO info;
    CFX_FloatRect recttmp;
    totalcount = CountChars();
    for(i = 0; i < totalcount; i++) {
        GetCharInfo(i, info);
        if(_IsIntersect(floatrect, info.m_CharBox)) {
            if(bstart) {
                m_CountBSArray->Add(i);
                counttmp = 1;
                recttmp = info.m_CharBox;
                bstart = FALSE;
            } else if(_IsInsameline(recttmp, info.m_CharBox)) {
                recttmp.right = info.m_CharBox.right;
                if(info.m_CharBox.top > recttmp.top) {
                    recttmp.top = info.m_CharBox.top;
                }
                if(info.m_CharBox.bottom < recttmp.bottom) {
                    recttmp.bottom = info.m_CharBox.bottom;
                }
                counttmp ++;
            } else {
                m_CountBSArray->Add(counttmp);
                m_CountBSArray->Add(i);
                counttmp = 1;
                j++;
                recttmp = info.m_CharBox;
            }
        }
    }
    m_CountBSArray->Add(counttmp);
    j++;
    return j;
}
void CRF_TextPage::GetBoundedSegment(int index, int& start, int& count) const
{
    if (!m_CountBSArray) {
        return;
    }
    if(m_CountBSArray->GetSize() <= index * 2) {
        start = 0;
        count = 0;
        return;
    }
    start = *(int *)m_CountBSArray->GetAt(index * 2);
    count = *(int *)m_CountBSArray->GetAt(index * 2 + 1);
}

int CRF_TextPage::GetWordBreak(int index, int direction) const
{
    return -1;
}
CFX_WideString CRF_TextPage::GetPageText(int start, int nCount ) const
{
    if(nCount == -1) {
        nCount = CountChars();
        start = 0;
    } else if(nCount < 1) {
        return L"";
    } else if(start >= CountChars()) {
        return L"";
    }
    int i, index = start + nCount;
    FPDF_CHAR_INFO info;
    CFX_WideString str;
    CFX_FloatRect recttmp;
    FX_BOOL bstart = TRUE;
    for(i = start; i < index; i++) {
        GetCharInfo(i, info);
        if(bstart) {
            recttmp = info.m_CharBox;
            str += info.m_Unicode;
            bstart = FALSE;
        } else if (_IsInsameline(recttmp, info.m_CharBox)) {
            str += info.m_Unicode;
        } else {
            str += L"\r\n";
            recttmp = info.m_CharBox;
            str += info.m_Unicode;
        }
    }
    if(str.IsEmpty()) {
        return L"";
    }
    return str;
}