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

#include <string>

#include "base/logging.h"
#include "base/pickle.h"
#include "base/utf_string_conversions.h"
#include "googleurl/src/gurl.h"

static const int kBitsPerByte = 8;

namespace {

void AddTargetToList(GtkTargetList* targets, int target_code) {
  switch (target_code) {
    case gtk_dnd_util::TEXT_PLAIN:
      gtk_target_list_add_text_targets(targets, gtk_dnd_util::TEXT_PLAIN);
      break;

    case gtk_dnd_util::TEXT_URI_LIST:
      gtk_target_list_add_uri_targets(targets, gtk_dnd_util::TEXT_URI_LIST);
      break;

    case gtk_dnd_util::TEXT_HTML:
      gtk_target_list_add(
          targets, gtk_dnd_util::GetAtomForTarget(gtk_dnd_util::TEXT_HTML),
          0, gtk_dnd_util::TEXT_HTML);
      break;

    case gtk_dnd_util::NETSCAPE_URL:
      gtk_target_list_add(targets,
          gtk_dnd_util::GetAtomForTarget(gtk_dnd_util::NETSCAPE_URL),
          0, gtk_dnd_util::NETSCAPE_URL);
      break;

    case gtk_dnd_util::CHROME_TAB:
    case gtk_dnd_util::CHROME_BOOKMARK_ITEM:
    case gtk_dnd_util::CHROME_NAMED_URL:
      gtk_target_list_add(targets, gtk_dnd_util::GetAtomForTarget(target_code),
                          GTK_TARGET_SAME_APP, target_code);
      break;

    case gtk_dnd_util::DIRECT_SAVE_FILE:
      gtk_target_list_add(targets,
          gtk_dnd_util::GetAtomForTarget(gtk_dnd_util::DIRECT_SAVE_FILE),
          0, gtk_dnd_util::DIRECT_SAVE_FILE);
      break;

    default:
      NOTREACHED() << " Unexpected target code: " << target_code;
  }
}

}  // namespace

namespace gtk_dnd_util {

GdkAtom GetAtomForTarget(int target) {
  switch (target) {
    case CHROME_TAB:
      static GdkAtom tab_atom = gdk_atom_intern(
          const_cast<char*>("application/x-chrome-tab"), false);
      return tab_atom;

    case TEXT_HTML:
      static GdkAtom html_atom = gdk_atom_intern(
          const_cast<char*>("text/html"), false);
      return html_atom;

    case CHROME_BOOKMARK_ITEM:
      static GdkAtom bookmark_atom = gdk_atom_intern(
          const_cast<char*>("application/x-chrome-bookmark-item"), false);
      return bookmark_atom;

    case TEXT_PLAIN:
      static GdkAtom text_atom = gdk_atom_intern(
          const_cast<char*>("text/plain;charset=utf-8"), false);
      return text_atom;

    case TEXT_URI_LIST:
      static GdkAtom uris_atom = gdk_atom_intern(
          const_cast<char*>("text/uri-list"), false);
      return uris_atom;

    case CHROME_NAMED_URL:
      static GdkAtom named_url = gdk_atom_intern(
          const_cast<char*>("application/x-chrome-named-url"), false);
      return named_url;

    case NETSCAPE_URL:
      static GdkAtom netscape_url = gdk_atom_intern(
          const_cast<char*>("_NETSCAPE_URL"), false);
      return netscape_url;

    case TEXT_PLAIN_NO_CHARSET:
      static GdkAtom text_no_charset_atom = gdk_atom_intern(
          const_cast<char*>("text/plain"), false);
      return text_no_charset_atom;

    case DIRECT_SAVE_FILE:
      static GdkAtom xds_atom = gdk_atom_intern(
          const_cast<char*>("XdndDirectSave0"), false);
      return xds_atom;

    default:
      NOTREACHED();
  }

  return NULL;
}

GtkTargetList* GetTargetListFromCodeMask(int code_mask) {
  GtkTargetList* targets = gtk_target_list_new(NULL, 0);

  for (size_t i = 1; i < INVALID_TARGET; i = i << 1) {
    if (i == CHROME_WEBDROP_FILE_CONTENTS)
      continue;

    if (i & code_mask)
      AddTargetToList(targets, i);
  }

  return targets;
}

void SetSourceTargetListFromCodeMask(GtkWidget* source, int code_mask) {
  GtkTargetList* targets = GetTargetListFromCodeMask(code_mask);
  gtk_drag_source_set_target_list(source, targets);
  gtk_target_list_unref(targets);
}

void SetDestTargetList(GtkWidget* dest, const int* target_codes) {
  GtkTargetList* targets = gtk_target_list_new(NULL, 0);

  for (size_t i = 0; target_codes[i] != -1; ++i) {
    AddTargetToList(targets, target_codes[i]);
  }

  gtk_drag_dest_set_target_list(dest, targets);
  gtk_target_list_unref(targets);
}

void WriteURLWithName(GtkSelectionData* selection_data,
                      const GURL& url,
                      string16 title,
                      int type) {
  if (title.empty()) {
    // We prefer to not have empty titles. Set it to the filename extracted
    // from the URL.
    title = UTF8ToUTF16(url.ExtractFileName());
  }

  switch (type) {
    case TEXT_PLAIN: {
      gtk_selection_data_set_text(selection_data, url.spec().c_str(),
                                  url.spec().length());
      break;
    }
    case TEXT_URI_LIST: {
      gchar* uri_array[2];
      uri_array[0] = strdup(url.spec().c_str());
      uri_array[1] = NULL;
      gtk_selection_data_set_uris(selection_data, uri_array);
      free(uri_array[0]);
      break;
    }
    case CHROME_NAMED_URL: {
      Pickle pickle;
      pickle.WriteString(UTF16ToUTF8(title));
      pickle.WriteString(url.spec());
      gtk_selection_data_set(
          selection_data,
          GetAtomForTarget(gtk_dnd_util::CHROME_NAMED_URL),
          kBitsPerByte,
          reinterpret_cast<const guchar*>(pickle.data()),
          pickle.size());
      break;
    }
    case NETSCAPE_URL: {
      // _NETSCAPE_URL format is URL + \n + title.
      std::string utf8_text = url.spec() + "\n" + UTF16ToUTF8(title);
      gtk_selection_data_set(selection_data,
                             selection_data->target,
                             kBitsPerByte,
                             reinterpret_cast<const guchar*>(utf8_text.c_str()),
                             utf8_text.length());
      break;
    }

    default: {
      NOTREACHED();
      break;
    }
  }
}

bool ExtractNamedURL(GtkSelectionData* selection_data,
                     GURL* url,
                     string16* title) {
  if (!selection_data || selection_data->length <= 0)
    return false;

  Pickle data(reinterpret_cast<char*>(selection_data->data),
              selection_data->length);
  void* iter = NULL;
  std::string title_utf8, url_utf8;
  if (!data.ReadString(&iter, &title_utf8) ||
      !data.ReadString(&iter, &url_utf8)) {
    return false;
  }

  GURL gurl(url_utf8);
  if (!gurl.is_valid())
    return false;

  *url = gurl;
  *title = UTF8ToUTF16(title_utf8);
  return true;
}

bool ExtractURIList(GtkSelectionData* selection_data, std::vector<GURL>* urls) {
  gchar** uris = gtk_selection_data_get_uris(selection_data);
  if (!uris)
    return false;

  for (size_t i = 0; uris[i] != NULL; ++i) {
    GURL url(uris[i]);
    if (url.is_valid())
      urls->push_back(url);
  }

  g_strfreev(uris);
  return true;
}

bool ExtractNetscapeURL(GtkSelectionData* selection_data,
                        GURL* url,
                        string16* title) {
  if (!selection_data || selection_data->length <= 0)
    return false;

  // Find the first '\n' in the data. It is the separator between the url and
  // the title.
  std::string data(reinterpret_cast<char*>(selection_data->data),
                   selection_data->length);
  std::string::size_type newline = data.find('\n');
  if (newline == std::string::npos)
    return false;

  GURL gurl(data.substr(0, newline));
  if (!gurl.is_valid())
    return false;

  *url = gurl;
  *title = UTF8ToUTF16(data.substr(newline + 1));
  return true;
}

}  // namespace gtk_dnd_util