// Copyright (c) 2011 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 "base/logging.h"
#include "chrome/browser/extensions/extension_file_browser_private_api.h"
#include "chrome/browser/extensions/file_manager_util.h"
#include "chrome/browser/metrics/user_metrics.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_dialogs.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/views/html_dialog_view.h"
#include "chrome/browser/ui/views/window.h"
#include "chrome/browser/ui/webui/html_dialog_ui.h"
#include "content/browser/browser_thread.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "views/window/window.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/size.h"

// Shows a dialog box for selecting a file or a folder.
class FileManagerDialog
    : public SelectFileDialog,
      public HtmlDialogUIDelegate {

 public:
  explicit FileManagerDialog(Listener* listener);

  void CreateHtmlDialogView(Profile* profile, void* params) {
    HtmlDialogView* html_view = new HtmlDialogView(profile, this);
    browser::CreateViewsWindow(owner_window_, gfx::Rect(), html_view);
    html_view->InitDialog();
    html_view->window()->Show();
    tab_id_ = html_view->tab_contents()->controller().session_id().id();

    // Register our callback and associate it with our tab.
    FileDialogFunction::Callback::Add(tab_id_, listener_, html_view, params);
  }

  // BaseShellDialog implementation.

  virtual bool IsRunning(gfx::NativeWindow owner_window) const {
    return owner_window_ == owner_window;
  }

  virtual void ListenerDestroyed() {
    listener_ = NULL;
    FileDialogFunction::Callback::Remove(tab_id_);
  }

  // SelectFileDialog implementation.
  virtual void set_browser_mode(bool value) {
    browser_mode_ = value;
  }

  // HtmlDialogUIDelegate implementation.

  virtual bool IsDialogModal() const {
    return true;
  }

  virtual std::wstring GetDialogTitle() const {
    return title_;
  }

  virtual GURL GetDialogContentURL() const {
    return dialog_url_;
  }

  virtual void GetWebUIMessageHandlers(
      std::vector<WebUIMessageHandler*>* handlers) const {
  }

  // Get the size of the dialog.
  virtual void GetDialogSize(gfx::Size* size) const {
    size->set_width(720);
    size->set_height(580);
  }

  virtual std::string GetDialogArgs() const {
    return "";
  }

  // A callback to notify the delegate that the dialog closed.
  virtual void OnDialogClosed(const std::string& json_retval) {
    owner_window_ = NULL;
  }

  virtual void OnWindowClosed() {
    // Directly closing the window selects no files.
    const FileDialogFunction::Callback& callback =
        FileDialogFunction::Callback::Find(tab_id_);
    if (!callback.IsNull())
      callback.listener()->FileSelectionCanceled(callback.params());
  }

  // A callback to notify the delegate that the contents have gone
  // away. Only relevant if your dialog hosts code that calls
  // windows.close() and you've allowed that.  If the output parameter
  // is set to true, then the dialog is closed.  The default is false.
  virtual void OnCloseContents(TabContents* source, bool* out_close_dialog) {
    *out_close_dialog = true;
  }

  // A callback to allow the delegate to dictate that the window should not
  // have a title bar.  This is useful when presenting branded interfaces.
  virtual bool ShouldShowDialogTitle() const {
    return false;
  }

  // A callback to allow the delegate to inhibit context menu or show
  // customized menu.
  virtual bool HandleContextMenu(const ContextMenuParams& params) {
    return true;
  }

 protected:
  // SelectFileDialog implementation.
  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);

 private:
  virtual ~FileManagerDialog() {}

  int32 tab_id_;

  // True when opening in browser, otherwise in OOBE/login mode.
  bool browser_mode_;

  gfx::NativeWindow owner_window_;

  std::wstring title_;

  // Base url plus query string.
  GURL dialog_url_;

  DISALLOW_COPY_AND_ASSIGN(FileManagerDialog);
};

// Linking this implementation of SelectFileDialog::Create into the target
// selects FileManagerDialog as the dialog of choice.
// static
SelectFileDialog* SelectFileDialog::Create(Listener* listener) {
  DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO));
  DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::FILE));
  return new FileManagerDialog(listener);
}

FileManagerDialog::FileManagerDialog(Listener* listener)
    : SelectFileDialog(listener),
      tab_id_(0),
      browser_mode_(true),
      owner_window_(0) {
}

void FileManagerDialog::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 owner_window,
    void* params) {

  if (owner_window_) {
    LOG(ERROR) << "File dialog already in use!";
    return;
  }

  title_ = UTF16ToWide(title);
  owner_window_ = owner_window;

  dialog_url_ = FileManagerUtil::GetFileBrowserUrlWithParams(type, title,
      default_path, file_types, file_type_index, default_extension);

  if (browser_mode_) {
    Browser* browser = BrowserList::GetLastActive();
    if (browser) {
      DCHECK_EQ(browser->type(), Browser::TYPE_NORMAL);
      CreateHtmlDialogView(browser->profile(), params);
      return;
    }
  }

  BrowserThread::PostTask(
      BrowserThread::UI,
      FROM_HERE,
      NewRunnableMethod(this,
                        &FileManagerDialog::CreateHtmlDialogView,
                        ProfileManager::GetDefaultProfile(), params));
}