// Copyright 2016 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 "core/fpdfdoc/cpdf_defaultappearance.h"

#include <algorithm>
#include <vector>

#include "core/fpdfapi/parser/cpdf_dictionary.h"
#include "core/fpdfapi/parser/cpdf_simple_parser.h"
#include "core/fpdfapi/parser/fpdf_parser_utility.h"
#include "core/fpdfdoc/cpdf_formfield.h"
#include "core/fxcrt/fx_string.h"
#include "core/fxcrt/notreached.h"
#include "core/fxge/cfx_color.h"

namespace {

ByteString GetDefaultAppearanceString(const CPDF_Dictionary* annot_dict,
                                      const CPDF_Dictionary* acroform_dict) {
  ByteString default_appearance_string;
  RetainPtr<const CPDF_Object> default_appearance_object =
      CPDF_FormField::GetFieldAttrForDict(annot_dict, "DA");
  if (default_appearance_object) {
    default_appearance_string = default_appearance_object->GetString();
  }
  if (default_appearance_string.IsEmpty() && acroform_dict) {
    default_appearance_string = acroform_dict->GetByteStringFor("DA");
  }
  return default_appearance_string;
}

// Find the token and its |nParams| parameters from the start of data,
// and move the current position to the start of those parameters.
bool FindTagParamFromStart(CPDF_SimpleParser* parser,
                           ByteStringView token,
                           int nParams) {
  nParams++;

  std::vector<uint32_t> pBuf(nParams);
  int buf_index = 0;
  int buf_count = 0;

  parser->SetCurrentPosition(0);
  while (true) {
    pBuf[buf_index++] = parser->GetCurrentPosition();
    if (buf_index == nParams) {
      buf_index = 0;
    }

    buf_count++;
    if (buf_count > nParams) {
      buf_count = nParams;
    }

    ByteStringView word = parser->GetWord();
    if (word.IsEmpty()) {
      return false;
    }

    if (word == token) {
      if (buf_count < nParams) {
        continue;
      }

      parser->SetCurrentPosition(pBuf[buf_index]);
      return true;
    }
  }
}

}  // namespace

CPDF_DefaultAppearance::CPDF_DefaultAppearance(const ByteString& csDA)
    : da_(csDA) {}

CPDF_DefaultAppearance::CPDF_DefaultAppearance(
    const CPDF_Dictionary* annot_dict,
    const CPDF_Dictionary* acroform_dict)
    : CPDF_DefaultAppearance(
          GetDefaultAppearanceString(annot_dict, acroform_dict)) {}

CPDF_DefaultAppearance::~CPDF_DefaultAppearance() = default;

std::optional<CPDF_DefaultAppearance::FontNameAndSize>
CPDF_DefaultAppearance::GetFont() const {
  if (da_.IsEmpty()) {
    return std::nullopt;
  }

  CPDF_SimpleParser syntax(da_.AsStringView().unsigned_span());
  if (!FindTagParamFromStart(&syntax, "Tf", 2)) {
    return FontNameAndSize();
  }

  // Deliberately using separate statements here to ensure the correct
  // evaluation order.
  FontNameAndSize result;
  result.name = PDF_NameDecode(syntax.GetWord().Substr(1));
  result.size = StringToFloat(syntax.GetWord());
  return result;
}

float CPDF_DefaultAppearance::GetFontSizeOrZero() const {
  auto maybe_font_name_and_size = GetFont();
  if (!maybe_font_name_and_size.has_value()) {
    return 0;
  }
  return maybe_font_name_and_size.value().size;
}

std::optional<CFX_Color> CPDF_DefaultAppearance::GetColor() const {
  if (da_.IsEmpty()) {
    return std::nullopt;
  }

  CPDF_SimpleParser syntax(da_.AsStringView().unsigned_span());
  if (FindTagParamFromStart(&syntax, "g", 1)) {
    float gray = StringToFloat(syntax.GetWord());
    return CFX_Color::MakeGray(gray);
  }
  if (FindTagParamFromStart(&syntax, "rg", 3)) {
    float r = StringToFloat(syntax.GetWord());
    float g = StringToFloat(syntax.GetWord());
    float b = StringToFloat(syntax.GetWord());
    return CFX_Color::MakeRGB(r, g, b);
  }
  if (FindTagParamFromStart(&syntax, "k", 4)) {
    float c = StringToFloat(syntax.GetWord());
    float m = StringToFloat(syntax.GetWord());
    float y = StringToFloat(syntax.GetWord());
    float k = StringToFloat(syntax.GetWord());
    return CFX_Color::MakeCMYK(c, m, y, k);
  }
  return std::nullopt;
}

std::optional<CFX_Color::TypeAndARGB> CPDF_DefaultAppearance::GetColorARGB()
    const {
  std::optional<CFX_Color> maybe_color = GetColor();
  if (!maybe_color.has_value()) {
    return std::nullopt;
  }

  const CFX_Color& color = maybe_color.value();
  if (color.nColorType == CFX_Color::Type::kGray) {
    int g = static_cast<int>(color.fColor1 * 255 + 0.5f);
    return CFX_Color::TypeAndARGB(CFX_Color::Type::kGray,
                                  ArgbEncode(255, g, g, g));
  }
  if (color.nColorType == CFX_Color::Type::kRGB) {
    int r = static_cast<int>(color.fColor1 * 255 + 0.5f);
    int g = static_cast<int>(color.fColor2 * 255 + 0.5f);
    int b = static_cast<int>(color.fColor3 * 255 + 0.5f);
    return CFX_Color::TypeAndARGB(CFX_Color::Type::kRGB,
                                  ArgbEncode(255, r, g, b));
  }
  if (color.nColorType == CFX_Color::Type::kCMYK) {
    float r = 1.0f - std::min(1.0f, color.fColor1 + color.fColor4);
    float g = 1.0f - std::min(1.0f, color.fColor2 + color.fColor4);
    float b = 1.0f - std::min(1.0f, color.fColor3 + color.fColor4);
    return CFX_Color::TypeAndARGB(
        CFX_Color::Type::kCMYK,
        ArgbEncode(255, static_cast<int>(r * 255 + 0.5f),
                   static_cast<int>(g * 255 + 0.5f),
                   static_cast<int>(b * 255 + 0.5f)));
  }
  NOTREACHED();
}

// static
bool CPDF_DefaultAppearance::FindTagParamFromStartForTesting(
    CPDF_SimpleParser* parser,
    ByteStringView token,
    int nParams) {
  return FindTagParamFromStart(parser, token, nParams);
}
