// Copyright (c) 2012 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/app/app_api.h"

#include "base/json/json_writer.h"
#include "base/string_number_conversions.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/extensions/app_notification_manager.h"
#include "chrome/browser/extensions/extension_event_router.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_constants.h"
#include "content/public/browser/notification_service.h"
#include "googleurl/src/gurl.h"
#include "webkit/glue/web_intent_data.h"

namespace {

const char kBodyTextKey[] = "bodyText";
const char kExtensionIdKey[] = "extensionId";
const char kLinkTextKey[] = "linkText";
const char kLinkUrlKey[] = "linkUrl";
const char kTitleKey[] = "title";

const char kInvalidExtensionIdError[] =
    "Invalid extension id";
const char kMissingLinkTextError[] =
    "You must specify linkText if you use linkUrl";
const char kOnLaunchedEvent[] = "experimental.app.onLaunched";

}  // anonymous namespace

namespace extensions {

bool AppNotifyFunction::RunImpl() {
  if (!include_incognito() && profile_->IsOffTheRecord()) {
    error_ = extension_misc::kAppNotificationsIncognitoError;
    return false;
  }

  DictionaryValue* details;
  EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &details));
  EXTENSION_FUNCTION_VALIDATE(details != NULL);

  // TODO(asargent) remove this before the API leaves experimental.
  std::string id = extension_id();
  if (details->HasKey(kExtensionIdKey)) {
    EXTENSION_FUNCTION_VALIDATE(details->GetString(kExtensionIdKey, &id));
    if (!profile()->GetExtensionService()->GetExtensionById(id, true)) {
      error_ = kInvalidExtensionIdError;
      return false;
    }
  }

  std::string title;
  if (details->HasKey(kTitleKey))
    EXTENSION_FUNCTION_VALIDATE(details->GetString(kTitleKey, &title));

  std::string body;
  if (details->HasKey(kBodyTextKey))
    EXTENSION_FUNCTION_VALIDATE(details->GetString(kBodyTextKey, &body));

  scoped_ptr<AppNotification> item(new AppNotification(
      true, base::Time::Now(), "", id, title, body));

  if (details->HasKey(kLinkUrlKey)) {
    std::string link_url;
    EXTENSION_FUNCTION_VALIDATE(details->GetString(kLinkUrlKey, &link_url));
    item->set_link_url(GURL(link_url));
    if (!item->link_url().is_valid()) {
      error_ = "Invalid url: " + link_url;
      return false;
    }
    if (!details->HasKey(kLinkTextKey)) {
      error_ = kMissingLinkTextError;
      return false;
    }
    std::string link_text;
    EXTENSION_FUNCTION_VALIDATE(details->GetString(kLinkTextKey,
                                                   &link_text));
    item->set_link_text(link_text);
  }

  AppNotificationManager* manager =
      profile()->GetExtensionService()->app_notification_manager();

  // TODO(beaudoin) We should probably report an error if Add returns false.
  manager->Add(item.release());

  return true;
}

bool AppClearAllNotificationsFunction::RunImpl() {
  if (!include_incognito() && profile_->IsOffTheRecord()) {
    error_ = extension_misc::kAppNotificationsIncognitoError;
    return false;
  }

  std::string id = extension_id();
  DictionaryValue* details = NULL;
  if (args_->GetDictionary(0, &details) && details->HasKey(kExtensionIdKey)) {
    EXTENSION_FUNCTION_VALIDATE(details->GetString(kExtensionIdKey, &id));
    if (!profile()->GetExtensionService()->GetExtensionById(id, true)) {
      error_ = kInvalidExtensionIdError;
      return false;
    }
  }

  AppNotificationManager* manager =
      profile()->GetExtensionService()->app_notification_manager();
  manager->ClearAll(id);
  return true;
}

// static.
void AppEventRouter::DispatchOnLaunchedEvent(
    Profile* profile, const Extension* extension) {
  // TODO(benwells): remove this logging.
  LOG(ERROR) << "Dispatching onLaunched with no data.";
  profile->GetExtensionEventRouter()->DispatchEventToExtension(
      extension->id(), kOnLaunchedEvent, "[]", NULL, GURL());
}

// static.
void AppEventRouter::DispatchOnLaunchedEventWithFileEntry(
    Profile* profile, const Extension* extension, const string16& action,
    const std::string& file_system_id, const FilePath& base_name) {
  // TODO(benwells): remove this logging.
  LOG(ERROR) << "Dispatching onLaunched with file entry.";
  ListValue args;
  DictionaryValue* launch_data = new DictionaryValue();
  DictionaryValue* intent = new DictionaryValue();
  intent->SetString("action", UTF16ToUTF8(action));
  intent->SetString("type", "chrome-extension://fileentry");
  launch_data->Set("intent", intent);
  args.Append(launch_data);
  DictionaryValue* intent_data = new DictionaryValue();
  intent_data->SetString("format", "fileEntry");
  intent_data->SetString("fileSystemId", file_system_id);
  intent_data->SetString("baseName", base_name.value());
  args.Append(intent_data);
  std::string json_args;
  base::JSONWriter::Write(&args, &json_args);
  profile->GetExtensionEventRouter()->DispatchEventToExtension(
      extension->id(), kOnLaunchedEvent, json_args, NULL, GURL());
}

// static.
void AppEventRouter::DispatchOnLaunchedEventWithWebIntent(
    Profile* profile, const Extension* extension,
    const webkit_glue::WebIntentData web_intent_data) {
  // TODO(benwells): remove this logging.
  LOG(ERROR) << "Dispatching onLaunched with WebIntent.";
  ListValue args;
  DictionaryValue* launch_data = new DictionaryValue();
  DictionaryValue* intent = new DictionaryValue();
  intent->SetString("action", UTF16ToUTF8(web_intent_data.action));
  intent->SetString("type", UTF16ToUTF8(web_intent_data.type));
  launch_data->Set("intent", intent);
  args.Append(launch_data);
  DictionaryValue* intent_data;
  switch (web_intent_data.data_type) {
    case webkit_glue::WebIntentData::SERIALIZED:
      intent_data = new DictionaryValue();
      intent_data->SetString("format", "serialized");
      intent_data->SetString("data", UTF16ToUTF8(web_intent_data.data));
      args.Append(intent_data);
      break;
    case webkit_glue::WebIntentData::UNSERIALIZED:
      intent->SetString("data", UTF16ToUTF8(web_intent_data.unserialized_data));
      break;
    case webkit_glue::WebIntentData::BLOB:
      intent_data = new DictionaryValue();
      intent_data->SetString("format", "blob");
      intent_data->SetString("blobFileName", web_intent_data.blob_file.value());
      intent_data->SetString("blobLength",
                             base::Int64ToString(web_intent_data.blob_length));
      args.Append(intent_data);
      break;
    default:
      NOTREACHED();
      break;
  }
  std::string json_args;
  base::JSONWriter::Write(&args, &json_args);
  profile->GetExtensionEventRouter()->DispatchEventToExtension(
      extension->id(), kOnLaunchedEvent, json_args, NULL, GURL());
}

}  // namespace extensions