/* Copyright (c) Aprotim Sanyal. All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimers
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution. 
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

function SpellCheckChoice(newWord, myParent)
{
   this.word = newWord;
   this.parent = myParent;
   var self = this;

   this.createLink = 
      function ()
      {
         var link = document.createElement('a');
	 link.href = "#";
	 link.onclick = function() { self.parent.replaceWord(self.word, "corrected"); return false;};
         link.appendChild(document.createTextNode(self.word));
	 return link;
      }
}

function SpellCheckWord(newId, newWord, newOptions)
{
   this.id = newId;
   this.word = newWord;
   this.undo = null;
   this.options = newOptions;
   var self = this;

   this.createLinks = 
   function () {
      var links = new Array();
      for (var i = 0; i < self.options.length; ++i)
      {
         var choice = new SpellCheckChoice(self.options[i], self);
         links[i] = choice.createLink();
      }
      return links;
   }

   this.undoReplace =
   function ()
   {
      if (self.undo)
      {
         var currNode = document.getElementById(self.id);
         currNode.parentNode.replaceChild(self.undo, currNode);
      }
      else
      {
         alert('what are you trying to pull?');
      }
   }

   this.editWord = 
   function ()
   {
      if (document.getElementById)
      {
         var oldNode = document.getElementById(self.id);
         var replacementNode = document.createElement('input');
         replacementNode.setAttribute('type', 'text');
         replacementNode.value = self.word;
         replacementNode.onblur = 
            function()
            {
               replacementNode.parentNode.replaceChild(oldNode, replacementNode);
               self.replaceWord(replacementNode.value, 'maybe');
               delete replacementNode;
            };
         oldNode.parentNode.replaceChild(replacementNode, oldNode);
      }
   }
   
   this.makeEditButton = 
   function ()
   {
      var button = document.createElement('input');
      button.setAttribute('type', 'button');
      button.value = 'edit';
      button.onclick = function() { self.editWord () };
      return button;
   }

   this.makeUndoButton = 
   function()
   {
      var undoButton = document.createElement('input');
      undoButton.setAttribute('type','button');
      undoButton.value =  'undo change';
      undoButton.onclick= function() {self.undoReplace()};
      return undoButton;
   }

}

SpellCheckWord.prototype.replaceWord = 
   function (newWord, newclass)
   {
      if (document.getElementById)
      {
         var oldNode = document.getElementById(this.id);
         
         // undo button
         var undoButton = new Array();
         undoButton[0] = this.makeUndoButton();
         
         var replacementNode = createDropdown(newWord, this.id, undoButton, 'spelling ' + newclass);
         this.undo = oldNode;
         oldNode.parentNode.replaceChild(replacementNode, oldNode);
      }
   }

function SpellCheck(textNodeId)
{
   if (!document.getElementById) return;

   this.textNode = document.getElementById(textNodeId);
   if (!this.textNode) throw "No valid textnode specified for SpellCheck object! Tried one with the ID \"" + textNodeId + "\"";

   this.spellDiv = null;
   this.oncheck = null;
   this.onendcheck = null;
   
   var self = this;


   function makeSpellDiv()
   {
      var newDiv = document.createElement('div');
      //newDiv.style.display = "inline";
      //newDiv.style.height = self.textNode.clientHeight+'px';
      newDiv.style.width = self.textNode.clientWidth+'px';
      newDiv.style.border = "1px solid black";
      newDiv.style.margin = self.textNode.style.margin;
      newDiv.style.padding = self.textNode.style.padding;
      newDiv.style.fontFamily = "monospace";
      //newDiv.style.overflow = "auto";
      //newDiv.style.whiteSpace = "pre";
      newDiv.id = self.textNode.id;
      return newDiv;
   }
   
   this.process = function(req)
   {
      if (!req){
         alert("DANGER, WILL ROBINSON!");
         return;
      }

      if(req.readyState != 4) return;

      if (req.status != 200)
      {
         alert('Spellchecking failed!  Sorry.  Received status: '+ req.status + ' ' + req.statusText);
         return;
      }

      self.spellDiv = makeSpellDiv();
      badNodes = req.responseXML.getElementsByTagName('word');

      var currChar = 0;
      for (var i = 0; i < badNodes.length; ++i)
      {
         var wordId = self.spellDiv.id + '_bad' + i; 
         var wordText = badNodes[i].getAttribute('name');
         var offset = parseInt(badNodes[i].getAttribute('offset')) - 1;

         var optionNodes = badNodes[i].getElementsByTagName('choice');
	 
         var options = new Array();
         for (var j = 0; j < optionNodes.length; ++j)
         {
            options.push(optionNodes[j].firstChild.nodeValue);
         }
         var word = new SpellCheckWord (wordId, wordText, options);

         var optionsLinks = word.createLinks();
         optionsLinks.push(word.makeEditButton());

         var dropdown = createDropdown(wordText, wordId, optionsLinks, 'spelling misspelling');

         var preceding_text = self.textNode.value.slice(currChar, offset);
         self.spellDiv.appendChild(document.createTextNode(preceding_text));
         self.spellDiv.appendChild(dropdown);
         currChar = offset + badNodes[i].getAttribute('name').length;
      }
      

      self.spellDiv.appendChild(document.createTextNode(self.textNode.value.slice(currChar)));
      self.textNode.parentNode.replaceChild(self.spellDiv, self.textNode);
      if (self.oncheck) self.oncheck();
   }

   this.request = 
      function ()
      {
	 if (self.spellDiv) return;

	 var req;
         if(window.XMLHttpRequest) {
            try {
               req = new XMLHttpRequest();
            } catch(e) {
               req = false;
            }
         // branch for IE/Windows ActiveX version
         } else if(window.ActiveXObject) {
            try {
               req = new ActiveXObject("Msxml2.XMLHTTP");
            } catch(e) {
               try {
                  req = new ActiveXObject("Microsoft.XMLHTTP");
               } catch(e) {
                  req = false;
               }
            }
         }
   
         if (!req || !document.getElementById)
         {
            alert('Spellchecking failed!  Sorry.');
            return;
         }
         else
         {
            req.onreadystatechange = function() { self.process(req);}
            req.open("GET", "spellcheck.pl?text=" + escape(self.textNode.value), true);
            req.send(null);
         }
      }
}
   
SpellCheck.prototype.end = 
   function()
   {

      this.textNode.value = "";
      if (!this.spellDiv) return;

      for (var i = 0; i< this.spellDiv.childNodes.length; ++i)
      {
	   if (this.spellDiv.childNodes[i].onblur) this.spellDiv.childNodes[i].onblur();
   	   if (this.spellDiv.childNodes[i].nodeType == 3)
	   {
		   this.textNode.value += this.spellDiv.childNodes[i].nodeValue;
	   }
	   else if (this.spellDiv.childNodes[i].firstChild.nodeType == 3)
	   {
		   this.textNode.value += this.spellDiv.childNodes[i].firstChild.nodeValue;
	   }
      }
      this.spellDiv.parentNode.replaceChild(this.textNode, this.spellDiv);
      delete this.spellDiv;
      this.spellDiv = null;
      if (this.onendcheck) this.onendcheck();
   }
