// 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 <stdlib.h>
#include <string.h>

#include "base/command_line.h"
#include "base/metrics/histogram.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/c/private/ppb_pdf.h"
#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/proxy/ppb_image_data_proxy.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 CommandLine& parsed_command_line = *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;
}

PP_Var PDFResource::GetLocalizedString(PP_ResourceString string_id) {
  std::string localized_string;
  int32_t result = SyncCall<PpapiPluginMsg_PDF_GetLocalizedStringReply>(
      RENDERER, PpapiHostMsg_PDF_GetLocalizedString(string_id),
      &localized_string);
  if (result != PP_OK)
    return PP_MakeUndefined();
  return ppapi::StringVar::StringToPPVar(localized_string);
}

void PDFResource::SearchString(const unsigned short* input_string,
                               const unsigned short* input_term,
                               bool case_sensitive,
                               PP_PrivateFindResult** results, int* count) {
  if (locale_.empty())
    locale_ = GetLocale();
  const char16* string = reinterpret_cast<const char16*>(input_string);
  const char16* term = reinterpret_cast<const char16*>(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);
  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(status == U_ZERO_ERROR);

  std::vector<PP_PrivateFindResult> pp_results;
  while (match_start != USEARCH_DONE) {
    size_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(status == U_ZERO_ERROR);
  }

  *count = pp_results.size();
  if (*count) {
    *results = reinterpret_cast<PP_PrivateFindResult*>(malloc(
        *count * sizeof(PP_PrivateFindResult)));
    memcpy(*results, &pp_results[0], *count * sizeof(PP_PrivateFindResult));
  } else {
    *results = NULL;
  }

  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::HistogramPDFPageCount(int count) {
  UMA_HISTOGRAM_COUNTS_10000("PDF.PageCount", count);
}

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::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 PrintWebViewHelper::IsPrintingEnabled.
      result = PP_FALSE;
      break;
  }
  return result;
}

PP_Resource PDFResource::GetResourceImageForScale(PP_ResourceImage image_id,
                                                  float scale) {
  IPC::Message reply;
  ResourceMessageReplyParams reply_params;
  int32_t result = GenericSyncCall(
      RENDERER, PpapiHostMsg_PDF_GetResourceImage(image_id, scale), &reply,
      &reply_params);
  if (result != PP_OK)
    return 0;

  HostResource resource;
  PP_ImageDataDesc image_desc;
  if (!UnpackMessage<PpapiPluginMsg_PDF_GetResourceImageReply>(
      reply, &resource, &image_desc)) {
    return 0;
  }

  if (resource.is_null())
    return 0;
  if (!PPB_ImageData_Shared::IsImageDataDescValid(image_desc))
    return 0;

  base::SharedMemoryHandle handle;
  if (!reply_params.TakeSharedMemoryHandleAtIndex(0, &handle))
    return 0;
  return (new SimpleImageData(resource, image_desc, handle))->GetReference();
}

PP_Resource PDFResource::GetResourceImage(PP_ResourceImage image_id) {
  return GetResourceImageForScale(image_id, 1.0f);
}

PP_Bool PDFResource::IsOutOfProcess() {
  return PP_TRUE;
}

}  // namespace proxy
}  // namespace ppapi