// Copyright 2014 The PDFium Authors
// 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 "xfa/fxfa/cxfa_fftext.h"

#include "xfa/fgas/graphics/cfgas_gegraphics.h"
#include "xfa/fgas/layout/cfgas_linkuserdata.h"
#include "xfa/fwl/fwl_widgethit.h"
#include "xfa/fxfa/cxfa_ffapp.h"
#include "xfa/fxfa/cxfa_ffdoc.h"
#include "xfa/fxfa/cxfa_ffpageview.h"
#include "xfa/fxfa/cxfa_ffwidget.h"
#include "xfa/fxfa/cxfa_textlayout.h"
#include "xfa/fxfa/parser/cxfa_margin.h"

CXFA_FFText::CXFA_FFText(CXFA_Node* pNode) : CXFA_FFWidget(pNode) {}

CXFA_FFText::~CXFA_FFText() = default;

void CXFA_FFText::RenderWidget(CFGAS_GEGraphics* pGS,
                               const CFX_Matrix& matrix,
                               HighlightOption highlight) {
  if (!HasVisibleStatus()) {
    return;
  }

  CFX_Matrix mtRotate = GetRotateMatrix();
  mtRotate.Concat(matrix);

  CXFA_FFWidget::RenderWidget(pGS, mtRotate, highlight);

  CXFA_TextLayout* pTextLayout = node_->GetTextLayout();
  if (!pTextLayout) {
    return;
  }

  CFX_RenderDevice* pRenderDevice = pGS->GetRenderDevice();
  CFX_RectF rtText = GetRectWithoutRotate();
  CXFA_Margin* margin = node_->GetMarginIfExists();
  if (margin) {
    CXFA_ContentLayoutItem* pItem = GetLayoutItem();
    if (!pItem->GetPrev() && !pItem->GetNext()) {
      XFA_RectWithoutMargin(&rtText, margin);
    } else {
      float fTopInset = 0;
      float fBottomInset = 0;
      if (!pItem->GetPrev()) {
        fTopInset = margin->GetTopInset();
      } else if (!pItem->GetNext()) {
        fBottomInset = margin->GetBottomInset();
      }

      rtText.Deflate(margin->GetLeftInset(), fTopInset, margin->GetRightInset(),
                     fBottomInset);
    }
  }

  CFX_Matrix mt(1, 0, 0, 1, rtText.left, rtText.top);
  CFX_RectF rtClip = mtRotate.TransformRect(rtText);
  mt.Concat(mtRotate);
  pTextLayout->DrawString(pRenderDevice, mt, rtClip,
                          GetLayoutItem()->GetIndex());
}

bool CXFA_FFText::IsLoaded() {
  CXFA_TextLayout* pTextLayout = node_->GetTextLayout();
  return pTextLayout && !pTextLayout->HasBlock();
}

void CXFA_FFText::PerformLayout() {
  CXFA_FFWidget::PerformLayout();
  CXFA_TextLayout* pTextLayout = node_->GetTextLayout();
  if (!pTextLayout || !pTextLayout->HasBlock()) {
    return;
  }
  pTextLayout->ClearBlocks();
  CXFA_ContentLayoutItem* pItem = GetLayoutItem();
  if (!pItem->GetPrev() && !pItem->GetNext()) {
    return;
  }
  pItem = pItem->GetFirst();
  while (pItem) {
    CFX_RectF rtText = pItem->GetAbsoluteRect();
    CXFA_Margin* margin = node_->GetMarginIfExists();
    if (margin) {
      if (!pItem->GetPrev()) {
        rtText.height -= margin->GetTopInset();
      } else if (!pItem->GetNext()) {
        rtText.height -= margin->GetBottomInset();
      }
    }
    pTextLayout->ItemBlocks(rtText, pItem->GetIndex());
    pItem = pItem->GetNext();
  }
  pTextLayout->ResetHasBlock();
}

bool CXFA_FFText::AcceptsFocusOnButtonDown(
    Mask<XFA_FWL_KeyFlag> dwFlags,
    const CFX_PointF& point,
    CFWL_MessageMouse::MouseCommand command) {
  return command == CFWL_MessageMouse::MouseCommand::kLeftButtonDown &&
         GetRectWithoutRotate().Contains(point) &&
         !GetLinkURLAtPoint(point).IsEmpty();
}

bool CXFA_FFText::OnLButtonDown(Mask<XFA_FWL_KeyFlag> dwFlags,
                                const CFX_PointF& point) {
  SetButtonDown(true);
  return true;
}

bool CXFA_FFText::OnMouseMove(Mask<XFA_FWL_KeyFlag> dwFlags,
                              const CFX_PointF& point) {
  return GetRectWithoutRotate().Contains(point) &&
         !GetLinkURLAtPoint(point).IsEmpty();
}

bool CXFA_FFText::OnLButtonUp(Mask<XFA_FWL_KeyFlag> dwFlags,
                              const CFX_PointF& point) {
  if (!IsButtonDown()) {
    return false;
  }

  SetButtonDown(false);
  WideString wsURLContent = GetLinkURLAtPoint(point);
  if (wsURLContent.IsEmpty()) {
    return false;
  }

  GetDoc()->GotoURL(wsURLContent);
  return true;
}

FWL_WidgetHit CXFA_FFText::HitTest(const CFX_PointF& point) {
  if (GetRectWithoutRotate().Contains(point) &&
      !GetLinkURLAtPoint(point).IsEmpty()) {
    return FWL_WidgetHit::HyperLink;
  }
  return FWL_WidgetHit::Unknown;
}

WideString CXFA_FFText::GetLinkURLAtPoint(const CFX_PointF& point) {
  CXFA_TextLayout* pTextLayout = node_->GetTextLayout();
  if (!pTextLayout) {
    return WideString();
  }

  CFX_RectF rect = GetRectWithoutRotate();
  return pTextLayout->GetLinkURLAtPoint(point - rect.TopLeft());
}
