// Copyright 2015 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 "chrome/browser/extensions/api/instance_id/instance_id_api.h"

#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/services/gcm/instance_id/instance_id_profile_service.h"
#include "chrome/browser/services/gcm/instance_id/instance_id_profile_service_factory.h"
#include "chrome/common/extensions/api/instance_id.h"
#include "components/gcm_driver/instance_id/instance_id_driver.h"
#include "extensions/common/extension.h"

namespace extensions {

namespace {

// Error messages.
const char kInvalidParameter[] = "Function was called with invalid parameters.";
const char kDisabled[] = "Instance ID is currently disabled.";
const char kAsyncOperationPending[] = "Asynchronous operation is pending.";
const char kNetworkError[] = "Network error occurred.";
const char kServerError[] = "Server error occurred.";
const char kUnknownError[] = "Unknown error occurred.";

const char* InstanceIDResultToError(instance_id::InstanceID::Result result) {
  switch (result) {
    case instance_id::InstanceID::INVALID_PARAMETER:
      return kInvalidParameter;
    case instance_id::InstanceID::DISABLED:
      return kDisabled;
    case instance_id::InstanceID::ASYNC_OPERATION_PENDING:
      return kAsyncOperationPending;
    case instance_id::InstanceID::NETWORK_ERROR:
      return kNetworkError;
    case instance_id::InstanceID::SERVER_ERROR:
      return kServerError;
    case instance_id::InstanceID::UNKNOWN_ERROR:
      return kUnknownError;
    default:
       NOTREACHED() << "Unexpected value of result cannot be converted: "
                    << result;
  }
  return "";
}

}  // namespace

InstanceIDApiFunction::InstanceIDApiFunction() {
}

InstanceIDApiFunction::~InstanceIDApiFunction() {
}

ExtensionFunction::ResponseAction InstanceIDApiFunction::Run() {
  if (Profile::FromBrowserContext(browser_context())->IsOffTheRecord()) {
    return RespondNow(Error(
        "chrome.instanceID not supported in incognito mode"));
  }

  bool isInstanceIDEnabled = IsEnabled();
  UMA_HISTOGRAM_BOOLEAN("InstanceID.Enabled", isInstanceIDEnabled);

  if (!isInstanceIDEnabled) {
    return RespondNow(Error(
        InstanceIDResultToError(instance_id::InstanceID::DISABLED)));
  }

  return DoWork();
}

bool InstanceIDApiFunction::IsEnabled() const {
  Profile* profile = Profile::FromBrowserContext(browser_context());

  return instance_id::InstanceIDProfileService::IsInstanceIDEnabled(profile);
}

instance_id::InstanceID* InstanceIDApiFunction::GetInstanceID() const {
  return instance_id::InstanceIDProfileServiceFactory::GetForProfile(
      Profile::FromBrowserContext(browser_context()))->driver()->
          GetInstanceID(extension()->id());
}

InstanceIDGetIDFunction::InstanceIDGetIDFunction() {}

InstanceIDGetIDFunction::~InstanceIDGetIDFunction() {}

ExtensionFunction::ResponseAction InstanceIDGetIDFunction::DoWork() {
  GetInstanceID()->GetID(
      base::Bind(&InstanceIDGetIDFunction::GetIDCompleted, this));
  return RespondLater();
}

void InstanceIDGetIDFunction::GetIDCompleted(const std::string& id) {
  Respond(OneArgument(new base::StringValue(id)));
}

InstanceIDGetCreationTimeFunction::InstanceIDGetCreationTimeFunction() {}

InstanceIDGetCreationTimeFunction::~InstanceIDGetCreationTimeFunction() {}

ExtensionFunction::ResponseAction InstanceIDGetCreationTimeFunction::DoWork() {
  GetInstanceID()->GetCreationTime(
      base::Bind(&InstanceIDGetCreationTimeFunction::GetCreationTimeCompleted,
                 this));
  return RespondLater();
}

void InstanceIDGetCreationTimeFunction::GetCreationTimeCompleted(
    const base::Time& creation_time) {
  Respond(OneArgument(new base::FundamentalValue(creation_time.ToDoubleT())));
}

InstanceIDGetTokenFunction::InstanceIDGetTokenFunction() {}

InstanceIDGetTokenFunction::~InstanceIDGetTokenFunction() {}

ExtensionFunction::ResponseAction InstanceIDGetTokenFunction::DoWork() {
  scoped_ptr<api::instance_id::GetToken::Params> params =
      api::instance_id::GetToken::Params::Create(*args_);
  EXTENSION_FUNCTION_VALIDATE(params.get());

  std::map<std::string, std::string> options;
  if (params->get_token_params.options.get())
    options = params->get_token_params.options->additional_properties;

  GetInstanceID()->GetToken(
      params->get_token_params.authorized_entity,
      params->get_token_params.scope,
      options,
      base::Bind(&InstanceIDGetTokenFunction::GetTokenCompleted, this));

  return RespondLater();
}

void InstanceIDGetTokenFunction::GetTokenCompleted(
    const std::string& token,
    instance_id::InstanceID::Result result) {
  if (result == instance_id::InstanceID::SUCCESS)
    Respond(OneArgument(new base::StringValue(token)));
  else
    Respond(Error(InstanceIDResultToError(result)));
}

InstanceIDDeleteTokenFunction::InstanceIDDeleteTokenFunction() {}

InstanceIDDeleteTokenFunction::~InstanceIDDeleteTokenFunction() {}

ExtensionFunction::ResponseAction InstanceIDDeleteTokenFunction::DoWork() {
  scoped_ptr<api::instance_id::DeleteToken::Params> params =
      api::instance_id::DeleteToken::Params::Create(*args_);
  EXTENSION_FUNCTION_VALIDATE(params.get());

  GetInstanceID()->DeleteToken(
      params->delete_token_params.authorized_entity,
      params->delete_token_params.scope,
      base::Bind(&InstanceIDDeleteTokenFunction::DeleteTokenCompleted, this));

  return RespondLater();
}

void InstanceIDDeleteTokenFunction::DeleteTokenCompleted(
    instance_id::InstanceID::Result result) {
  if (result == instance_id::InstanceID::SUCCESS)
    Respond(NoArguments());
  else
    Respond(Error(InstanceIDResultToError(result)));
}

InstanceIDDeleteIDFunction::InstanceIDDeleteIDFunction() {}

InstanceIDDeleteIDFunction::~InstanceIDDeleteIDFunction() {}

ExtensionFunction::ResponseAction InstanceIDDeleteIDFunction::DoWork() {
  GetInstanceID()->DeleteID(
      base::Bind(&InstanceIDDeleteIDFunction::DeleteIDCompleted, this));

  return RespondLater();
}

void InstanceIDDeleteIDFunction::DeleteIDCompleted(
    instance_id::InstanceID::Result result) {
  if (result == instance_id::InstanceID::SUCCESS)
    Respond(NoArguments());
  else
    Respond(Error(InstanceIDResultToError(result)));
}

}  // namespace extensions