// 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

Components.utils.import("resource:///modules/FlockFlickrStrings.jsm");
Components.utils.import("resource:///modules/FlockStringBundleHelpers.jsm");

const EDITOR_STYLE = "font-size: 0.8em; " + 
                     "font-family: 'Lucida Sans', 'Lucida Sans Unicode', " +
                     "'Lucida Grande', sans-serif, Arial;";

var gViewSource = false;
var gOverrideParagraphBehavior = false;
var gB;

function editorWidget(aEditorDeck, aEditor) {
  var inst = this;

  // XUL elements
  this.mEditorDeck = aEditorDeck;
  this.mEditor = aEditor;
  this.mNSHTMLEditor = null;
  this.mNSEditor = null;

  // State variable
  this.viewSource = false; // true if the current view is "source code"

  // Initialize
  this.mEditor.contentWindow.document.designMode = "on";
  this.mEditor.contentWindow.document.execCommand("undo", false, null);
  this.mEditor.contentWindow.document.body.setAttribute("style", EDITOR_STYLE);

  // Preview pane style and script
  var previewDoc = document.getElementById("previewpane").contentWindow.document;
  var head = previewDoc.documentElement.getElementsByTagName("head")[0];
  var cssNode = previewDoc.createElement("link");
  cssNode.type = "text/css";
  cssNode.rel = "stylesheet";
  cssNode.href = "chrome://flock/content/blog/preview.css";
  cssNode.media = "screen";
  head.appendChild(cssNode);
  var scriptNode = previewDoc.createElement("script");
  scriptNode.type = "application/x-javascript";
  scriptNode.src = "chrome://flock/content/blog/preview.js";
  head.appendChild(scriptNode);

  if (document.getElementById("forecolor")) {
    // TODO: get rid of that hardcoded call
    document.getElementById("forecolor").color = "#000000";
  }

  this.mNSEditor = this.mEditor.boxObject.docShell.QueryInterface(Components.interfaces.nsIEditorDocShell);
  this.mNSHTMLEditor = this.mNSEditor.editor.QueryInterface(Components.interfaces.nsIHTMLEditor);
  this.mNSHTMLEditor.QueryInterface(Components.interfaces.nsIEditorStyleSheets).addStyleSheet("chrome://browser/content/flock/shelf/editor.css");
  this.mNSHTMLEditor.returnInParagraphCreatesNewParagraph = true;
  this.mNSHTMLEditor.endOfDocument();

  // To upload a picture by dropping in the editor  
  this.mNSHTMLEditor.addInsertionListener(InsertionListener);
}

var InsertionListener = {
  notifyOfInsertion:
  function notifyOfInsertion(mimeType, contentSourceURL, sourceDocument,
                             willDeleteSelection, aDocFragment,
                             contentStartNode, contentStartOffset,
                             contentEndNode, contentEndOffset,
                             aInsertionPointNode, aInsertionPointOffset,
                             continueWithInsertion) {
    try {
      this.mInsertionPointNode = aInsertionPointNode.value;
      this.mInsertionPointOffset = aInsertionPointOffset.value;

      var spaceball = FlockFlickrStrings.getAsRegExp("spaceballUrlPattern");

      var fragment = aDocFragment.value;
      var src = null;

      // Check if this is a desktop image, if yes trigger the uploader
      if (fragment.firstChild.nodeName == "IMG") {
        src = fragment.firstChild.getAttribute("src");
      } else if (fragment.firstChild.nodeName == "A") {
        // Bug 8004: BMP images appear as links
        src = fragment.firstChild.getAttribute("href");
      }
      if (src && src.match(/^file:\/\//) && FlockMedia.isAPhoto(src)) {
        this.mSrc = src;
        window.setTimeout(InsertionListener.tryInsert, 0);
        return;
      }

      // Check if this is a Flickr spaceball, if yes fix it
      if (fragment.firstChild.innerHTML &&
          fragment.firstChild.innerHTML.match(spaceball))
      {
        var node = document.createElement("img");
        var src = FlockFlickrStrings.getImageUrlFromDoc(sourceDocument);
        node.setAttribute("src", src);
        var editor = gEditor.mNSEditor
                            .editor
                            .QueryInterface(Components.interfaces.nsIEditor);
        editor.insertNode(node,
                          aInsertionPointNode.value,
                          aInsertionPointOffset.value);
        // Hack to force the editor to refresh its display
        gEditor.setContent(gEditor.getXHtml());
        return;
      }

      // No special case, just handle that like a standard drop
      continueWithInsertion.value = true;
    } catch (ex) {
      // Something went wrong, fall back to standard drop
      continueWithInsertion.value = true;
    }
  },
  tryInsert: function () {
    var url = InsertionListener.mSrc;
    var rval = { value: "" }
    if (!FlockMedia.getUploadSvc().hasOnePhotoAccount()) {
      var strbundle = document.getElementById("editorStringBundle");
      var title = strbundle.getString("flock.blog.photo.title");
      var brand = brandGetString("brand", "brandShortName");
      var msg = strbundle.getFormattedString("flock.blog.photo.desc", [brand]);
      var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
                                .getService(Components.interfaces.nsIPromptService);
      promptService.alert(window, title, msg);
      return;
    }
    
    var photoUploadObserver = {
      onMediaImportStart: function(aFilename, aCount) {
      },

      onMediaImportFinish: function(aUpload, aCount) {
        UploadUIUtils.launchSinglePhotoUpload(aUpload,
                                              document.getElementById("edit"));
      },

      onMediaImportError: function (aFilename) {
        var strbundle = document.getElementById("editorStringBundle");
        var msg   = strbundle.getFormatterString("flock.blog.photo.error",[aFilename]);
        alert(msg);
      }
    }
    FlockMedia.getUploadSvc().importSingleMediaItem(photoUploadObserver, url);
  }
}

editorWidget.prototype = {
  // Switch tab
  viewsource: function (aSource) {
    var html;
    if (aSource) {
      this.mEditorDeck.selectedIndex = 1;
    } else {
      document.getElementById("editordeck").selectedIndex = 0;
    }
    if (this.viewSource == aSource)
      return;
    if (aSource) {
      document.getElementById("editsource").value = this.getXHtml();
      document.getElementById("editsource").focus();
    } else {
      document.getElementById("shelfEditorToolbar").removeAttribute("hidden");
      html = document.getElementById("editsource").value;
      if (html == "") {
        html = "<br/>";
      }
      this.setContent(html);
      document.getElementById("edit").contentWindow.focus();
    }
    this.viewSource = aSource;
  },

  seepreview: function () {
    var html = (this.viewSource) ? this.source2xhtml(document.getElementById("editsource").value) : this.mEditor.contentWindow.document.body.innerHTML;
    this.setContent(html);
    var previewDoc = document.getElementById("previewpane").contentWindow.document;
    var body = previewDoc.documentElement.getElementsByTagName("body")[0];
    body.innerHTML = html;
    body.setAttribute("onclick", "previewClick(event);");
    this.mEditorDeck.selectedIndex = 2;
  },

  execCommand: function (action) {
    var strbundle = document.getElementById("editorStringBundle");
    var win = this.mEditor.contentWindow;
    var doc = win.document;
    var sel = win.getSelection();
    switch (action){
      case "link":
        this.link();
      case "insertimage":
        this.insertImage();
        break;
      case "formatblock":
        this.mEditor
            .contentWindow
            .document
            .execCommand(action,
                         false,
                         "<" + document.getElementById("formatblock").value
                             + ">");
        break;
      case "fontsize":
        this.mEditor
            .contentWindow
            .document
            .execCommand(action,
                         false,
                         document.getElementById("fontsize").value);
        break;
      case "fontname":
        this.mEditor
            .contentWindow
            .document
            .execCommand(action,
                         false,
                         document.getElementById("fontname").value);
        break;
      case "forecolor":
        this.mEditor
            .contentWindow
            .document
            .execCommand(action,
                         false,
                         document.getElementById("forecolor").color);
        break;
      default:
        this.mEditor.contentWindow.document.execCommand(action, false, null);
    }
  },

  linkEnabled: function() {
    var sel = this.mEditor.contentWindow.getSelection();
    var node = this.mNSHTMLEditor.selection.getRangeAt(0).cloneContents().firstChild;
    var anchorElement = this.mNSHTMLEditor.getElementOrParentByTagName("href", this.mNSHTMLEditor.selection.anchorNode);

    // Text is selection
    if (sel != "") {
      return true;
    }

    if (node) {
      // An image is selected
      if (node.nodeName == "IMG") {
        return true;
      }
    }

    // No selection, but the cursor is on a link
    if (anchorElement) {
      return true;
    }

    return false;
  },

  insertImage: function() {
    var params = {
      URL: "http://",
      title: "",
      align: "",
      alt: "",
      cancelled: false
    };
    window.openDialog("chrome://flock/content/blog/imageProperties.xul",
                      "_blank", "modal,centerscreen", params);
    if (!params.cancelled) {
      var style;
      switch (params.align) {
        case "left":
          style = "margin: 10px 10px 0 0; float: left;";
          break;
        case "right":
          style = "margin: 0 0 10px 10px; float: right;";
          break;
        case "center":
          style = "margin: 0 auto 10px; display: block; text-align: center;";
          break;
        default:
          style="";
      }
      this.mNSHTMLEditor.insertHTML('<img src="'+params.URL+'" style="'+style+'" title="'+params.title+'" alt="'+params.alt+'"/>');
    }
  },

  link: function() {
    var url = "http://";
    ///////////////////////////////////////////////////////////////////////
    // NOTE: Use "href" instead of "a" to distinguish from Named Anchor
    // The returned node is has an "a" tagName
    var tagName = "href";
    // See if we have a single selected image
    var imageElement = this.mNSHTMLEditor.getSelectedElement("img");

    // Get an anchor element if caret or
    //   entire selection is within the link.
    anchorElement = this.mNSHTMLEditor.getSelectedElement(tagName);

    if (anchorElement) {
      // Select the entire link
      this.mNSHTMLEditor.selectElement(anchorElement);
    } else {
      // If selection starts in a link, but extends beyond it,
      //   the user probably wants to extend existing link to new selection,
      //   so check if either end of selection is within a link
      // POTENTIAL PROBLEM: This prevents user from selecting text in an existing
      //   link and making 2 links. 
      // Note that this isn't a problem with images, handled above
  
      anchorElement = this.mNSHTMLEditor.getElementOrParentByTagName("href", this.mNSHTMLEditor.selection.anchorNode);
      if (!anchorElement) {
        anchorElement = this.mNSHTMLEditor.getElementOrParentByTagName("href", this.mNSHTMLEditor.selection.focusNode);
      }

      if (anchorElement) {
        // But clone it for reinserting/merging around existing
        //   link that only partially overlaps the selection
        anchorElement = anchorElement.cloneNode(false);
        //insertNew = true;
        replaceExistingLink = true;
      }
    }

    var selectedText;
    if (!imageElement) {
      // We get here if selection is exactly around a link node
      var node = this.mNSHTMLEditor.selection.getRangeAt(0).cloneContents().firstChild;
      if (node.nodeName == "A") {
        selectedText  = node.getAttribute("HREF");
      }
      if ((!selectedText) && (anchorElement)) {
        // No text, look for first image in the selection
        var children = anchorElement.childNodes;
        if (children) {
          for (var i = 0; i < children.length; i++) {
            var nodeName = children.item(i).nodeName.toLowerCase();
            if (nodeName == "img") {
              imageElement = children.item(i);
              break;
            }
          }
        }
      }
    }

    // Set "caption" for link source and the source text or image URL
    if (imageElement) {
      if (imageElement.parentNode.nodeName == "A") {
        url = imageElement.parentNode.href;
      }
    } else if (selectedText) {
      url = selectedText;
    }

    var strbundle = document.getElementById("editorStringBundle");
    var szURL = prompt(strbundle.getString("flock.blog.loading.enterURL"), url);
    if ((szURL != null) && (szURL != "")) {
      this.mEditor.contentWindow.document.execCommand("CreateLink", false, szURL);
    }
    return;
  },

  unlinkEnabled: function () {
    var sel = document.getElementById("edit").contentWindow.getSelection();
    var node = this.mNSHTMLEditor.selection.getRangeAt(0).cloneContents().firstChild;
    var anchorElement = this.mNSHTMLEditor.getElementOrParentByTagName("href", this.mNSHTMLEditor.selection.anchorNode);

    if (sel != "") {
      return true;
    }

    if (node) {
      if (node.nodeName == "IMG") {
        return true;
      }
    }

    // No selection, but the cursor is on a link
    if (anchorElement) {
      return true;
    }

    return false;
  },

  source2xhtml: function (aContent) {
    var result = aContent
    if (!result || result == "") {
      result = "<br />";
    }
    result = result.replace(/(\r)?\n/gi, "<br/>");
    result = result.replace(/<br( \/)?>/gi, "<br/>");

    // XHTML
    result = result.replace(/<(br|hr|img)([^>]*)([^\/])>/gi, "<$1$2$3 />");
    result = result.replace(/<(br|hr|img)([^>]*)  \/>/gi, "<$1$2 />");
    result = result.replace(/<(br|hr|img)([^>]*)"\/>/gi, "<$1$2\" />");
    result = result.replace(/<br>/gi, "<br />");
 
    return result;  
  },

  setContent: function (aContent) {
    aContent = this.source2xhtml(aContent);
    this.mEditor.contentWindow.document.body.innerHTML = aContent;
    this.mEditor.contentWindow.document.body.setAttribute("style",
                                                          EDITOR_STYLE);
  },

  getXHtml: function () {
    var text = this.mEditor.contentWindow.document.body.innerHTML;

    // Replace 0 or 1 newlines followed by a BR followed by 0 or 1 newline
    // with a formatted BR.
    text = text.replace(/<br([^>]*)>[\r\f]?\n/gi, "<br$1 />");
    text = text.replace(/[\r\f]\n? ?/gi, " ");
    text = text.replace(/\n ?/gi, " ");

    // Remove all BRs at the end of the string.
    text = text.replace(/<br( \/)?>$/gi, "");

    // Replace any new BRs entered in the message tab with newlines.
    text = text.replace(/<br( \/)?>/gi,"\n");

    // XHTML
    text = text.replace(/<(br|hr|img)([^>]*)([^\/])>/gi, "<$1$2$3 />");
    text = text.replace(/<(br|hr|img)([^>]*)  \/>/gi, "<$1$2 />");
    text = text.replace(/<(br|hr|img)([^>]*)"\/>/gi, "<$1$2\" />");
    text = text.replace(/<br>/gi, "<br />");
    return text;
  },
  
  uninit: function() {
    if (InsertionListener && this.mNSHTMLEditor)
      this.mNSHTMLEditor.removeInsertionListener(InsertionListener);
  }
}
