var noun_type_output_format = new CmdUtils.NounType("output-format", ["text", "otn", "html"]);
const DOCURLBASE = 'https://metalink2.oracle.com/metalink/plsql/showdoc?db=NOT&id=';
const PATCHURLBASE = 'https://updates.oracle.com/ARULink/PatchDetails/process_form?patch_num=';
const SRURLBASE = 'https://metalink2.oracle.com/metalink/plsql/f?p=110:20:::::P20_TARNUM,P20_CTRYCODE:';

/* GetClipboard()
   This code also swiped from http://developer.mozilla.org/en/Using_the_Clipboard
*/

function GetClipboard () {
	var clip = Components.classes["@mozilla.org/widget/clipboard;1"].getService(Components.interfaces.nsIClipboard);
	if (!clip) return false;

	var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
	if (!trans) return false;
	trans.addDataFlavor("text/unicode");
	
	clip.getData(trans, clip.kGlobalClipboard);

	var str       = new Object();
	var strLength = new Object();
        
	trans.getTransferData("text/unicode", str, strLength);
        if (str) str       = str.value.QueryInterface(Components.interfaces.nsISupportsString);  
        return str.data.substring(0, strLength.value / 2);  
}

/* CopyToClipboard(clip: text to copy to clipboard)
 * Shamelessly swiped from http://developer.mozilla.org/en/Using_the_Clipboard 
 * and chopped up to suit my nefarious purposes. I don't care about creating 
 * an HTML version, so I cut that out.  Reduce, reuse, recycle! :-)
 */

function CopyToClipboard (clip) {

   var textUnicode = clip;

   var str = Components.classes["@mozilla.org/supports-string;1"].
                       createInstance(Components.interfaces.nsISupportsString);
   if (!str) return false; // couldn't get string obj
   str.data = textUnicode; // unicode string?

   var trans = Components.classes["@mozilla.org/widget/transferable;1"].
                       createInstance(Components.interfaces.nsITransferable);
   if (!trans) return false; //no transferable widget found

   trans.addDataFlavor("text/unicode");
   trans.setTransferData("text/unicode", str, textUnicode.length * 2); // *2 because it's unicode

   var clipboard = Components.classes["@mozilla.org/widget/clipboard;1"].
                       getService(Components.interfaces.nsIClipboard); 
   if (!clipboard) return false; // couldn't get the clipboard

   clipboard.setData(trans, null, Components.interfaces.nsIClipboard.kGlobalClipboard);
   return true;
}

function GenerateOutputRecord (target, url, label, subj) {
   var outrec = null;
   if (target == "otn") {
      outrec = "[" + subj + "|" + url + "|" + label +"]";
   } else if (target == "html") {
      outrec = "<a href=\"" + url + "\" title=\"" + label +"\">" + subj + "</a>";
   } else if (target == "text") {
      if ( subj != label ) {
         outrec = label + ": " + subj + " (" + url + ")";
      } else {
         outrec = label + " (" + url + ")";
      }
   } else { //Return just the URL, no fancy-schmancy stuff
      outrec = url;
   }

   if (! CopyToClipboard(outrec)) {
      displayMessage("Failed to copy output to clipboard");
   } else {
      var successmsg = target + " style record(s) copied to clipboard for \n" +
                       label + ": \n" + subj;
      displayMessage(successmsg);
   }
}

CmdUtils.CreateCommand({
   name: "metalink-urlify",
   synonynms: ["mlu"],
   icon: "http://www.oracle.com/favicon.ico",
   homepage: "http://only4left.jpiwowar.com/ubiquity-commands",
   author: {name: "John Piwowar", homepage: "http://only4left.jpiwowar.com"},
   license: "MPL,GPL",
   description: "Generate an URL and description for a Metalink Doc ID and copy it to the clipboard, suitable for pasting eleswhere.",
   help: "usage:  metalink-urlify <i>DocID</i> [as <i>format</i>]<br>" + 
         "<i>DocID:</i> Metalink document number of the form nnnnnn.n<br>" +
         "<i>format:</i> [otn|html|text]<p>If format is unspecified, then just an URL is returned.<br>" +
         "This command will attempt to determine the title of the document if it detects an active Metalink session.<p>"+
         "Example outputs for 987654.1 in the various styles:<br>"+
         "<b>otn:</b>  [Controlling orbital lasers with APEX and Oracle Spatial|"+
                       "https://metalink.oracle.com/metalink/plsql/showdoc?db=NOT&id=987654.1|Metalink Note 987654.1]<br>"+
         "<b>html:</b> &lt;a href=\"https://metalink.oracle.com/metalink/plsql/showdoc?db=NOT&id=987654.1\" "+
                       "title=\"Metalink Note 987654.1\"&gt;Controlling orbital lasers with APEX and Oracle Spatial&lt;/a&gt;<br>"+
         "<b>text:</b> Metalink Note 987654.1: Controlling orbital lasers with APEX and Oracle Spatial "+
                       "(https://metalink.oracle.com/metalink/plsql/showdoc?db=NOT&id=987654.1)<br>" +
         "<b>no format specified:</b> https://metalink.oracle.com/metalink/plsql/showdoc?db=NOT&id=987654.1<br>"+
         "<i>Note:</i> In cases where the title of the document cannot be found, the note number will be substituted instead.",
   takes: { metalink_doc_id: noun_arb_text },
   modifiers: { as: noun_type_output_format },

   preview: function(pBlock, inputObj, inputMods) {
     if (! inputMods["as"].text ) {
        var preview_message = "Copy an URL for Metalink Doc ID <i>" + inputObj.text + 
                              "</i> to the clipboard.";
     } else {
        var preview_message = "Generate an URL and description for Metalink DOC ID <i>" + 
                              inputObj.text + "</i> in <b>" + inputMods["as"].text + 
                              "</b> format and copy it to the clipboard";
     }

     pBlock.innerHTML = preview_message;
   },

   execute: function(inputObj, inputMods) {

      function _OutputWarning( warnmsg ){
         displayMessage("Warning: " + warnmsg + " Defaulting to boring output.");
      }

      const MLSTARTURL = "https://metalink2.oracle.com/metalink/plsql/ml2_gui.startup";

      // This ugly URL (noteurl) is the one that  will retrieve the content of the Metalink doc
      // and enable us to get the subject.  Ugh.  Anyone have a shorter one that works?

      var noteurl = 'https://metalink2.oracle.com/metalink/plsql/f?p=130:14:' +
                    '{SESS_ID}::::p14_database_id,p14_docid,p14_show_header,p14_show_help,' +   
                    'p14_black_frame,p14_font:NOT,{NOTEID},1,1,1,helvetica';
      var outputstyle = "URL-only";
      var docid = inputObj.text;
      var docurl = DOCURLBASE + docid;
      var doclabel = "Metalink Note " + docid;
      var docsubj = null;

      if (inputMods["as"].text) {
         outputstyle = inputMods["as"].text;
      } 

      noteurl = noteurl.replace('{NOTEID}',docid);

      // Grab the Metalink start page (not to be confused with the login page) to determine 
      // the session ID needed to build the document URL

      Utils.ajaxGet(MLSTARTURL, function(MLStart) {
         CmdUtils.loadJQuery( function (jQuery) {
         
            //Crude-but-effective heuristic: If you attempt to access the main Metalink page and get 
            //the login page instead, then you aren't connected to Metalink.
         
            var logincheck = jQuery(MLStart).filter(':contains(Login)').text().length;
            var warnmsg = null;

            if (logincheck == 0) {  // Yay, connection to Metalink exists!
               var locurl = jQuery(MLStart).filter(':contains(document.location.href)').text();
               var sess_id  = locurl.match(/\d+:\d+:\d+/)[0].split(':')[2];
               noteurl = noteurl.replace('{SESS_ID}',sess_id);

               Utils.ajaxGet(noteurl, function(MLNote) { //Now grab the note
                  CmdUtils.loadJQuery( function (jQuery) {
                     docsubj = jQuery(MLNote).find('td').filter(':contains(Subject)').siblings().text();
                     if (docsubj.length == 0) {  //Some docs have a different format...
                        docsubj = jQuery(MLNote).find('h1').text();
                     } 
                     if (docsubj.length == 0) { //At this point, I give up. :-)
                        _OutputWarning("Unable to find document subject.");
                        docsubj = doclabel;
                     }
                     GenerateOutputRecord(outputstyle, docurl, doclabel, docsubj);
                  }); 
               }); 

            } else {
               _OutputWarning("No Metalink session found.");
               GenerateOutputRecord(outputstyle, docurl, doclabel, doclabel);
            }            
         }); // MLStart loadJQuery
      }); // MLSTARTURL ajaxGet
   }
})

CmdUtils.CreateCommand({
   name: "metalink-open",
   synonynms: ["mlo"],
   icon: "http://www.oracle.com/favicon.ico",
   homepage: "http://only4left.jpiwowar.com/ubiquity-commands",
   author: {name: "John Piwowar", homepage: "http://only4left.jpiwowar.com"},
   license: "MPL,GPL",
   description: "Search a highlighted block of text, or the clipboard, for Metalink Note IDs, patch numbers, or SR numbers, and open them.",
   help: "usage:  metalink-open [this]<br>" +
         "Will pull data from the clipboard if no selection detected in browser, or if<br>" +
         "user does not supply an object for the command.<br>"+
         "Note: Does not check for a valid Metalink session.  If you aren't connected<br>" +
         "to Metalink, be prepared to log in and refresh a few tabs.",
   takes: { selection: noun_arb_text },

   preview: function(pBlock, inputObj) {
      var scanwhat = "supplied string";
      if (inputObj.text.length > 0 && CmdUtils.getSelection().length > 0) {
         scanwhat = "selected text";
      } else if (inputObj.text.length == 0) {
         scanwhat = "clipboard";
      }

      var preview_message = "Open Metalink documents for numbers found in the " + scanwhat +".";
     
      pBlock.innerHTML = preview_message;
   },

   execute: function(inputObj, inputMods) {

      //Utils.openUrlInBrowser doesn't do what I want in the current version of Ubiquity, 
      //so I'm borrowing code from the patch until it does. 
      //reference: http://hg.toolness.com/ubiquity-firefox/rev/86e832a2e14d

      function _OpenUrl(url) {
          var windowManager = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService(Components.interfaces.nsIWindowMediator);
          var browserWindow = windowManager.getMostRecentWindow("navigator:browser");
          var browser = browserWindow.getBrowser();
          if (browser.mCurrentBrowser.currentURI.spec == "about:blank" && !browser.webProgress.isLoadingDocument) {
             browserWindow.loadURI(url, null, null, false);
          } else {
             browser.loadOneTab(url, null, null, null, false, false); 
          } 
      }
      var text2scan = null;
      if (inputObj.text.length > 0) {
         text2scan=inputObj.text;
      } else {
         displayMessage("Reading from clipboard...");
         text2scan=GetClipboard();
      }
      
      var doclist=text2scan.match(/\d{6,8}(\.\d{1,3})*/g); // Get the Metalinky-looking numbers
      
      //Crude but mostly effective guessing follows...
      for (num in doclist) {
         if (doclist[num].match(/\d{7}\.\d{3}/) || doclist[num].match(/\d{8}\.\d{1}/)) { //Looks like an SR...
            _OpenUrl(SRURLBASE+doclist[num]);
         } else if (doclist[num].match(/\d{7}/)) { //Probably a patch number
            _OpenUrl(PATCHURLBASE+doclist[num]);
         } else if (doclist[num].match(/\d{6}\.\d/)) { // Probably a Metalink Note
            _OpenUrl(DOCURLBASE+doclist[num]);
         } else if (doclist[num].length == 6) { //Probably a Note ID w/o the trailing .1
            _OpenUrl(DOCURLBASE+doclist[num]+'.1');
         } else {
            displayMessage(doclist[num] + ' is oh-so-inscrutable.');
         }
      }
      
   } 
}) 