// 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 "xfa/fxfa/app/xfa_fwltheme.h"

#include "xfa/fde/tto/fde_textout.h"
#include "xfa/fgas/crt/fgas_codepage.h"
#include "xfa/fwl/basewidget/ifwl_barcode.h"
#include "xfa/fwl/basewidget/ifwl_caret.h"
#include "xfa/fwl/basewidget/ifwl_checkbox.h"
#include "xfa/fwl/basewidget/ifwl_combobox.h"
#include "xfa/fwl/basewidget/ifwl_datetimepicker.h"
#include "xfa/fwl/basewidget/ifwl_edit.h"
#include "xfa/fwl/basewidget/ifwl_listbox.h"
#include "xfa/fwl/basewidget/ifwl_monthcalendar.h"
#include "xfa/fwl/basewidget/ifwl_picturebox.h"
#include "xfa/fwl/basewidget/ifwl_pushbutton.h"
#include "xfa/fwl/basewidget/ifwl_scrollbar.h"
#include "xfa/fwl/core/cfwl_themebackground.h"
#include "xfa/fwl/core/cfwl_themetext.h"
#include "xfa/fxgraphics/cfx_color.h"
#include "xfa/include/fxfa/xfa_ffapp.h"
#include "xfa/include/fxfa/xfa_ffwidget.h"

namespace {

const FX_WCHAR* const g_FWLTheme_CalFonts[] = {
    L"Arial", L"Courier New", L"DejaVu Sans",
};

}  // namespace

CXFA_FFWidget* XFA_ThemeGetOuterWidget(IFWL_Widget* pWidget) {
  IFWL_Widget* pOuter = pWidget;
  while (pOuter->GetOuter()) {
    pOuter = pOuter->GetOuter();
  }
  if (pOuter) {
    return (CXFA_FFWidget*)pOuter->GetPrivateData(pOuter);
  }
  return NULL;
}
CXFA_FWLTheme::CXFA_FWLTheme(CXFA_FFApp* pApp) : m_pApp(pApp) {
  m_pTextOut = NULL;
  m_dwCapacity = 0;
  m_fCapacity = 0;
  m_pCalendarFont = NULL;
  m_Rect.Set(0, 0, 0, 0);
  m_pCheckBoxTP = new CXFA_FWLCheckBoxTP;
  m_pListBoxTP = new CFWL_ListBoxTP;
  m_pPictureBoxTP = new CFWL_PictureBoxTP;
  m_pSrollBarTP = new CFWL_ScrollBarTP;
  m_pEditTP = new CXFA_FWLEditTP;
  m_pComboBoxTP = new CFWL_ComboBoxTP;
  m_pMonthCalendarTP = new CFWL_MonthCalendarTP;
  m_pDateTimePickerTP = new CFWL_DateTimePickerTP;
  m_pPushButtonTP = new CFWL_PushButtonTP;
  m_pCaretTP = new CFWL_CaretTP;
  m_pBarcodeTP = new CFWL_BarcodeTP;
  Initialize();
}
CXFA_FWLTheme::~CXFA_FWLTheme() {
  Finalize();
  delete m_pCheckBoxTP;
  delete m_pListBoxTP;
  delete m_pPictureBoxTP;
  delete m_pSrollBarTP;
  delete m_pEditTP;
  delete m_pComboBoxTP;
  delete m_pMonthCalendarTP;
  delete m_pDateTimePickerTP;
  delete m_pPushButtonTP;
  delete m_pCaretTP;
  delete m_pBarcodeTP;
}
FWL_ERR CXFA_FWLTheme::Initialize() {
  m_pTextOut = IFDE_TextOut::Create();
  for (size_t i = 0; !m_pCalendarFont && i < FX_ArraySize(g_FWLTheme_CalFonts);
       ++i) {
    m_pCalendarFont = IFX_Font::LoadFont(g_FWLTheme_CalFonts[i], 0, 0,
                                         m_pApp->GetFDEFontMgr());
  }
  if (!m_pCalendarFont) {
#if _FXM_PLATFORM_ == _FXM_PLATFORM_WINDOWS_
    m_pCalendarFont = m_pApp->GetFDEFontMgr()->GetDefFontByCodePage(
        FX_CODEPAGE_MSWin_WesternEuropean, 0, NULL);
#else
    m_pCalendarFont = m_pApp->GetFDEFontMgr()->GetFontByCodePage(
        FX_CODEPAGE_MSWin_WesternEuropean, 0, NULL);
#endif
  }

  FXSYS_assert(NULL != m_pCalendarFont);
  FWLTHEME_Init();
  return FWL_ERR_Succeeded;
}
FWL_ERR CXFA_FWLTheme::Finalize() {
  if (m_pTextOut) {
    m_pTextOut->Release();
    m_pTextOut = NULL;
  }
  if (m_pCalendarFont) {
    m_pCalendarFont->Release();
    m_pCalendarFont = NULL;
  }
  FWLTHEME_Release();
  return FWL_ERR_Succeeded;
}
FX_BOOL CXFA_FWLTheme::IsValidWidget(IFWL_Widget* pWidget) {
  return TRUE;
}
uint32_t CXFA_FWLTheme::GetThemeID(IFWL_Widget* pWidget) {
  return 0;
}
uint32_t CXFA_FWLTheme::SetThemeID(IFWL_Widget* pWidget,
                                   uint32_t dwThemeID,
                                   FX_BOOL bChildren) {
  return 0;
}
FX_BOOL CXFA_FWLTheme::DrawBackground(CFWL_ThemeBackground* pParams) {
  return GetTheme(pParams->m_pWidget)->DrawBackground(pParams);
}
FX_BOOL CXFA_FWLTheme::DrawText(CFWL_ThemeText* pParams) {
  if (pParams->m_wsText.IsEmpty()) {
    return FWL_ERR_Indefinite;
  }
  if (pParams->m_pWidget->GetClassID() == FWL_CLASSHASH_MonthCalendar) {
    CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pParams->m_pWidget);
    if (!pWidget) {
      return FWL_ERR_Indefinite;
    }
    m_pTextOut->SetStyles(pParams->m_dwTTOStyles);
    m_pTextOut->SetAlignment(pParams->m_iTTOAlign);
    m_pTextOut->SetFont(m_pCalendarFont);
    m_pTextOut->SetFontSize(FWLTHEME_CAPACITY_FontSize);
    m_pTextOut->SetTextColor(FWLTHEME_CAPACITY_TextColor);
    if ((pParams->m_iPart == FWL_PART_MCD_DatesIn) &&
        !(pParams->m_dwStates & FWL_ITEMSTATE_MCD_Flag) &&
        (pParams->m_dwStates &
         (FWL_PARTSTATE_MCD_Hovered | FWL_PARTSTATE_MCD_Selected))) {
      m_pTextOut->SetTextColor(0xFFFFFFFF);
    }
    if (pParams->m_iPart == FWL_PART_MCD_Caption) {
      if (m_pMonthCalendarTP->GetThemeID(pParams->m_pWidget) == 0) {
        m_pTextOut->SetTextColor(ArgbEncode(0xff, 0, 153, 255));
      } else {
        m_pTextOut->SetTextColor(ArgbEncode(0xff, 128, 128, 0));
      }
    }
    CFX_Graphics* pGraphics = pParams->m_pGraphics;
    CFX_RenderDevice* pRenderDevice = pGraphics->GetRenderDevice();
    if (!pRenderDevice)
      return FALSE;
    m_pTextOut->SetRenderDevice(pRenderDevice);
    CFX_Matrix mtPart = pParams->m_matrix;
    CFX_Matrix* pMatrix = pGraphics->GetMatrix();
    if (pMatrix) {
      mtPart.Concat(*pMatrix);
    }
    m_pTextOut->SetMatrix(mtPart);
    m_pTextOut->DrawLogicText(pParams->m_wsText, pParams->m_wsText.GetLength(),
                              pParams->m_rtPart);
    return TRUE;
  }
  CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pParams->m_pWidget);
  if (!pWidget) {
    return FWL_ERR_Indefinite;
  }
  CXFA_WidgetAcc* pAcc = pWidget->GetDataAcc();
  CFX_Graphics* pGraphics = pParams->m_pGraphics;
  CFX_RenderDevice* pRenderDevice = pGraphics->GetRenderDevice();
  if (!pRenderDevice)
    return FALSE;
  m_pTextOut->SetRenderDevice(pRenderDevice);
  m_pTextOut->SetStyles(pParams->m_dwTTOStyles);
  m_pTextOut->SetAlignment(pParams->m_iTTOAlign);
  m_pTextOut->SetFont(pAcc->GetFDEFont());
  m_pTextOut->SetFontSize(pAcc->GetFontSize());
  m_pTextOut->SetTextColor(pAcc->GetTextColor());
  CFX_Matrix mtPart = pParams->m_matrix;
  CFX_Matrix* pMatrix = pGraphics->GetMatrix();
  if (pMatrix) {
    mtPart.Concat(*pMatrix);
  }
  m_pTextOut->SetMatrix(mtPart);
  m_pTextOut->DrawLogicText(pParams->m_wsText, pParams->m_wsText.GetLength(),
                            pParams->m_rtPart);
  return TRUE;
}
void* CXFA_FWLTheme::GetCapacity(CFWL_ThemePart* pThemePart,
                                 uint32_t dwCapacity) {
  switch (dwCapacity) {
    case FWL_WGTCAPACITY_Font: {
      if (CXFA_FFWidget* pWidget =
              XFA_ThemeGetOuterWidget(pThemePart->m_pWidget)) {
        return pWidget->GetDataAcc()->GetFDEFont();
      }
    } break;
    case FWL_WGTCAPACITY_FontSize: {
      if (CXFA_FFWidget* pWidget =
              XFA_ThemeGetOuterWidget(pThemePart->m_pWidget)) {
        m_fCapacity = pWidget->GetDataAcc()->GetFontSize();
        return &m_fCapacity;
      }
    } break;
    case FWL_WGTCAPACITY_TextColor: {
      if (CXFA_FFWidget* pWidget =
              XFA_ThemeGetOuterWidget(pThemePart->m_pWidget)) {
        m_dwCapacity = pWidget->GetDataAcc()->GetTextColor();
        return &m_dwCapacity;
      }
    } break;
    case FWL_WGTCAPACITY_LineHeight: {
      if (CXFA_FFWidget* pWidget =
              XFA_ThemeGetOuterWidget(pThemePart->m_pWidget)) {
        m_fCapacity = pWidget->GetDataAcc()->GetLineHeight();
        return &m_fCapacity;
      }
    } break;
    case FWL_WGTCAPACITY_ScrollBarWidth: {
      m_fCapacity = 9;
      return &m_fCapacity;
    } break;
    case FWL_WGTCAPACITY_UIMargin: {
      CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pThemePart->m_pWidget);
      if (pWidget) {
        CXFA_LayoutItem* pItem = pWidget;
        CXFA_WidgetAcc* pWidgetAcc = pWidget->GetDataAcc();
        pWidgetAcc->GetUIMargin(m_Rect);
        if (CXFA_Para para = pWidgetAcc->GetPara()) {
          m_Rect.left += para.GetMarginLeft();
          if (pWidgetAcc->IsMultiLine()) {
            m_Rect.width += para.GetMarginRight();
          }
        }
        if (pItem->GetPrev() == NULL) {
          if (pItem->GetNext()) {
            m_Rect.height = 0;
          }
        } else if (pItem->GetNext() == NULL) {
          m_Rect.top = 0;
        } else {
          m_Rect.top = 0;
          m_Rect.height = 0;
        }
      }
      return &m_Rect;
    } break;
    case FWL_WGTCAPACITY_SpaceAboveBelow: {
      CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pThemePart->m_pWidget);
      if (pWidget) {
        CXFA_WidgetAcc* pWidgetAcc = pWidget->GetDataAcc();
        if (CXFA_Para para = pWidgetAcc->GetPara()) {
          m_SizeAboveBelow.x = para.GetSpaceAbove();
          m_SizeAboveBelow.y = para.GetSpaceBelow();
        }
      }
      return &m_SizeAboveBelow;
    } break;
    default:
      break;
  }
  if (pThemePart->m_pWidget->GetClassID() == FWL_CLASSHASH_MonthCalendar &&
      dwCapacity >= FWL_MCCAPACITY_Sun && dwCapacity <= FWL_MCCAPACITY_Today) {
    if (CXFA_FFWidget* pWidget =
            XFA_ThemeGetOuterWidget(pThemePart->m_pWidget)) {
      IXFA_AppProvider* pAppProvider = pWidget->GetAppProvider();
      m_wsResource.Empty();
      pAppProvider->LoadString(
          XFA_IDS_StringWeekDay_Sun + dwCapacity - FWL_WGTCAPACITY_MAX - 5,
          m_wsResource);
      if (!m_wsResource.IsEmpty()) {
        return &m_wsResource;
      }
    }
  }
  return GetTheme(pThemePart->m_pWidget)->GetCapacity(pThemePart, dwCapacity);
}
FX_BOOL CXFA_FWLTheme::IsCustomizedLayout(IFWL_Widget* pWidget) {
  return GetTheme(pWidget)->IsCustomizedLayout(pWidget);
}
FWL_ERR CXFA_FWLTheme::GetPartRect(CFWL_ThemePart* pThemePart) {
  CFX_RectF rect;
  return GetTheme(pThemePart->m_pWidget)->GetPartRect(pThemePart, rect);
}
FX_BOOL CXFA_FWLTheme::IsInPart(CFWL_ThemePart* pThemePart,
                                FX_FLOAT fx,
                                FX_FLOAT fy) {
  return GetTheme(pThemePart->m_pWidget)->IsInPart(pThemePart, fx, fy);
}
FX_BOOL CXFA_FWLTheme::CalcTextRect(CFWL_ThemeText* pParams, CFX_RectF& rect) {
  if (pParams->m_pWidget->GetClassID() == FWL_CLASSHASH_MonthCalendar) {
    CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pParams->m_pWidget);
    if (!pWidget) {
      return FWL_ERR_Indefinite;
    }
    if (!pParams)
      return FALSE;
    if (!m_pTextOut)
      return FALSE;
    m_pTextOut->SetFont(m_pCalendarFont);
    m_pTextOut->SetFontSize(FWLTHEME_CAPACITY_FontSize);
    m_pTextOut->SetTextColor(FWLTHEME_CAPACITY_TextColor);
    m_pTextOut->SetAlignment(pParams->m_iTTOAlign);
    m_pTextOut->SetStyles(pParams->m_dwTTOStyles);
    m_pTextOut->CalcLogicSize(pParams->m_wsText, pParams->m_wsText.GetLength(),
                              rect);
    return TRUE;
  }
  CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pParams->m_pWidget);
  if (!pWidget) {
    return FWL_ERR_Indefinite;
  }
  CXFA_WidgetAcc* pAcc = pWidget->GetDataAcc();
  m_pTextOut->SetFont(pAcc->GetFDEFont());
  m_pTextOut->SetFontSize(pAcc->GetFontSize());
  m_pTextOut->SetTextColor(pAcc->GetTextColor());
  if (!pParams)
    return FALSE;
  if (!m_pTextOut)
    return FALSE;
  m_pTextOut->SetAlignment(pParams->m_iTTOAlign);
  m_pTextOut->SetStyles(pParams->m_dwTTOStyles);
  m_pTextOut->CalcLogicSize(pParams->m_wsText, pParams->m_wsText.GetLength(),
                            rect);
  return TRUE;
}
CFWL_WidgetTP* CXFA_FWLTheme::GetTheme(IFWL_Widget* pWidget) {
  switch (pWidget->GetClassID()) {
    case FWL_CLASSHASH_CheckBox:
      return m_pCheckBoxTP;
    case FWL_CLASSHASH_ListBox:
      return m_pListBoxTP;
    case FWL_CLASSHASH_PictureBox:
      return m_pPictureBoxTP;
    case FWL_CLASSHASH_ScrollBar:
      return m_pSrollBarTP;
    case FWL_CLASSHASH_Edit:
      return m_pEditTP;
    case FWL_CLASSHASH_ComboBox:
      return m_pComboBoxTP;
    case FWL_CLASSHASH_MonthCalendar:
      return m_pMonthCalendarTP;
    case FWL_CLASSHASH_DateTimePicker:
      return m_pDateTimePickerTP;
    case FWL_CLASSHASH_PushButton:
      return m_pPushButtonTP;
    case FWL_CLASSHASH_Caret:
      return m_pCaretTP;
    case FWL_CLASSHASH_Barcode:
      return m_pBarcodeTP;
    default:
      break;
  }
  return NULL;
}
CXFA_FWLCheckBoxTP::CXFA_FWLCheckBoxTP() {}
FX_BOOL CXFA_FWLCheckBoxTP::DrawBackground(CFWL_ThemeBackground* pParams) {
  if (pParams->m_iPart != FWL_PART_CKB_CheckBox) {
    return TRUE;
  }
  if (((pParams->m_dwStates & FWL_PARTSTATE_CKB_Mask2) ==
       FWL_PARTSTATE_CKB_Checked) ||
      ((pParams->m_dwStates & FWL_PARTSTATE_CKB_Mask2) ==
       FWL_PARTSTATE_CKB_Neutral)) {
    DrawCheckSign(pParams->m_pWidget, pParams->m_pGraphics, &pParams->m_rtPart,
                  pParams->m_dwStates, &pParams->m_matrix);
  }
  return TRUE;
}
void CXFA_FWLCheckBoxTP::DrawCheckSign(IFWL_Widget* pWidget,
                                       CFX_Graphics* pGraphics,
                                       const CFX_RectF* pRtBox,
                                       int32_t iState,
                                       CFX_Matrix* pMatrix) {
  CFX_RectF rtSign(*pRtBox);
  uint32_t dwColor = 0xFF000000;
  if ((iState & FWL_PARTSTATE_CKB_Mask2) == FWL_PARTSTATE_CKB_Neutral) {
    dwColor = 0xFFA9A9A9;
  }
  {
    uint32_t dwStyle = pWidget->GetStylesEx();
    rtSign.Deflate(rtSign.width / 4, rtSign.height / 4);
    switch (dwStyle & FWL_STYLEEXT_CKB_SignShapeMask) {
      case FWL_STYLEEXT_CKB_SignShapeCheck:
        DrawSignCheck(pGraphics, &rtSign, dwColor, pMatrix);
        break;
      case FWL_STYLEEXT_CKB_SignShapeCircle:
        DrawSignCircle(pGraphics, &rtSign, dwColor, pMatrix);
        break;
      case FWL_STYLEEXT_CKB_SignShapeCross:
        DrawSignCross(pGraphics, &rtSign, dwColor, pMatrix);
        break;
      case FWL_STYLEEXT_CKB_SignShapeDiamond:
        DrawSignDiamond(pGraphics, &rtSign, dwColor, pMatrix);
        break;
      case FWL_STYLEEXT_CKB_SignShapeSquare:
        DrawSignSquare(pGraphics, &rtSign, dwColor, pMatrix);
        break;
      case FWL_STYLEEXT_CKB_SignShapeStar:
        DrawSignStar(pGraphics, &rtSign, dwColor, pMatrix);
        break;
      default:
        break;
    }
  }
}
CXFA_FWLEditTP::CXFA_FWLEditTP() {}
CXFA_FWLEditTP::~CXFA_FWLEditTP() {}
FX_BOOL CXFA_FWLEditTP::DrawBackground(CFWL_ThemeBackground* pParams) {
  if (FWL_PART_EDT_CombTextLine == pParams->m_iPart) {
    CXFA_FFWidget* pWidget = XFA_ThemeGetOuterWidget(pParams->m_pWidget);
    FX_ARGB cr = 0xFF000000;
    FX_FLOAT fWidth = 1.0f;
    if (CXFA_Border borderUI = pWidget->GetDataAcc()->GetUIBorder()) {
      CXFA_Edge edge = borderUI.GetEdge(0);
      if (edge) {
        cr = edge.GetColor();
        fWidth = edge.GetThickness();
      }
    }
    CFX_Color crLine(cr);
    pParams->m_pGraphics->SetStrokeColor(&crLine);
    pParams->m_pGraphics->SetLineWidth(fWidth);
    pParams->m_pGraphics->StrokePath(pParams->m_pPath, &pParams->m_matrix);
    return TRUE;
  }
  return CFWL_EditTP::DrawBackground(pParams);
}