// 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 <algorithm>
#include <iterator>
#include <string>

#include "base/command_line.h"
#include "base/file_path.h"
#include "base/path_service.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "base/memory/scoped_ptr.h"
#include "content/public/browser/browser_thread.h"
#include "chrome/browser/intents/intent_service_host.h"
#include "chrome/browser/intents/native_services.h"
#include "chrome/browser/intents/web_intents_util.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_intents_dispatcher.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/dialogs/select_file_dialog_factory.h"
#include "webkit/glue/web_intent_data.h"
#include "webkit/glue/web_intent_reply_data.h"
#include "webkit/glue/web_intent_service_data.h"

using content::BrowserThread;

namespace {

const std::string kPoodlePath = "/home/poodles/skippy.png";
bool picker_success_mode = true;
const int64 kTestFileSize = 193;

FilePath CreateTestFile() {
  FilePath file;
  PathService::Get(chrome::DIR_TEST_DATA, &file);
  file = file.AppendASCII("web_intents").AppendASCII("test.png");
  return file;
}

class TestIntentsDispatcher : public content::WebIntentsDispatcher {
 public:
  explicit TestIntentsDispatcher(const webkit_glue::WebIntentData& intent)
      : intent_(intent) {}

  virtual const webkit_glue::WebIntentData& GetIntent() OVERRIDE {
    return intent_;
  }

  virtual void DispatchIntent(content::WebContents* web_contents) OVERRIDE {}
  virtual void ResetDispatch() OVERRIDE {}

  virtual void SendReply(const webkit_glue::WebIntentReply& reply) OVERRIDE {
    reply_.reset(new webkit_glue::WebIntentReply(reply));
  }

  virtual void RegisterReplyNotification(
      const base::Callback<void(webkit_glue::WebIntentReplyType)>&) OVERRIDE {
  }

  webkit_glue::WebIntentData intent_;

  scoped_ptr<webkit_glue::WebIntentReply> reply_;
};

// Stub SelectFileDialog designed for testing.
class TestSelectFileDialog : public ui::SelectFileDialog {
 public:
  // Main factory method which returns correct type.
  static ui::SelectFileDialog* Create(
      Listener* listener,
      ui::SelectFilePolicy* policy) {
    return new TestSelectFileDialog(listener, policy);
  }

  // SelectFileDialog implementation.
  virtual bool IsRunning(gfx::NativeWindow parent_window) const OVERRIDE {
    return false;
  }
  virtual void ListenerDestroyed() OVERRIDE {}
  virtual bool HasMultipleFileTypeChoicesImpl() OVERRIDE {
    return false;
  }

 protected:
  TestSelectFileDialog(
      Listener* listener, ui::SelectFilePolicy* policy)
      : ui::SelectFileDialog(listener, policy),
        listener_(listener) {
    test_file_ = CreateTestFile();
  }
  virtual ~TestSelectFileDialog() {}

  // SelectFileDialog implementation.
  // |params| is user data we pass back via the Listener interface.
  virtual void SelectFileImpl(
      Type type,
      const string16& title,
      const FilePath& default_path,
      const FileTypeInfo* file_types,
      int file_type_index,
      const FilePath::StringType& default_extension,
      gfx::NativeWindow owning_window,
      void* params) OVERRIDE {
    if (picker_success_mode)
      listener_->FileSelected(test_file_, kTestFileSize, NULL);
    else
      listener_->FileSelectionCanceled(NULL);
  }

 private:
  // Weak pointer to handler for dialog events.
  Listener* listener_;
  FilePath test_file_;

  DISALLOW_COPY_AND_ASSIGN(TestSelectFileDialog);
};

class TestSelectFileDialogFactory : public ui::SelectFileDialogFactory {
 public:
  virtual ui::SelectFileDialog* Create(
      ui::SelectFileDialog::Listener* listener,
      ui::SelectFilePolicy* policy) {
    return TestSelectFileDialog::Create(listener, policy);
  }
};

}  // namespace

class NativeServicesBrowserTest : public InProcessBrowserTest {
 protected:
  NativeServicesBrowserTest() {}

  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
    // We start the test server now instead of in
    // SetUpInProcessBrowserTestFixture so that we can get its port number.
    ASSERT_TRUE(test_server()->Start());

    command_line->AppendSwitch(switches::kWebIntentsNativeServicesEnabled);
    test_file_ = CreateTestFile();
  }

  virtual void SetUpOnMainThread() OVERRIDE {
    factory_.reset(new TestSelectFileDialogFactory());
    ui::SelectFileDialog::SetFactory(factory_.get());
  }

  virtual Browser* GetBrowser() { return browser(); }
  scoped_ptr<TestSelectFileDialogFactory> factory_;
  FilePath test_file_;
};

IN_PROC_BROWSER_TEST_F(NativeServicesBrowserTest, PickFileSelected) {
  content::WebContents* tab = chrome::GetActiveWebContents(GetBrowser());

  webkit_glue::WebIntentData intent;
  intent.action = ASCIIToUTF16(web_intents::kActionPick);
  intent.type = ASCIIToUTF16("image/*");

  TestIntentsDispatcher dispatcher(intent);
  web_intents::NativeServiceFactory factory;
  GURL url(web_intents::kNativeFilePickerUrl);
  scoped_ptr<web_intents::IntentServiceHost> service(
      factory.CreateServiceInstance(url, intent, tab));
  service->HandleIntent(&dispatcher);

  // Reads of file size are done on the FILE thread, then posted
  // back to the UI thread.
  content::RunAllPendingInMessageLoop(BrowserThread::FILE);
  content::RunAllPendingInMessageLoop(BrowserThread::UI);

  ASSERT_TRUE(dispatcher.reply_);
  EXPECT_EQ(
      webkit_glue::WebIntentReply(
            webkit_glue::WEB_INTENT_REPLY_SUCCESS,
            test_file_,
            kTestFileSize),
      *dispatcher.reply_.get());
}

IN_PROC_BROWSER_TEST_F(NativeServicesBrowserTest, PickFileCancelled) {
  picker_success_mode = false;
  content::WebContents* tab = chrome::GetActiveWebContents(GetBrowser());

  webkit_glue::WebIntentData intent;
  intent.action = ASCIIToUTF16(web_intents::kActionPick);
  intent.type = ASCIIToUTF16("image/*");

  TestIntentsDispatcher dispatcher(intent);
  web_intents::NativeServiceFactory factory;
  GURL url(web_intents::kNativeFilePickerUrl);
  scoped_ptr<web_intents::IntentServiceHost> service(
      factory.CreateServiceInstance(url, intent, tab));
  service->HandleIntent(&dispatcher);

  ASSERT_TRUE(dispatcher.reply_);
  EXPECT_EQ(
      webkit_glue::WebIntentReply(
            webkit_glue::WEB_INTENT_REPLY_FAILURE,
            string16()),
      *dispatcher.reply_.get());
}