// 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 "public/fpdf_formfill.h"

#include <memory>
#include <utility>
#include <variant>

#include "constants/form_fields.h"
#include "core/fpdfapi/page/cpdf_annotcontext.h"
#include "core/fpdfapi/page/cpdf_occontext.h"
#include "core/fpdfapi/page/cpdf_page.h"
#include "core/fpdfapi/parser/cpdf_dictionary.h"
#include "core/fpdfapi/parser/cpdf_document.h"
#include "core/fpdfapi/parser/cpdf_stream.h"
#include "core/fpdfapi/render/cpdf_renderoptions.h"
#include "core/fpdfdoc/cpdf_formcontrol.h"
#include "core/fpdfdoc/cpdf_formfield.h"
#include "core/fpdfdoc/cpdf_interactiveform.h"
#include "core/fxcrt/cfx_bidi_resolver.h"
#include "core/fxcrt/notreached.h"
#include "core/fxge/cfx_gemodule.h"
#include "core/fxge/cfx_renderdevice.h"
#include "core/fxge/dib/cfx_dibitmap.h"
#include "fpdfsdk/cpdfsdk_annot.h"
#include "fpdfsdk/cpdfsdk_formfillenvironment.h"
#include "fpdfsdk/cpdfsdk_helpers.h"
#include "fpdfsdk/cpdfsdk_interactiveform.h"
#include "fpdfsdk/cpdfsdk_pageview.h"
#include "fpdfsdk/cpdfsdk_widget.h"
#include "public/fpdfview.h"

#ifdef PDF_ENABLE_XFA
#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
#include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
#endif  // PDF_ENABLE_XFA

#if defined(PDF_USE_SKIA)
class SkCanvas;
#endif  // defined(PDF_USE_SKIA)

#ifdef PDF_ENABLE_XFA
static_assert(static_cast<int>(AlertButton::kDefault) ==
                  JSPLATFORM_ALERT_BUTTON_DEFAULT,
              "Default alert button types must match");
static_assert(static_cast<int>(AlertButton::kOK) == JSPLATFORM_ALERT_BUTTON_OK,
              "OK alert button types must match");
static_assert(static_cast<int>(AlertButton::kOKCancel) ==
                  JSPLATFORM_ALERT_BUTTON_OKCANCEL,
              "OKCancel alert button types must match");
static_assert(static_cast<int>(AlertButton::kYesNo) ==
                  JSPLATFORM_ALERT_BUTTON_YESNO,
              "YesNo alert button types must match");
static_assert(static_cast<int>(AlertButton::kYesNoCancel) ==
                  JSPLATFORM_ALERT_BUTTON_YESNOCANCEL,
              "YesNoCancel alert button types must match");

static_assert(static_cast<int>(AlertIcon::kDefault) ==
                  JSPLATFORM_ALERT_ICON_DEFAULT,
              "Default alert icon types must match");
static_assert(static_cast<int>(AlertIcon::kError) ==
                  JSPLATFORM_ALERT_ICON_ERROR,
              "Error alert icon types must match");
static_assert(static_cast<int>(AlertIcon::kWarning) ==
                  JSPLATFORM_ALERT_ICON_WARNING,
              "Warning alert icon types must match");
static_assert(static_cast<int>(AlertIcon::kQuestion) ==
                  JSPLATFORM_ALERT_ICON_QUESTION,
              "Question alert icon types must match");
static_assert(static_cast<int>(AlertIcon::kStatus) ==
                  JSPLATFORM_ALERT_ICON_STATUS,
              "Status alert icon types must match");
static_assert(static_cast<int>(AlertIcon::kAsterisk) ==
                  JSPLATFORM_ALERT_ICON_ASTERISK,
              "Asterisk alert icon types must match");

static_assert(static_cast<int>(AlertReturn::kOK) == JSPLATFORM_ALERT_RETURN_OK,
              "OK alert return types must match");
static_assert(static_cast<int>(AlertReturn::kCancel) ==
                  JSPLATFORM_ALERT_RETURN_CANCEL,
              "Cancel alert return types must match");
static_assert(static_cast<int>(AlertReturn::kNo) == JSPLATFORM_ALERT_RETURN_NO,
              "No alert return types must match");
static_assert(static_cast<int>(AlertReturn::kYes) ==
                  JSPLATFORM_ALERT_RETURN_YES,
              "Yes alert return types must match");

static_assert(static_cast<int>(FormType::kNone) == FORMTYPE_NONE,
              "None form types must match");
static_assert(static_cast<int>(FormType::kAcroForm) == FORMTYPE_ACRO_FORM,
              "AcroForm form types must match");
static_assert(static_cast<int>(FormType::kXFAFull) == FORMTYPE_XFA_FULL,
              "XFA full form types must match");
static_assert(static_cast<int>(FormType::kXFAForeground) ==
                  FORMTYPE_XFA_FOREGROUND,
              "XFA foreground form types must match");
#endif  // PDF_ENABLE_XFA

static_assert(static_cast<int>(FormFieldType::kUnknown) ==
                  FPDF_FORMFIELD_UNKNOWN,
              "Unknown form field types must match");
static_assert(static_cast<int>(FormFieldType::kPushButton) ==
                  FPDF_FORMFIELD_PUSHBUTTON,
              "PushButton form field types must match");
static_assert(static_cast<int>(FormFieldType::kCheckBox) ==
                  FPDF_FORMFIELD_CHECKBOX,
              "CheckBox form field types must match");
static_assert(static_cast<int>(FormFieldType::kRadioButton) ==
                  FPDF_FORMFIELD_RADIOBUTTON,
              "RadioButton form field types must match");
static_assert(static_cast<int>(FormFieldType::kComboBox) ==
                  FPDF_FORMFIELD_COMBOBOX,
              "ComboBox form field types must match");
static_assert(static_cast<int>(FormFieldType::kListBox) ==
                  FPDF_FORMFIELD_LISTBOX,
              "ListBox form field types must match");
static_assert(static_cast<int>(FormFieldType::kTextField) ==
                  FPDF_FORMFIELD_TEXTFIELD,
              "TextField form field types must match");
static_assert(static_cast<int>(FormFieldType::kSignature) ==
                  FPDF_FORMFIELD_SIGNATURE,
              "Signature form field types must match");
#ifdef PDF_ENABLE_XFA
static_assert(static_cast<int>(FormFieldType::kXFA) == FPDF_FORMFIELD_XFA,
              "XFA form field types must match");
static_assert(static_cast<int>(FormFieldType::kXFA_CheckBox) ==
                  FPDF_FORMFIELD_XFA_CHECKBOX,
              "XFA CheckBox form field types must match");
static_assert(static_cast<int>(FormFieldType::kXFA_ComboBox) ==
                  FPDF_FORMFIELD_XFA_COMBOBOX,
              "XFA ComboBox form field types must match");
static_assert(static_cast<int>(FormFieldType::kXFA_ImageField) ==
                  FPDF_FORMFIELD_XFA_IMAGEFIELD,
              "XFA ImageField form field types must match");
static_assert(static_cast<int>(FormFieldType::kXFA_ListBox) ==
                  FPDF_FORMFIELD_XFA_LISTBOX,
              "XFA ListBox form field types must match");
static_assert(static_cast<int>(FormFieldType::kXFA_PushButton) ==
                  FPDF_FORMFIELD_XFA_PUSHBUTTON,
              "XFA PushButton form field types must match");
static_assert(static_cast<int>(FormFieldType::kXFA_Signature) ==
                  FPDF_FORMFIELD_XFA_SIGNATURE,
              "XFA Signature form field types must match");
static_assert(static_cast<int>(FormFieldType::kXFA_TextField) ==
                  FPDF_FORMFIELD_XFA_TEXTFIELD,
              "XFA TextField form field types must match");
#endif  // PDF_ENABLE_XFA
static_assert(kFormFieldTypeCount == FPDF_FORMFIELD_COUNT,
              "Number of form field types must match");

static_assert(static_cast<int>(CPDF_AAction::kCloseDocument) ==
                  FPDFDOC_AACTION_WC,
              "CloseDocument action must match");
static_assert(static_cast<int>(CPDF_AAction::kSaveDocument) ==
                  FPDFDOC_AACTION_WS,
              "SaveDocument action must match");
static_assert(static_cast<int>(CPDF_AAction::kDocumentSaved) ==
                  FPDFDOC_AACTION_DS,
              "DocumentSaved action must match");
static_assert(static_cast<int>(CPDF_AAction::kPrintDocument) ==
                  FPDFDOC_AACTION_WP,
              "PrintDocument action must match");
static_assert(static_cast<int>(CPDF_AAction::kDocumentPrinted) ==
                  FPDFDOC_AACTION_DP,
              "DocumentPrinted action must match");

namespace {

CPDFSDK_Widget* GetWidget(FPDF_FORMHANDLE handle, FPDF_ANNOTATION annot) {
  auto* form_fill_env = CPDFSDKFormFillEnvironmentFromFPDFFormHandle(handle);
  if (!form_fill_env) {
    return nullptr;
  }
  auto* annot_context = CPDFAnnotContextFromFPDFAnnotation(annot);
  if (!annot_context) {
    return nullptr;
  }
  auto* page_view =
      form_fill_env->GetOrCreatePageView(annot_context->GetPage());
  if (!page_view->IsValid()) {
    return nullptr;
  }
  return ToCPDFSDKWidget(
      page_view->GetAnnotByDict(annot_context->GetAnnotDict()));
}

CPDFSDK_PageView* FormHandleToPageView(FPDF_FORMHANDLE hHandle,
                                       FPDF_PAGE fpdf_page) {
  IPDF_Page* pPage = IPDFPageFromFPDFPage(fpdf_page);
  if (!pPage) {
    return nullptr;
  }

  CPDFSDK_FormFillEnvironment* pFormFillEnv =
      CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle);
  return pFormFillEnv ? pFormFillEnv->GetOrCreatePageView(pPage) : nullptr;
}

#if defined(PDF_USE_SKIA)
using BitmapOrCanvas = std::variant<CFX_DIBitmap*, SkCanvas*>;
#else
using BitmapOrCanvas = std::variant<CFX_DIBitmap*>;
#endif

// `dest` must be non-null.
void FFLCommon(FPDF_FORMHANDLE hHandle,
               FPDF_PAGE fpdf_page,
               BitmapOrCanvas dest,
               int start_x,
               int start_y,
               int size_x,
               int size_y,
               int rotate,
               int flags) {
  if (!hHandle) {
    return;
  }

  IPDF_Page* pPage = IPDFPageFromFPDFPage(fpdf_page);
  if (!pPage) {
    return;
  }

  RetainPtr<CFX_DIBitmap> holder;
#if defined(PDF_USE_SKIA)
  SkCanvas* canvas = nullptr;
#endif

  const bool dest_is_bitmap = std::holds_alternative<CFX_DIBitmap*>(dest);
  if (dest_is_bitmap) {
    holder.Reset(std::get<CFX_DIBitmap*>(dest));
    CHECK(holder);
  } else {
#if defined(PDF_USE_SKIA)
    if (!CFX_GEModule::Get()->UseSkiaRenderer()) {
      return;
    }

    canvas = std::get<SkCanvas*>(dest);
    CHECK(canvas);
#endif
  }

  CPDF_Document* pPDFDoc = pPage->GetDocument();
  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, fpdf_page);

  const FX_RECT rect(start_x, start_y, start_x + size_x, start_y + size_y);
  CFX_Matrix matrix = pPage->GetDisplayMatrixForRect(rect, rotate);

  std::unique_ptr<CFX_RenderDevice> pDevice;
  if (dest_is_bitmap) {
    pDevice = CFX_RenderDevice::CreateForBitmap(
        holder, !!(flags & FPDF_REVERSE_BYTE_ORDER));
  } else {
#if defined(PDF_USE_SKIA)
    pDevice = CFX_RenderDevice::CreateForSkiaCanvas(*canvas);
#endif
  }
  if (!pDevice) {
    return;
  }

  {
    CFX_RenderDevice::StateRestorer restorer(pDevice.get());
    pDevice->SetClip_Rect(rect);

    CPDF_RenderOptions options;
    options.GetOptions().bClearType = !!(flags & FPDF_LCD_TEXT);

    // Grayscale output
    if (flags & FPDF_GRAYSCALE) {
      options.SetColorMode(CPDF_RenderOptions::kGray);
    }

    options.SetDrawAnnots(flags & FPDF_ANNOT);
    options.SetOCContext(
        pdfium::MakeRetain<CPDF_OCContext>(pPDFDoc, CPDF_OCContext::kView));

    if (pPageView) {
      pPageView->PageView_OnDraw(pDevice.get(), matrix, &options, rect);
    }
  }
}

// Returns true if formfill version is correctly set. See |version| in
// FPDF_FORMFILLINFO for details regarding correct version.
bool CheckFormfillVersion(FPDF_FORMFILLINFO* formInfo) {
  if (!formInfo || formInfo->version < 1 || formInfo->version > 2) {
    return false;
  }

#ifdef PDF_ENABLE_XFA
  if (formInfo->version != 2) {
    return false;
  }
#endif  // PDF_ENABLE_XFA

  return true;
}

}  // namespace

FPDF_EXPORT int FPDF_CALLCONV
FPDFPage_HasFormFieldAtPoint(FPDF_FORMHANDLE hHandle,
                             FPDF_PAGE page,
                             double page_x,
                             double page_y) {
  const CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
  if (pPage) {
    CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle);
    if (!pForm) {
      return -1;
    }

    const CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm();
    const CPDF_FormControl* pFormCtrl = pPDFForm->GetControlAtPoint(
        pPage,
        CFX_PointF(static_cast<float>(page_x), static_cast<float>(page_y)),
        nullptr);
    if (!pFormCtrl) {
      return -1;
    }
    const CPDF_FormField* pFormField = pFormCtrl->GetField();
    return pFormField ? static_cast<int>(pFormField->GetFieldType()) : -1;
  }

#ifdef PDF_ENABLE_XFA
  const CPDFXFA_Page* pXFAPage = ToXFAPage(IPDFPageFromFPDFPage(page));
  if (pXFAPage) {
    return pXFAPage->HasFormFieldAtPoint(
        CFX_PointF(static_cast<float>(page_x), static_cast<float>(page_y)));
  }
#endif  // PDF_ENABLE_XFA

  return -1;
}

FPDF_EXPORT int FPDF_CALLCONV
FPDFPage_FormFieldZOrderAtPoint(FPDF_FORMHANDLE hHandle,
                                FPDF_PAGE page,
                                double page_x,
                                double page_y) {
  CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle);
  if (!pForm) {
    return -1;
  }

  CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
  if (!pPage) {
    return -1;
  }

  CPDF_InteractiveForm* pPDFForm = pForm->GetInteractiveForm();
  int z_order = -1;
  pPDFForm->GetControlAtPoint(
      pPage, CFX_PointF(static_cast<float>(page_x), static_cast<float>(page_y)),
      &z_order);
  return z_order;
}

FPDF_EXPORT FPDF_FORMHANDLE FPDF_CALLCONV
FPDFDOC_InitFormFillEnvironment(FPDF_DOCUMENT document,
                                FPDF_FORMFILLINFO* formInfo) {
  if (!CheckFormfillVersion(formInfo)) {
    return nullptr;
  }

  auto* pDocument = CPDFDocumentFromFPDFDocument(document);
  if (!pDocument) {
    return nullptr;
  }

#ifdef PDF_ENABLE_XFA
  CPDFXFA_Context* context = nullptr;
  if (!formInfo->xfa_disabled) {
    if (!pDocument->GetExtension()) {
      pDocument->SetExtension(std::make_unique<CPDFXFA_Context>(pDocument));
    }

    // If the CPDFXFA_Context has a FormFillEnvironment already then we've done
    // this and can just return the old Env. Otherwise, we'll end up setting a
    // new environment into the XFADocument and, that could get weird.
    context = static_cast<CPDFXFA_Context*>(pDocument->GetExtension());
    if (context->GetFormFillEnv()) {
      return FPDFFormHandleFromCPDFSDKFormFillEnvironment(
          context->GetFormFillEnv());
    }
  }
#endif  // PDF_ENABLE_XFA

  auto pFormFillEnv =
      std::make_unique<CPDFSDK_FormFillEnvironment>(pDocument, formInfo);

#ifdef PDF_ENABLE_XFA
  if (context) {
    context->SetFormFillEnv(pFormFillEnv.get());
  }
#endif  // PDF_ENABLE_XFA

  ReportUnsupportedXFA(pDocument);

  return FPDFFormHandleFromCPDFSDKFormFillEnvironment(
      pFormFillEnv.release());  // Caller takes ownership.
}

FPDF_EXPORT void FPDF_CALLCONV
FPDFDOC_ExitFormFillEnvironment(FPDF_FORMHANDLE hHandle) {
  if (!hHandle) {
    return;
  }

  // Take back ownership of the form fill environment. This is the inverse of
  // FPDFDOC_InitFormFillEnvironment() above.
  std::unique_ptr<CPDFSDK_FormFillEnvironment> pFormFillEnv(
      CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle));

#ifdef PDF_ENABLE_XFA
  // Reset the focused annotations and remove the SDK document from the
  // XFA document.
  pFormFillEnv->ClearAllFocusedAnnots();
  // If the document was closed first, it's possible the XFA document
  // is now a nullptr.
  auto* context =
      static_cast<CPDFXFA_Context*>(pFormFillEnv->GetDocExtension());
  if (context) {
    context->SetFormFillEnv(nullptr);
  }
#endif  // PDF_ENABLE_XFA
}

FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnMouseMove(FPDF_FORMHANDLE hHandle,
                                                     FPDF_PAGE page,
                                                     int modifier,
                                                     double page_x,
                                                     double page_y) {
  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
  return pPageView &&
         pPageView->OnMouseMove(
             Mask<FWL_EVENTFLAG>::FromUnderlyingUnchecked(modifier),
             CFX_PointF(page_x, page_y));
}

FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FORM_OnMouseWheel(FPDF_FORMHANDLE hHandle,
                  FPDF_PAGE page,
                  int modifier,
                  const FS_POINTF* page_coord,
                  int delta_x,
                  int delta_y) {
  if (!page_coord) {
    return false;
  }

  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
  return pPageView &&
         pPageView->OnMouseWheel(
             Mask<FWL_EVENTFLAG>::FromUnderlyingUnchecked(modifier),
             CFXPointFFromFSPointF(*page_coord), CFX_Vector(delta_x, delta_y));
}

FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnFocus(FPDF_FORMHANDLE hHandle,
                                                 FPDF_PAGE page,
                                                 int modifier,
                                                 double page_x,
                                                 double page_y) {
  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
  return pPageView &&
         pPageView->OnFocus(
             Mask<FWL_EVENTFLAG>::FromUnderlyingUnchecked(modifier),
             CFX_PointF(page_x, page_y));
}

FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnLButtonDown(FPDF_FORMHANDLE hHandle,
                                                       FPDF_PAGE page,
                                                       int modifier,
                                                       double page_x,
                                                       double page_y) {
#ifdef PDF_ENABLE_CLICK_LOGGING
  fprintf(stderr, "mousedown,left,%d,%d\n", static_cast<int>(round(page_x)),
          static_cast<int>(round(page_y)));
#endif  // PDF_ENABLE_CLICK_LOGGING
  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
  return pPageView &&
         pPageView->OnLButtonDown(
             Mask<FWL_EVENTFLAG>::FromUnderlyingUnchecked(modifier),
             CFX_PointF(page_x, page_y));
}

FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnLButtonUp(FPDF_FORMHANDLE hHandle,
                                                     FPDF_PAGE page,
                                                     int modifier,
                                                     double page_x,
                                                     double page_y) {
#ifdef PDF_ENABLE_CLICK_LOGGING
  fprintf(stderr, "mouseup,left,%d,%d\n", static_cast<int>(round(page_x)),
          static_cast<int>(round(page_y)));
#endif  // PDF_ENABLE_CLICK_LOGGING
  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
  return pPageView &&
         pPageView->OnLButtonUp(
             Mask<FWL_EVENTFLAG>::FromUnderlyingUnchecked(modifier),
             CFX_PointF(page_x, page_y));
}

FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FORM_OnLButtonDoubleClick(FPDF_FORMHANDLE hHandle,
                          FPDF_PAGE page,
                          int modifier,
                          double page_x,
                          double page_y) {
#ifdef PDF_ENABLE_CLICK_LOGGING
  fprintf(stderr, "mousedown,doubleleft,%d,%d\n",
          static_cast<int>(round(page_x)), static_cast<int>(round(page_y)));
#endif  // PDF_ENABLE_CLICK_LOGGING
  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
  return pPageView &&
         pPageView->OnLButtonDblClk(
             Mask<FWL_EVENTFLAG>::FromUnderlyingUnchecked(modifier),
             CFX_PointF(page_x, page_y));
}

FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnRButtonDown(FPDF_FORMHANDLE hHandle,
                                                       FPDF_PAGE page,
                                                       int modifier,
                                                       double page_x,
                                                       double page_y) {
#ifdef PDF_ENABLE_CLICK_LOGGING
  fprintf(stderr, "mousedown,right,%d,%d\n", static_cast<int>(round(page_x)),
          static_cast<int>(round(page_y)));
#endif  // PDF_ENABLE_CLICK_LOGGING
  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
  return pPageView &&
         pPageView->OnRButtonDown(
             Mask<FWL_EVENTFLAG>::FromUnderlyingUnchecked(modifier),
             CFX_PointF(page_x, page_y));
}

FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnRButtonUp(FPDF_FORMHANDLE hHandle,
                                                     FPDF_PAGE page,
                                                     int modifier,
                                                     double page_x,
                                                     double page_y) {
#ifdef PDF_ENABLE_CLICK_LOGGING
  fprintf(stderr, "mouseup,right,%d,%d\n", static_cast<int>(round(page_x)),
          static_cast<int>(round(page_y)));
#endif  // PDF_ENABLE_CLICK_LOGGING
  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
  return pPageView &&
         pPageView->OnRButtonUp(
             Mask<FWL_EVENTFLAG>::FromUnderlyingUnchecked(modifier),
             CFX_PointF(page_x, page_y));
}

FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnKeyDown(FPDF_FORMHANDLE hHandle,
                                                   FPDF_PAGE page,
                                                   int nKeyCode,
                                                   int modifier) {
  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
  return pPageView &&
         pPageView->OnKeyDown(
             static_cast<FWL_VKEYCODE>(nKeyCode),
             Mask<FWL_EVENTFLAG>::FromUnderlyingUnchecked(modifier));
}

FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnKeyUp(FPDF_FORMHANDLE hHandle,
                                                 FPDF_PAGE page,
                                                 int nKeyCode,
                                                 int modifier) {
  return false;
}

FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_OnChar(FPDF_FORMHANDLE hHandle,
                                                FPDF_PAGE page,
                                                int nChar,
                                                int modifier) {
  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
  return pPageView &&
         pPageView->OnChar(
             nChar, Mask<FWL_EVENTFLAG>::FromUnderlyingUnchecked(modifier));
}

FPDF_EXPORT unsigned long FPDF_CALLCONV
FORM_GetFocusedText(FPDF_FORMHANDLE hHandle,
                    FPDF_PAGE page,
                    void* buffer,
                    unsigned long buflen) {
  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
  if (!pPageView) {
    return 0;
  }
  // SAFETY: required from caller.
  return Utf16EncodeMaybeCopyAndReturnLength(
      pPageView->GetFocusedFormText(),
      UNSAFE_BUFFERS(SpanFromFPDFApiArgs(buffer, buflen)));
}

FPDF_EXPORT unsigned long FPDF_CALLCONV
FORM_GetSelectedText(FPDF_FORMHANDLE hHandle,
                     FPDF_PAGE page,
                     void* buffer,
                     unsigned long buflen) {
  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
  if (!pPageView) {
    return 0;
  }
  // SAFETY: required from caller.
  return Utf16EncodeMaybeCopyAndReturnLength(
      pPageView->GetSelectedText(),
      UNSAFE_BUFFERS(SpanFromFPDFApiArgs(buffer, buflen)));
}

FPDF_EXPORT void FPDF_CALLCONV
FORM_ReplaceAndKeepSelection(FPDF_FORMHANDLE hHandle,
                             FPDF_PAGE page,
                             FPDF_WIDESTRING wsText) {
  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
  if (!pPageView) {
    return;
  }
  // SAFETY: required from caller.
  pPageView->ReplaceAndKeepSelection(
      UNSAFE_BUFFERS(WideStringFromFPDFWideString(wsText)));
}

FPDF_EXPORT void FPDF_CALLCONV FORM_ReplaceSelection(FPDF_FORMHANDLE hHandle,
                                                     FPDF_PAGE page,
                                                     FPDF_WIDESTRING wsText) {
  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
  if (!pPageView) {
    return;
  }
  // SAFETY: required from caller.
  pPageView->ReplaceSelection(
      UNSAFE_BUFFERS(WideStringFromFPDFWideString(wsText)));
}

FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_SelectAllText(FPDF_FORMHANDLE hHandle,
                                                       FPDF_PAGE page) {
  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
  return pPageView && pPageView->SelectAllText();
}

FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_CanUndo(FPDF_FORMHANDLE hHandle,
                                                 FPDF_PAGE page) {
  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
  if (!pPageView) {
    return false;
  }
  return pPageView->CanUndo();
}

FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_CanRedo(FPDF_FORMHANDLE hHandle,
                                                 FPDF_PAGE page) {
  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
  if (!pPageView) {
    return false;
  }
  return pPageView->CanRedo();
}

FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_Undo(FPDF_FORMHANDLE hHandle,
                                              FPDF_PAGE page) {
  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
  if (!pPageView) {
    return false;
  }
  return pPageView->Undo();
}

FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FORM_Redo(FPDF_FORMHANDLE hHandle,
                                              FPDF_PAGE page) {
  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
  if (!pPageView) {
    return false;
  }
  return pPageView->Redo();
}

FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FORM_ForceToKillFocus(FPDF_FORMHANDLE hHandle) {
  CPDFSDK_FormFillEnvironment* pFormFillEnv =
      CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle);
  if (!pFormFillEnv) {
    return false;
  }
  return pFormFillEnv->KillFocusAnnot({});
}

FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FORM_GetFocusedAnnot(FPDF_FORMHANDLE handle,
                     int* page_index,
                     FPDF_ANNOTATION* annot) {
  if (!page_index || !annot) {
    return false;
  }

  CPDFSDK_FormFillEnvironment* form_fill_env =
      CPDFSDKFormFillEnvironmentFromFPDFFormHandle(handle);
  if (!form_fill_env) {
    return false;
  }

  // Set |page_index| and |annot| to default values. This is returned when there
  // is no focused annotation.
  *page_index = -1;
  *annot = nullptr;

  CPDFSDK_Annot* cpdfsdk_annot = form_fill_env->GetFocusAnnot();
  if (!cpdfsdk_annot) {
    return true;
  }

  // TODO(crbug.com/42270486): Handle XFA case.
  if (cpdfsdk_annot->AsXFAWidget()) {
    return true;
  }

  CPDFSDK_PageView* page_view = cpdfsdk_annot->GetPageView();
  if (!page_view->IsValid()) {
    return true;
  }

  IPDF_Page* page = cpdfsdk_annot->GetPage();
  if (!page) {
    return true;
  }

  RetainPtr<CPDF_Dictionary> annot_dict =
      cpdfsdk_annot->GetPDFAnnot()->GetMutableAnnotDict();
  auto annot_context =
      std::make_unique<CPDF_AnnotContext>(std::move(annot_dict), page);

  *page_index = page_view->GetPageIndex();
  // Caller takes ownership.
  *annot = FPDFAnnotationFromCPDFAnnotContext(annot_context.release());
  return true;
}

FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FORM_SetFocusedAnnot(FPDF_FORMHANDLE handle, FPDF_ANNOTATION annot) {
  CPDFSDK_FormFillEnvironment* form_fill_env =
      CPDFSDKFormFillEnvironmentFromFPDFFormHandle(handle);
  if (!form_fill_env) {
    return false;
  }

  CPDF_AnnotContext* annot_context = CPDFAnnotContextFromFPDFAnnotation(annot);
  if (!annot_context) {
    return false;
  }

  CPDFSDK_PageView* page_view =
      form_fill_env->GetOrCreatePageView(annot_context->GetPage());
  if (!page_view->IsValid()) {
    return false;
  }

  RetainPtr<CPDF_Dictionary> annot_dict = annot_context->GetMutableAnnotDict();
  ObservedPtr<CPDFSDK_Annot> cpdfsdk_annot(
      page_view->GetAnnotByDict(annot_dict.Get()));
  if (!cpdfsdk_annot) {
    return false;
  }

  return form_fill_env->SetFocusAnnot(cpdfsdk_annot);
}

FPDF_EXPORT void FPDF_CALLCONV FPDF_FFLDraw(FPDF_FORMHANDLE hHandle,
                                            FPDF_BITMAP bitmap,
                                            FPDF_PAGE page,
                                            int start_x,
                                            int start_y,
                                            int size_x,
                                            int size_y,
                                            int rotate,
                                            int flags) {
  CFX_DIBitmap* cbitmap = CFXDIBitmapFromFPDFBitmap(bitmap);
  if (!cbitmap) {
    return;
  }

#if defined(PDF_USE_SKIA)
  CFX_DIBitmap::ScopedPremultiplier scoped_premultiplier(
      pdfium::WrapRetain(cbitmap));
#endif
  FFLCommon(hHandle, page, cbitmap, start_x, start_y, size_x, size_y, rotate,
            flags);
}

#if defined(PDF_USE_SKIA)
FPDF_EXPORT void FPDF_CALLCONV FPDF_FFLDrawSkia(FPDF_FORMHANDLE hHandle,
                                                FPDF_SKIA_CANVAS canvas,
                                                FPDF_PAGE page,
                                                int start_x,
                                                int start_y,
                                                int size_x,
                                                int size_y,
                                                int rotate,
                                                int flags) {
  SkCanvas* sk_canvas = SkCanvasFromFPDFSkiaCanvas(canvas);
  if (!sk_canvas) {
    return;
  }

  FFLCommon(hHandle, page, sk_canvas, start_x, start_y, size_x, size_y, rotate,
            flags);
}
#endif  // defined(PDF_USE_SKIA)

FPDF_EXPORT void FPDF_CALLCONV
FPDF_SetFormFieldHighlightColor(FPDF_FORMHANDLE hHandle,
                                int fieldType,
                                unsigned long color) {
  CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle);
  if (!pForm) {
    return;
  }

  std::optional<FormFieldType> cast_input =
      CPDF_FormField::IntToFormFieldType(fieldType);
  if (!cast_input.has_value()) {
    return;
  }

  if (cast_input.value() == FormFieldType::kUnknown) {
    pForm->SetAllHighlightColors(static_cast<FX_COLORREF>(color));
  } else {
    pForm->SetHighlightColor(static_cast<FX_COLORREF>(color),
                             cast_input.value());
  }
}

FPDF_EXPORT void FPDF_CALLCONV
FPDF_SetFormFieldHighlightAlpha(FPDF_FORMHANDLE hHandle, unsigned char alpha) {
  if (CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle)) {
    pForm->SetHighlightAlpha(alpha);
  }
}

FPDF_EXPORT void FPDF_CALLCONV
FPDF_RemoveFormFieldHighlight(FPDF_FORMHANDLE hHandle) {
  if (CPDFSDK_InteractiveForm* pForm = FormHandleToInteractiveForm(hHandle)) {
    pForm->RemoveAllHighLights();
  }
}

FPDF_EXPORT void FPDF_CALLCONV FORM_OnAfterLoadPage(FPDF_PAGE page,
                                                    FPDF_FORMHANDLE hHandle) {
  if (CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page)) {
    pPageView->SetValid(true);
  }
}

FPDF_EXPORT void FPDF_CALLCONV FORM_OnBeforeClosePage(FPDF_PAGE page,
                                                      FPDF_FORMHANDLE hHandle) {
  CPDFSDK_FormFillEnvironment* pFormFillEnv =
      CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle);
  if (!pFormFillEnv) {
    return;
  }

  IPDF_Page* pPage = IPDFPageFromFPDFPage(page);
  if (!pPage) {
    return;
  }

  CPDFSDK_PageView* pPageView = pFormFillEnv->GetPageView(pPage);
  if (pPageView) {
    pPageView->SetValid(false);
    // RemovePageView() takes care of the delete for us.
    pFormFillEnv->RemovePageView(pPage);
  }
}

FPDF_EXPORT void FPDF_CALLCONV
FORM_DoDocumentJSAction(FPDF_FORMHANDLE hHandle) {
  CPDFSDK_FormFillEnvironment* pFormFillEnv =
      CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle);
  if (pFormFillEnv && pFormFillEnv->IsJSPlatformPresent()) {
    pFormFillEnv->ProcJavascriptAction();
  }
}

FPDF_EXPORT void FPDF_CALLCONV
FORM_DoDocumentOpenAction(FPDF_FORMHANDLE hHandle) {
  CPDFSDK_FormFillEnvironment* pFormFillEnv =
      CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle);
  if (pFormFillEnv) {
    pFormFillEnv->ProcOpenAction();
  }
}

FPDF_EXPORT void FPDF_CALLCONV FORM_DoDocumentAAction(FPDF_FORMHANDLE hHandle,
                                                      int aaType) {
  CPDFSDK_FormFillEnvironment* pFormFillEnv =
      CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle);
  if (!pFormFillEnv) {
    return;
  }

  CPDF_Document* doc = pFormFillEnv->GetPDFDocument();
  const CPDF_Dictionary* dict = doc->GetRoot();
  if (!dict) {
    return;
  }

  CPDF_AAction aa(dict->GetDictFor(pdfium::form_fields::kAA));
  auto type = static_cast<CPDF_AAction::AActionType>(aaType);
  if (aa.ActionExist(type)) {
    pFormFillEnv->DoActionDocument(aa.GetAction(type), type);
  }
}

FPDF_EXPORT void FPDF_CALLCONV FORM_DoPageAAction(FPDF_PAGE page,
                                                  FPDF_FORMHANDLE hHandle,
                                                  int aaType) {
  CPDFSDK_FormFillEnvironment* pFormFillEnv =
      CPDFSDKFormFillEnvironmentFromFPDFFormHandle(hHandle);
  if (!pFormFillEnv) {
    return;
  }

  IPDF_Page* pPage = IPDFPageFromFPDFPage(page);
  CPDF_Page* pPDFPage = CPDFPageFromFPDFPage(page);
  if (!pPDFPage) {
    return;
  }

  if (!pFormFillEnv->GetPageView(pPage)) {
    return;
  }

  CPDF_AAction aa(pPDFPage->GetDict()->GetDictFor(pdfium::form_fields::kAA));
  CPDF_AAction::AActionType type = aaType == FPDFPAGE_AACTION_OPEN
                                       ? CPDF_AAction::kOpenPage
                                       : CPDF_AAction::kClosePage;
  if (aa.ActionExist(type)) {
    pFormFillEnv->DoActionPage(aa.GetAction(type), type);
  }
}

FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FORM_SetIndexSelected(FPDF_FORMHANDLE hHandle,
                      FPDF_PAGE page,
                      int index,
                      FPDF_BOOL selected) {
  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
  return pPageView && pPageView->SetIndexSelected(index, selected);
}

FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FORM_IsIndexSelected(FPDF_FORMHANDLE hHandle, FPDF_PAGE page, int index) {
  CPDFSDK_PageView* pPageView = FormHandleToPageView(hHandle, page);
  return pPageView && pPageView->IsIndexSelected(index);
}

FPDF_EXPORT FPDF_TEXT_DIRECTION FPDF_CALLCONV
FORM_GetTextDirection(FPDF_FORMHANDLE handle, FPDF_ANNOTATION annot) {
  CPDFSDK_Widget* widget = GetWidget(handle, annot);
  if (!widget) {
    return FPDF_TEXTDIR_UNKNOWN;
  }

  switch (widget->GetTextDirection()) {
    case CFX_BidiResolver::ParagraphDirection::kAuto:
      return FPDF_TEXTDIR_AUTO;
    case CFX_BidiResolver::ParagraphDirection::kLeftToRight:
      return FPDF_TEXTDIR_LTR;
    case CFX_BidiResolver::ParagraphDirection::kRightToLeft:
      return FPDF_TEXTDIR_RTL;
  }
  NOTREACHED();
}

FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FORM_SetTextDirection(FPDF_FORMHANDLE handle,
                      FPDF_ANNOTATION annot,
                      FPDF_TEXT_DIRECTION direction) {
  CPDFSDK_Widget* widget = GetWidget(handle, annot);
  if (!widget) {
    return false;
  }

  CFX_BidiResolver::ParagraphDirection paragraph_direction;
  switch (direction) {
    case FPDF_TEXTDIR_AUTO:
      paragraph_direction = CFX_BidiResolver::ParagraphDirection::kAuto;
      break;
    case FPDF_TEXTDIR_LTR:
      paragraph_direction = CFX_BidiResolver::ParagraphDirection::kLeftToRight;
      break;
    case FPDF_TEXTDIR_RTL:
      paragraph_direction = CFX_BidiResolver::ParagraphDirection::kRightToLeft;
      break;
    default:
      return false;
  }

  auto* form_fill_env = CPDFSDKFormFillEnvironmentFromFPDFFormHandle(handle);
  CFFL_InteractiveFormFiller* form_filler =
      form_fill_env->GetInteractiveFormFiller();
  form_filler->UpdateFormFieldTextDirection(widget, paragraph_direction);

  return true;
}
