// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ppapi/proxy/pdf_resource.h"

#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include "base/command_line.h"
#include "base/debug/crash_logging.h"
#include "base/metrics/histogram.h"
#include "base/strings/utf_string_conversions.h"
#include "gin/v8_initializer.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/c/private/ppb_pdf.h"
#include "ppapi/proxy/plugin_globals.h"
#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/shared_impl/var.h"
#include "third_party/icu/source/i18n/unicode/usearch.h"

namespace ppapi {
namespace proxy {

namespace {

// TODO(raymes): This is just copied from render_thread_impl.cc. We should have
// generic code somewhere to get the locale in the plugin.
std::string GetLocale() {
  // The browser process should have passed the locale to the plugin via the
  // --lang command line flag.
  const base::CommandLine& parsed_command_line =
      *base::CommandLine::ForCurrentProcess();
  const std::string& lang = parsed_command_line.GetSwitchValueASCII("lang");
  DCHECK(!lang.empty());
  return lang;
}

}  // namespace

PDFResource::PDFResource(Connection connection, PP_Instance instance)
    : PluginResource(connection, instance) {
  SendCreate(RENDERER, PpapiHostMsg_PDF_Create());
}

PDFResource::~PDFResource() {
}

thunk::PPB_PDF_API* PDFResource::AsPPB_PDF_API() {
  return this;
}

void PDFResource::SearchString(const unsigned short* input_string,
                               const unsigned short* input_term,
                               bool case_sensitive,
                               PP_PrivateFindResult** results,
                               uint32_t* count) {
  if (locale_.empty())
    locale_ = GetLocale();
  const UChar* string =
      reinterpret_cast<const UChar*>(input_string);
  const UChar* term =
      reinterpret_cast<const UChar*>(input_term);

  UErrorCode status = U_ZERO_ERROR;
  UStringSearch* searcher = usearch_open(term, -1, string, -1, locale_.c_str(),
                                         0, &status);
  DCHECK(status == U_ZERO_ERROR || status == U_USING_FALLBACK_WARNING ||
         status == U_USING_DEFAULT_WARNING)
      << status;
  UCollationStrength strength = case_sensitive ? UCOL_TERTIARY : UCOL_PRIMARY;

  UCollator* collator = usearch_getCollator(searcher);
  if (ucol_getStrength(collator) != strength) {
    ucol_setStrength(collator, strength);
    usearch_reset(searcher);
  }

  status = U_ZERO_ERROR;
  int match_start = usearch_first(searcher, &status);
  DCHECK_EQ(U_ZERO_ERROR, status);

  std::vector<PP_PrivateFindResult> pp_results;
  while (match_start != USEARCH_DONE) {
    int32_t matched_length = usearch_getMatchedLength(searcher);
    PP_PrivateFindResult result;
    result.start_index = match_start;
    result.length = matched_length;
    pp_results.push_back(result);
    match_start = usearch_next(searcher, &status);
    DCHECK_EQ(U_ZERO_ERROR, status);
  }

  if (pp_results.empty() ||
      pp_results.size() > std::numeric_limits<uint32_t>::max() ||
      pp_results.size() > SIZE_MAX / sizeof(PP_PrivateFindResult)) {
    *count = 0;
    *results = nullptr;
  } else {
    *count = static_cast<uint32_t>(pp_results.size());
    const size_t result_size = pp_results.size() * sizeof(PP_PrivateFindResult);
    *results = reinterpret_cast<PP_PrivateFindResult*>(malloc(result_size));
    memcpy(*results, &pp_results[0], result_size);
  }

  usearch_close(searcher);
}

void PDFResource::DidStartLoading() {
  Post(RENDERER, PpapiHostMsg_PDF_DidStartLoading());
}

void PDFResource::DidStopLoading() {
  Post(RENDERER, PpapiHostMsg_PDF_DidStopLoading());
}

void PDFResource::SetContentRestriction(int restrictions) {
  Post(RENDERER, PpapiHostMsg_PDF_SetContentRestriction(restrictions));
}

void PDFResource::UserMetricsRecordAction(const PP_Var& action) {
  scoped_refptr<ppapi::StringVar> action_str(
      ppapi::StringVar::FromPPVar(action));
  if (action_str.get()) {
    Post(RENDERER,
         PpapiHostMsg_PDF_UserMetricsRecordAction(action_str->value()));
  }
}

void PDFResource::HasUnsupportedFeature() {
  Post(RENDERER, PpapiHostMsg_PDF_HasUnsupportedFeature());
}

void PDFResource::Print() {
  Post(RENDERER, PpapiHostMsg_PDF_Print());
}

void PDFResource::ShowAlertDialog(const char* message) {
  SyncCall<PpapiPluginMsg_PDF_ShowAlertDialogReply>(
      RENDERER, PpapiHostMsg_PDF_ShowAlertDialog(message));
}

bool PDFResource::ShowConfirmDialog(const char* message) {
  bool bool_result = false;
  if (SyncCall<PpapiPluginMsg_PDF_ShowConfirmDialogReply>(
          RENDERER, PpapiHostMsg_PDF_ShowConfirmDialog(message),
          &bool_result) != PP_OK) {
    return false;
  }
  return bool_result;
}

PP_Var PDFResource::ShowPromptDialog(const char* message,
                                     const char* default_answer) {
  std::string str_result;
  if (SyncCall<PpapiPluginMsg_PDF_ShowPromptDialogReply>(
          RENDERER, PpapiHostMsg_PDF_ShowPromptDialog(message, default_answer),
          &str_result) != PP_OK) {
    return PP_MakeUndefined();
  }
  return StringVar::StringToPPVar(str_result);
}

void PDFResource::SaveAs() {
  Post(RENDERER, PpapiHostMsg_PDF_SaveAs());
}

PP_Bool PDFResource::IsFeatureEnabled(PP_PDFFeature feature) {
  PP_Bool result = PP_FALSE;
  switch (feature) {
    case PP_PDFFEATURE_HIDPI:
      result = PP_TRUE;
      break;
    case PP_PDFFEATURE_PRINTING:
      // TODO(raymes): Use PrintRenderFrameHelper::IsPrintingEnabled.
      result = PP_FALSE;
      break;
  }
  return result;
}

void PDFResource::SetSelectedText(const char* selected_text) {
  Post(RENDERER,
       PpapiHostMsg_PDF_SetSelectedText(base::UTF8ToUTF16(selected_text)));
}

void PDFResource::SetLinkUnderCursor(const char* url) {
  Post(RENDERER, PpapiHostMsg_PDF_SetLinkUnderCursor(url));
}

void PDFResource::GetV8ExternalSnapshotData(const char** natives_data_out,
                                            int* natives_size_out,
                                            const char** snapshot_data_out,
                                            int* snapshot_size_out) {
  gin::V8Initializer::GetV8ExternalSnapshotData(
      natives_data_out, natives_size_out, snapshot_data_out, snapshot_size_out);
}

void PDFResource::SetAccessibilityDocInfo(
    PP_PrivateAccessibilityDocInfo* doc_info) {
  Post(RENDERER, PpapiHostMsg_PDF_SetAccessibilityDocInfo(*doc_info));
}

void PDFResource::SetAccessibilityViewportInfo(
    PP_PrivateAccessibilityViewportInfo* viewport_info) {
  Post(RENDERER, PpapiHostMsg_PDF_SetAccessibilityViewportInfo(*viewport_info));
}

void PDFResource::SetAccessibilityPageInfo(
    PP_PrivateAccessibilityPageInfo* page_info,
    PP_PrivateAccessibilityTextRunInfo text_runs[],
    PP_PrivateAccessibilityCharInfo chars[]) {
  std::vector<PP_PrivateAccessibilityTextRunInfo> text_run_vector(
      text_runs, text_runs + page_info->text_run_count);
  std::vector<PP_PrivateAccessibilityCharInfo> char_vector(
      chars, chars + page_info->char_count);
  Post(RENDERER, PpapiHostMsg_PDF_SetAccessibilityPageInfo(
                     *page_info, text_run_vector, char_vector));
}

void PDFResource::SetCrashData(const char* pdf_url, const char* top_level_url) {
  if (pdf_url) {
    static base::debug::CrashKeyString* subresource_url =
        base::debug::AllocateCrashKeyString("subresource_url",
                                            base::debug::CrashKeySize::Size256);
    base::debug::SetCrashKeyString(subresource_url, pdf_url);
  }
  if (top_level_url)
    PluginGlobals::Get()->SetActiveURL(top_level_url);
}

void PDFResource::SelectionChanged(const PP_FloatPoint& left,
                                   int32_t left_height,
                                   const PP_FloatPoint& right,
                                   int32_t right_height) {
  Post(RENDERER, PpapiHostMsg_PDF_SelectionChanged(left, left_height, right,
                                                   right_height));
}

}  // namespace proxy
}  // namespace ppapi
