// 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/ui/webui/instant_ui.h"

#include "base/bind.h"
#include "base/prefs/pref_service.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_instant_controller.h"
#include "chrome/browser/ui/search/instant_controller.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "components/user_prefs/pref_registry_syncable.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
#include "content/public/browser/web_ui_message_handler.h"
#include "grit/browser_resources.h"

namespace {

content::WebUIDataSource* CreateInstantHTMLSource() {
  content::WebUIDataSource* source =
      content::WebUIDataSource::Create(chrome::kChromeUIInstantHost);
  source->SetJsonPath("strings.js");
  source->AddResourcePath("instant.js", IDR_INSTANT_JS);
  source->AddResourcePath("instant.css", IDR_INSTANT_CSS);
  source->SetDefaultResource(IDR_INSTANT_HTML);
  return source;
}

#if !defined(OS_ANDROID)
std::string FormatTime(int64 time) {
  base::Time::Exploded exploded;
  base::Time::FromInternalValue(time).UTCExplode(&exploded);
  return base::StringPrintf("%04d-%02d-%02d %02d:%02d:%02d.%03d",
      exploded.year, exploded.month, exploded.day_of_month,
      exploded.hour, exploded.minute, exploded.second, exploded.millisecond);
}
#endif  // !defined(OS_ANDROID)

// This class receives JavaScript messages from the renderer.
// Note that the WebUI infrastructure runs on the UI thread, therefore all of
// this class's methods are expected to run on the UI thread.
class InstantUIMessageHandler
    : public content::WebUIMessageHandler,
      public base::SupportsWeakPtr<InstantUIMessageHandler> {
 public:
  InstantUIMessageHandler();
  virtual ~InstantUIMessageHandler();

  // WebUIMessageHandler implementation.
  virtual void RegisterMessages() OVERRIDE;

 private:
  void GetPreferenceValue(const base::ListValue* args);
  void SetPreferenceValue(const base::ListValue* args);
  void GetDebugInfo(const base::ListValue* value);
  void ClearDebugInfo(const base::ListValue* value);

  DISALLOW_COPY_AND_ASSIGN(InstantUIMessageHandler);
};

InstantUIMessageHandler::InstantUIMessageHandler() {}

InstantUIMessageHandler::~InstantUIMessageHandler() {}

void InstantUIMessageHandler::RegisterMessages() {
  web_ui()->RegisterMessageCallback(
      "getPreferenceValue",
      base::Bind(&InstantUIMessageHandler::GetPreferenceValue,
                 base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "setPreferenceValue",
      base::Bind(&InstantUIMessageHandler::SetPreferenceValue,
                 base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "getDebugInfo",
      base::Bind(&InstantUIMessageHandler::GetDebugInfo,
                 base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "clearDebugInfo",
      base::Bind(&InstantUIMessageHandler::ClearDebugInfo,
                 base::Unretained(this)));
}

void InstantUIMessageHandler::GetPreferenceValue(const base::ListValue* args) {
  std::string pref_name;
  if (!args->GetString(0, &pref_name)) return;

  base::StringValue pref_name_value(pref_name);
  if (pref_name == prefs::kInstantUIZeroSuggestUrlPrefix) {
    PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
    base::StringValue arg(prefs->GetString(pref_name.c_str()));
    web_ui()->CallJavascriptFunction(
        "instantConfig.getPreferenceValueResult", pref_name_value, arg);
  }
}

void InstantUIMessageHandler::SetPreferenceValue(const base::ListValue* args) {
  std::string pref_name;
  if (!args->GetString(0, &pref_name)) return;

  if (pref_name == prefs::kInstantUIZeroSuggestUrlPrefix) {
    std::string value;
    if (!args->GetString(1, &value))
      return;
    PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
    prefs->SetString(pref_name.c_str(), value);
  }
}

void InstantUIMessageHandler::GetDebugInfo(const base::ListValue* args) {
#if !defined(OS_ANDROID)
  typedef std::pair<int64, std::string> DebugEvent;

  if (!web_ui()->GetWebContents())
    return;
  Browser* browser = chrome::FindBrowserWithWebContents(
      web_ui()->GetWebContents());
  if (!browser || !browser->instant_controller())
    return;

  InstantController* instant = browser->instant_controller()->instant();
  const std::list<DebugEvent>& events = instant->debug_events();

  base::DictionaryValue data;
  base::ListValue* entries = new base::ListValue();
  for (std::list<DebugEvent>::const_iterator it = events.begin();
       it != events.end(); ++it) {
    base::DictionaryValue* entry = new base::DictionaryValue();
    entry->SetString("time", FormatTime(it->first));
    entry->SetString("text", it->second);
    entries->Append(entry);
  }
  data.Set("entries", entries);

  web_ui()->CallJavascriptFunction("instantConfig.getDebugInfoResult", data);
#endif
}

void InstantUIMessageHandler::ClearDebugInfo(const base::ListValue* args) {
#if !defined(OS_ANDROID)
  if (!web_ui()->GetWebContents())
    return;
  Browser* browser = chrome::FindBrowserWithWebContents(
      web_ui()->GetWebContents());
  if (!browser || !browser->instant_controller())
    return;

  browser->instant_controller()->instant()->ClearDebugEvents();
#endif
}

}  // namespace

////////////////////////////////////////////////////////////////////////////////
// InstantUI

InstantUI::InstantUI(content::WebUI* web_ui) : WebUIController(web_ui) {
  web_ui->AddMessageHandler(new InstantUIMessageHandler());

  // Set up the chrome://instant/ source.
  Profile* profile = Profile::FromWebUI(web_ui);
  content::WebUIDataSource::Add(profile, CreateInstantHTMLSource());
}

// static
void InstantUI::RegisterProfilePrefs(
    user_prefs::PrefRegistrySyncable* registry) {
  registry->RegisterStringPref(
      prefs::kInstantUIZeroSuggestUrlPrefix,
      std::string(),
      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
}