// Copyright 2016 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 "xfa/fxfa/parser/cxfa_boxdata.h"

#include "xfa/fxfa/parser/cxfa_corner.h"
#include "xfa/fxfa/parser/cxfa_cornerdata.h"
#include "xfa/fxfa/parser/cxfa_edge.h"
#include "xfa/fxfa/parser/cxfa_fill.h"
#include "xfa/fxfa/parser/cxfa_margin.h"
#include "xfa/fxfa/parser/cxfa_measurement.h"
#include "xfa/fxfa/parser/cxfa_node.h"

namespace {

std::vector<CXFA_StrokeData> GetStrokesInternal(CXFA_Node* pNode, bool bNull) {
  if (!pNode)
    return {};

  std::vector<CXFA_StrokeData> strokes;
  strokes.resize(8);
  int32_t i, j;
  for (i = 0, j = 0; i < 4; i++) {
    CXFA_CornerData cornerData =
        CXFA_CornerData(pNode->JSObject()->GetProperty<CXFA_Corner>(
            i, XFA_Element::Corner, i == 0));
    if (cornerData.HasValidNode() || i == 0) {
      strokes[j] = cornerData;
    } else if (!bNull) {
      if (i == 1 || i == 2)
        strokes[j] = strokes[0];
      else
        strokes[j] = strokes[2];
    }
    j++;
    CXFA_EdgeData edgeData =
        CXFA_EdgeData(pNode->JSObject()->GetProperty<CXFA_Edge>(
            i, XFA_Element::Edge, i == 0));
    if (edgeData.HasValidNode() || i == 0) {
      strokes[j] = edgeData;
    } else if (!bNull) {
      if (i == 1 || i == 2)
        strokes[j] = strokes[1];
      else
        strokes[j] = strokes[3];
    }
    j++;
  }
  return strokes;
}

static XFA_AttributeEnum Style3D(const std::vector<CXFA_StrokeData>& strokes,
                                 CXFA_StrokeData& strokeData) {
  if (strokes.empty())
    return XFA_AttributeEnum::Unknown;

  strokeData = strokes[0];
  for (size_t i = 1; i < strokes.size(); i++) {
    CXFA_StrokeData find = strokes[i];
    if (!find.HasValidNode())
      continue;

    if (!strokeData.HasValidNode())
      strokeData = find;
    else if (strokeData.GetStrokeType() != find.GetStrokeType())
      strokeData = find;
    break;
  }

  XFA_AttributeEnum iType = strokeData.GetStrokeType();
  if (iType == XFA_AttributeEnum::Lowered ||
      iType == XFA_AttributeEnum::Raised ||
      iType == XFA_AttributeEnum::Etched ||
      iType == XFA_AttributeEnum::Embossed) {
    return iType;
  }
  return XFA_AttributeEnum::Unknown;
}

}  // namespace

XFA_AttributeEnum CXFA_BoxData::GetHand() const {
  if (!m_pNode)
    return XFA_AttributeEnum::Even;
  return m_pNode->JSObject()->GetEnum(XFA_Attribute::Hand);
}

XFA_AttributeEnum CXFA_BoxData::GetPresence() const {
  if (!m_pNode)
    return XFA_AttributeEnum::Hidden;
  return m_pNode->JSObject()
      ->TryEnum(XFA_Attribute::Presence, true)
      .value_or(XFA_AttributeEnum::Visible);
}

int32_t CXFA_BoxData::CountEdges() const {
  if (!m_pNode)
    return 0;
  return m_pNode->CountChildren(XFA_Element::Edge, false);
}

CXFA_EdgeData CXFA_BoxData::GetEdgeData(int32_t nIndex) const {
  return CXFA_EdgeData(m_pNode ? m_pNode->JSObject()->GetProperty<CXFA_Edge>(
                                     nIndex, XFA_Element::Edge, nIndex == 0)
                               : nullptr);
}

std::vector<CXFA_StrokeData> CXFA_BoxData::GetStrokes() const {
  return GetStrokesInternal(m_pNode, false);
}

bool CXFA_BoxData::IsCircular() const {
  if (!m_pNode)
    return false;
  return m_pNode->JSObject()->GetBoolean(XFA_Attribute::Circular);
}

pdfium::Optional<int32_t> CXFA_BoxData::GetStartAngle() const {
  if (!m_pNode)
    return {};
  return m_pNode->JSObject()->TryInteger(XFA_Attribute::StartAngle, false);
}

pdfium::Optional<int32_t> CXFA_BoxData::GetSweepAngle() const {
  if (!m_pNode)
    return {};
  return m_pNode->JSObject()->TryInteger(XFA_Attribute::SweepAngle, false);
}

CXFA_FillData CXFA_BoxData::GetFillData(bool bModified) const {
  if (!m_pNode)
    return CXFA_FillData(nullptr);

  CXFA_Node* pFillNode = m_pNode->JSObject()->GetProperty<CXFA_Fill>(
      0, XFA_Element::Fill, bModified);
  return CXFA_FillData(pFillNode);
}

CXFA_MarginData CXFA_BoxData::GetMarginData() const {
  return CXFA_MarginData(
      m_pNode ? m_pNode->GetChild<CXFA_Margin>(0, XFA_Element::Margin, false)
              : nullptr);
}

std::tuple<XFA_AttributeEnum, bool, float> CXFA_BoxData::Get3DStyle() const {
  if (IsArc())
    return {XFA_AttributeEnum::Unknown, false, 0.0f};

  std::vector<CXFA_StrokeData> strokes = GetStrokesInternal(m_pNode, true);
  CXFA_StrokeData strokeData(nullptr);
  XFA_AttributeEnum iType = Style3D(strokes, strokeData);
  if (iType == XFA_AttributeEnum::Unknown)
    return {XFA_AttributeEnum::Unknown, false, 0.0f};

  return {iType, strokeData.IsVisible(), strokeData.GetThickness()};
}