// BEGIN FLOCK GPL
//
// Copyright Flock Inc. 2005-2009
// http://flock.com
//
// This file may be used under the terms of the
// GNU General Public License Version 2 or later (the "GPL"),
// http://www.gnu.org/licenses/gpl.html
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
// for the specific language governing rights and limitations under the
// License.
//
// END FLOCK GPL

/*
const RDFS = Components.classes['@mozilla.org/rdf/rdf-service;1'].getService (Components.interfaces.nsIRDFService);

function Namespace (ns) { return function (arg) { return RDFS.GetResource (ns+arg); } }
const W3RDF                     = Namespace ('http://www.w3.org/1999/02/22-rdf-syntax-ns#');
const NSRDF                     = Namespace ('http://home.netscape.com/NC-rdf#');
const WBRDF                     = Namespace ('http://home.netscape.com/WEB-rdf#');
const FLRDF                     = Namespace ('http://flock.com/rdf#');
const COLRDF                    = Namespace ('urn:flock:collection:');
*/
const NS_XHTML = 'http://www.w3.org/1999/xhtml';
const MARK_SELECTION_START = '\u200B\u200B\u200B\u200B\u200B';
const MARK_SELECTION_END = '\u200C\u200C\u200C\u200C\u200C';

var flock_shelf = {};

const shelf_service = Components.classes['@mozilla.org/rdf/datasource;1?name=flock-shelf'].getService(Components.interfaces['flockIShelfService']);
var shelfLogger = Components.classes['@flock.com/logger;1'].createInstance(Components.interfaces['flockILogger']);
shelfLogger.init('shelf');

var faves_coop = Components.classes["@flock.com/singleton;1"]
                           .getService(Components.interfaces.flockISingleton)
                           .getSingleton("chrome://flock/content/common/load-faves-coop.js")
                           .wrappedJSObject;

flock_shelf.getStringBundle = function() {
  var localeService =
    Components.classes["@mozilla.org/intl/nslocaleservice;1"]
              .getService(Components.interfaces.nsILocaleService);
  var stringBundleService =
    Components.classes["@mozilla.org/intl/stringbundle;1"]
              .getService(Components.interfaces.nsIStringBundleService);
  var stringBundle =
    stringBundleService.createBundle( "chrome://flock/locale/shelf/shelf.properties",
                                      localeService.getApplicationLocale() );
  return stringBundle;
}


function getSelection(){
  var focusedWindow = document.commandDispatcher.focusedWindow;
  var winWrapper = new XPCNativeWrapper(focusedWindow, 'document', 'getSelection()');
  return winWrapper.getSelection();
}

function makeRelativeLinksAbsolute(aBaseUrl, aDOMNode) {
  switch (aDOMNode.tagName) {
    case "IMG":
    case "img":
      var url = aDOMNode.getAttribute("src");
      if (!url.match("http"))
        aDOMNode.setAttribute('src', makeURLAbsolute(aBaseUrl, url));
      break;
    case "A":
    case "a":
      var url = aDOMNode.getAttribute("href");
      if (url && !url.match("http")) {
        aDOMNode.setAttribute("href", makeURLAbsolute(aBaseUrl, url));
      }
      break;
    default:
      break
  }
  if (aDOMNode.hasChildNodes()) {
    var kids = aDOMNode.childNodes;
    for (var i = 0; i < kids.length; i++) {
      makeRelativeLinksAbsolute(aBaseUrl, kids[i]);
    }
  }
}

// XXX daryl: Ok, this needs a whole lot of cleaning up. 
// Ripped from viewPartialSource.js in global in core source and 
// hacked a smidgin.
function select_src(selection){
  var range = selection.getRangeAt(0);
  var ancestorContainer = range.commonAncestorContainer;
  var doc = ancestorContainer.ownerDocument;

  var startContainer = range.startContainer;
  var endContainer = range.endContainer;
  var startOffset = range.startOffset;
  var endOffset = range.endOffset;

  // let the ancestor be an element
  if (ancestorContainer.nodeType == Node.TEXT_NODE ||
      ancestorContainer.nodeType == Node.CDATA_SECTION_NODE)
    ancestorContainer = ancestorContainer.parentNode;

  // for selectAll, let's use the entire document, including <html>...</html>
  // @see DocumentViewerImpl::SelectAll() for how selectAll is implemented
  try {
    if (ancestorContainer == doc.body)
      ancestorContainer = doc.documentElement;
  } catch (e) { }

  // each path is a "child sequence" (a.k.a. "tumbler") that
  // descends from the ancestor down to the boundary point
  var startPath = getPath(ancestorContainer, startContainer);
  var endPath = getPath(ancestorContainer, endContainer);

  // clone the fragment of interest and reset everything to be relative to it
  // note: it is with the clone that we operate from now on
  ancestorContainer = ancestorContainer.cloneNode(true);
  startContainer = ancestorContainer;
  endContainer = ancestorContainer;
  var i;
  for (i = startPath ? startPath.length-1 : -1; i >= 0; i--) {
    startContainer = startContainer.childNodes.item(startPath[i]);
  }
  for (i = endPath ? endPath.length-1 : -1; i >= 0; i--) {
    endContainer = endContainer.childNodes.item(endPath[i]);
  }

  // add special markers to record the extent of the selection
  // note: |startOffset| and |endOffset| are interpreted either as
  // offsets in the text data or as child indices (see the Range spec)
  // (here, munging the end point first to keep the start point safe...)
  var tmpNode;
  if (endContainer.nodeType == Node.TEXT_NODE ||
      endContainer.nodeType == Node.CDATA_SECTION_NODE) {
    // do some extra tweaks to try to avoid the view-source output to look like
    // ...<tag>]... or ...]</tag>... (where ']' marks the end of the selection).
    // To get a neat output, the idea here is to remap the end point from:
    // 1. ...<tag>]...   to   ...]<tag>...
    // 2. ...]</tag>...  to   ...</tag>]...
    if ((endOffset > 0 && endOffset < endContainer.data.length) ||
        !endContainer.parentNode || !endContainer.parentNode.parentNode)
      endContainer.insertData(endOffset, MARK_SELECTION_END);
    else {
      tmpNode = doc.createTextNode(MARK_SELECTION_END);
      endContainer = endContainer.parentNode;
      if (endOffset == 0)
        endContainer.parentNode.insertBefore(tmpNode, endContainer);
      else
        endContainer.parentNode.insertBefore(tmpNode, endContainer.nextSibling);
    }
  }
  else {
    tmpNode = doc.createTextNode(MARK_SELECTION_END);
    endContainer.insertBefore(tmpNode, endContainer.childNodes.item(endOffset));
  }

  if (startContainer.nodeType == Node.TEXT_NODE ||
      startContainer.nodeType == Node.CDATA_SECTION_NODE) {
    // do some extra tweaks to try to avoid the view-source output to look like
    // ...<tag>[... or ...[</tag>... (where '[' marks the start of the selection).
    // To get a neat output, the idea here is to remap the start point from:
    // 1. ...<tag>[...   to   ...[<tag>...
    // 2. ...[</tag>...  to   ...</tag>[...
    if ((startOffset > 0 && startOffset < startContainer.data.length) ||
        !startContainer.parentNode || !startContainer.parentNode.parentNode ||
        startContainer != startContainer.parentNode.lastChild)
      startContainer.insertData(startOffset, MARK_SELECTION_START);
    else {
      tmpNode = doc.createTextNode(MARK_SELECTION_START);
      startContainer = startContainer.parentNode;
      if (startOffset == 0)
        startContainer.parentNode.insertBefore(tmpNode, startContainer);
      else
        startContainer.parentNode.insertBefore(tmpNode, startContainer.nextSibling);
    }
  }
  else {
    tmpNode = doc.createTextNode(MARK_SELECTION_START);
    startContainer.insertBefore(tmpNode, startContainer.childNodes.item(startOffset));
  }

  // Make relative links absolute
  makeRelativeLinksAbsolute(doc.location, ancestorContainer);

  // now extract and display the syntax highlighted source
  tmpNode = doc.createElementNS(NS_XHTML, 'div');
  tmpNode.appendChild(ancestorContainer);

  var html = tmpNode.innerHTML;
  var txt = tmpNode.textContent;
  var html_start=html.indexOf(MARK_SELECTION_START) + 5; //Because there are five characters in that const.
  var html_end=html.indexOf(MARK_SELECTION_END);
  html=html.substring(html_start,html_end);
  var txt_start=txt.indexOf(MARK_SELECTION_START) + 5; //Because there are five characters in that const.
  var txt_end=txt.indexOf(MARK_SELECTION_END);
  txt=txt.substring(txt_start,txt_end);
  //dump('START: ' + mystart + '\nEND: ' + myend + '\nVAL: --' + html + '--\n');
  dump('TXT: ' + txt + '\n');
  dump('HTML: ' + html + '\n');
  return { html: html, txt: txt };
}

// XXX daryl: Also ripped from viewPartialSource.js. Probably a good 
// idea to keep this here, in case it changes in the core source.
function getPath(ancestor, node)
{
  var n = node;
  var p = n.parentNode;
  if (n == ancestor || !p)
    return null;
  var path = new Array();
  if (!path)
    return null;
  do {
    for (var i = 0; i < p.childNodes.length; i++) {
      if (p.childNodes.item(i) == n) {
        path.push(i);
        break;
      }
    }
    n = p;
    p = n.parentNode;
  } while (n != ancestor && p);
  return path;
}

function flock_getMarkedUpSelection(aSession){
  var windowManager = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService();
  var windowManagerInterface = windowManager.QueryInterface( Components.interfaces.nsIWindowMediator);
  var topWindow = windowManagerInterface.getMostRecentWindow("navigator:browser");

  var data = "";
  var node = aSession.sourceNode;
  var serializer = new XMLSerializer();
  var parser = new DOMParser();

  var flavourset = new FlavourSet();
  flavourset.appendFlavour("text/x-moz-url");
  flavourset.appendFlavour('text/html');
  flavourset.appendFlavour('text/unicode');

  // For dragging from outside the browser (not supported)
  if (!node) {
    return null;
  }

  var contentElement = topWindow.document.getElementById("content");
  var webPageUrl = contentElement.contentDocument.URL;
  var webPageTitle = contentElement.contentTitle;
  if (webPageTitle.length == 0)
    webPageTitle = contentElement.contentDocument.location;
  if (webPageUrl && webPageUrl.match(/^chrome:\/\/.+/))
    webPageUrl = "";

  var contentDetector = new nsContentDetector();
  contentDetector.setTarget(node);

  // Images
  if (contentDetector.onImage) {
    var imageTitle = node.alt;
    var imageURL = contentDetector.imageURL;
    // Don't insert a stupid image containing Javascript
    if (imageURL.match('javascript:'))
      return null;
    if (imageURL.value == webPageUrl.value) {
      // Bug 3756, when the image alone is displayed in the browser
      imageTitle = imageTitle.split('/').pop();
    }

    var content = '<a href="' + webPageUrl + '" title="' + imageTitle + '">' +
                  '<img border="0" alt="' + imageTitle + '" src="' + imageURL + '"/></a>';
    return {
      type: "image",
      title: imageTitle,
      URL: imageURL,
      content: content
    };
  }
    
  switch(node.nodeName.toLowerCase()) {
  //Handle image maps
/*    case 'area':
    var map='#' + node.parentNode.getAttribute('name');
    var images=topWindow.window._content.document.getElementsByTagName('img');
    dump(images.length + '\n');
    for(i=0; i<images.length; i++){
      dump('IMG: ' + images[i].src + '\n');
      if(images[i].getAttribute('usemap')==map){
        node=images[i];
      }
    }
    if(node.nodeName.toLowerCase()=='area'){
      dump('Could not find image corresponding to map ' + map + '.\n');
      return false;
    }
    //TODO: Normalize paths for node...
    return { type: type, header: header, label: label, src: serializer.serializeToString(node) };*/
  
  // Big hack. This is for when somebody drags the favicon onto the shelf. 
  // 'image' is actually a XUL image (the favicon), while HTML images are 'img'
  case 'image':
    return {
      type: "link",
      title: webPageTitle,
      URL: webPageUrl,
      content: '<a href="' + webPageUrl + '">' + webPageTitle + '</a>'
    };

  // Mediabar photos
  case 'photo':
    header = node.title;
    header = header.replace(/%20/g, ' ');
    label = node.src;
    webPageUrl = node.webPageUrl;
    var photoTitle = node.title.replace(/%20/g, " ");
    return {
      type: "image",
      title: photoTitle,
      URL: node.src,
      content: photoTitle + ": " + node.buildHTML()
    };
  // Browser tabs
  case 'tab':
  case 'xul:tab':
    return {
      type: "link",
      title: node.label,
      URL: window.top.content.document.location.toString(),
      content: '<a href="' + window.top.content.document.location.toString() + '"/>'+ node.label +'</a>'
    };
  case 'shelficon':
  case 'richtreefolder':
    return { dragfrom: node.id };
  default:
    //Handle selections  
    var selection = getSelection();

    if (selection.toString().length > 0) {
      dump ('We get the selection ' + selection + "\n");
      var selection_formatted = select_src(selection);

      data = '<blockquote cite="' + webPageUrl + '">'
           + selection_formatted.html + "</blockquote>"
           + '<p class="citation"><cite cite="' + webPageUrl + '"><a href="'
           + webPageUrl + '">' + title + '</a>' + "</cite></p><br />";
      header = webPageTitle;
      label = selection;
      return {
        type: "document",
        title: webPageTitle,
        URL: webPageUrl,
        content: data
      };
    }
    else {
      if (node.nodeName.toString().toLowerCase()=='#text'){
        node=node.parentNode;
      }

      //If it is an img, make sure the src attribute is the full path
      if (node.src){
        node.setAttribute('src', node.src);
        label = node.src;
        header = node.src;
        if (node.getAttribute('alt')){
          header = node.getAttribute('alt');
        }
        return {
          type: "image",
          title: header,
          URL: node.src,
          content: '<a href="' + webPageUrl + '" title="' + header + '">' +
                   '<img border="0" alt="' + header + '" src="' + node.src + '"/></a>'
        };
      }
      //If it is a link make sure the href attribute is the full path
      if (node.href){
        node.setAttribute('href', node.href);
        var label = node.href;
        var header = node.textContent;
        return {
          type: "link",
          title: label,
          URL: node.href,
          content: serializer.serializeToString(node)
        };
      }
    }
  }

  // sourceNode didn't work, let's try with dragData
  var dragData = nsTransferable.get(flavourset, nsDragAndDrop.getDragData, true).first.first;
  dump ("Flavour: "+dragData.flavour.contentType+"\n");
  switch (dragData.flavour.contentType) {
    case "text/x-moz-url":
      var parts = dragData.data.split("\n");
      return {
        type: "link",
        title: parts[1],
        URL: parts[0],
        content: '<a href="' + parts[0] + '">' + parts[1] + '</a>'
      };
    case "text/unicode":
    case "text/html":
      return {
        type: "document",
        title: dragData.data,
        URL: "",
        content: dragData.data
      };
  }

  // Couldn't find anything
  dump("Couldn't find anything!\n");
  return null;
}


function sendToShelf(event){
  var windowManager = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService();
  var windowManagerInterface = windowManager.QueryInterface( Components.interfaces.nsIWindowMediator);
  var topWindow = windowManagerInterface.getMostRecentWindow("navigator:browser");

  var node = topWindow.document.popupNode;

  var contentDetector = new nsContentDetector();
  contentDetector.setTarget(node);

  var location = top.document
                    .getElementById("content")
                    .mCurrentBrowser
                    .contentWindow
                    .location + "";
  var selection = getSelection();
  var pageTitle = topWindow.document.getElementById('content').contentTitle;

  var type;
  var title;
  var content;
  var url;

  if (contentDetector.onImage) {
    // Send selected image 
    type = "image";
    title = node.alt;
    url = contentDetector.imageURL;
    // Don't insert a stupid image containing Javascript
    if (url.match("javascript:")) {
      return;
    }
    content = <a href={location} title={title}>
                <img border="0" alt={title} src={url}/>
              </a>;
  } else if (contentDetector.onLink) {
    // Send selected link
    type = "link";
    title = node.textContent;
    url = contentDetector.linkURL;
    content = <a href={url}>{title}</a>;
  } else if (selection.toString().length > 0) {
    // Send selected text
    type = "document";
    title = pageTitle;
    url = location;
    content = <div>
                <blockquote cite={title}>{selection}</blockquote>
                <p class="citation">
                  <cite cite={url}>
                    <a href={url}>{title}</a>
                  </cite>
                </p>
              </div>;
  } else {
    // Default: send a link to the current page
    type = "link";
    title = pageTitle;
    url = location;
    content = <a href={url}>{title}</a>;
  }

  shelf_service.insert(title, content.toXMLString(), url, type, -1, null);
  giveClipboardFeedback();
}

function giveClipboardFeedback() {
  // if sidebar closed, give "send to web snippets" feedback via message bar 
  var notesBroadcaster = document.getElementById("flock_notesSidebarBroadcaster");
  if (notesBroadcaster.getAttribute('checked') != "true") {
    var bundle = flock_shelf.getStringBundle();
    var message = bundle.GetStringFromName('flock.shelf.notification.msg');
    // "Snippet saved - click 'View' to see your Web Snippets";
    var notificationBox = gBrowser.getNotificationBox();
    var notification = notificationBox.getNotificationWithValue("snippet-added");
    if (!notification) {
      var buttons = [{
        label: bundle.GetStringFromName('flock.shelf.notification.view'),
        accessKey: bundle.GetStringFromName("flock.shelf.notification.view.accesskey"),
        callback: function () { toggleSidebar('flock_notesSidebarBroadcaster'); }
      }];

      notificationBox.appendNotification(message,
                                         "snippet-added",
                                         null,
                                         notificationBox.FLOCK_PRIORITY_MEDIUM,
                                         buttons);
    }
  }
}

// From browser.js, required for mozextras.js
function makeURLAbsolute( base, url )
{
  try {
    var ioService = Components.classes["@mozilla.org/network/io-service;1"]
                 .getService(Components.interfaces.nsIIOService);
    var baseURI  = ioService.newURI(base, null, null);
 
    return ioService.newURI(baseURI.resolve(url), null, null).spec;
  }
  catch (e) {
    // Special URLs throw exceptions, in this case we just return it "as is"
    return url;
  }
}
