/*
 * The contents of this file are subject to the Netscape Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/NPL/
 *
 * 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.
 *
 * The Original Code is mozilla.org code.
 *
 * The Initial Developer of the Original Code is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation. All
 * Rights Reserved.
 *
 * Contributor(s): 
 */

top.MAX_RECIPIENTS = 0;

var inputElementType = "";
var selectElementType = "";
var selectElementIndexTable = null;

var test_addresses_sequence = false;
if (prefs)
	try {
		test_addresses_sequence = prefs.GetBoolPref("mail.debug.test_addresses_sequence");
	}
	catch (ex) {}

function awInputElementName()
{
    if (inputElementType == "")
        inputElementType = document.getElementById("msgRecipient#1").localName;
    return inputElementType;
}

function awSelectElementName()
{
    if (selectElementType == "")
        selectElementType = document.getElementById("msgRecipientType#1").localName;
    return selectElementType;
}

function awGetSelectItemIndex(itemData)
{
    if (selectElementIndexTable == null)
    {
	    selectElementIndexTable = new Object();
	    var selectElem = document.getElementById("msgRecipientType#1");
        for (var i = 0; i < selectElem.childNodes[0].childNodes.length; i ++)
    {
            var aData = selectElem.childNodes[0].childNodes[i].getAttribute("data");
            selectElementIndexTable[aData] = i;
        }
    }
    return selectElementIndexTable[itemData];
}

function Recipients2CompFields(msgCompFields)
{
	if (msgCompFields)
	{
		var i = 1;
		var addrTo = "";
		var addrCc = "";
		var addrBcc = "";
		var addrReply = "";
		var addrNg = "";
		var addrFollow = "";
		var addrOther = "";
		var to_Sep = "";
		var cc_Sep = "";
		var bcc_Sep = "";
		var reply_Sep = "";
		var ng_Sep = "";
		var follow_Sep = "";

    var inputField;
	    while ((inputField = awGetInputElement(i)))
	    {
	    	var fieldValue = inputField.value;
	    	if (fieldValue == null)
	    	  fieldValue = inputField.getAttribute("value");

	    	if (fieldValue != "")
	    	{
			    switch (awGetPopupElement(i).selectedItem.getAttribute("data"))
	    		{
	    			case "addr_to"			: addrTo += to_Sep + fieldValue; to_Sep = ",";					break;
	    			case "addr_cc"			: addrCc += cc_Sep + fieldValue; cc_Sep = ",";					break;
	    			case "addr_bcc"			: addrBcc += bcc_Sep + fieldValue; bcc_Sep = ",";				break;
	    			case "addr_reply"		: addrReply += reply_Sep + fieldValue; reply_Sep = ",";			break;
	    			case "addr_newsgroups"	: addrNg += ng_Sep + fieldValue; ng_Sep = ",";					break;
	    			case "addr_followup"	: addrFollow += follow_Sep + fieldValue; follow_Sep = ",";		break;
					case "addr_other"		: addrOther += other_header + ": " + fieldValue + "\n"; 		break;
	    		}
	    	}
	    	i ++;
	    }
    	msgCompFields.SetTo(addrTo);
    	msgCompFields.SetCc(addrCc);
    	msgCompFields.SetBcc(addrBcc);
    	msgCompFields.SetReplyTo(addrReply);
    	msgCompFields.SetNewsgroups(addrNg);
    	msgCompFields.SetFollowupTo(addrFollow);
        msgCompFields.SetOtherRandomHeaders(addrOther);
	}
	else
		dump("Message Compose Error: msgCompFields is null (ExtractRecipients)");
}

function CompFields2Recipients(msgCompFields, msgType)
{
	if (msgCompFields)
	{
	    var treeChildren = document.getElementById('addressWidgetBody');
	    var newTreeChildrenNode = treeChildren.cloneNode(false);
	    var templateNode = treeChildren.firstChild;
		
		top.MAX_RECIPIENTS = 0;

		awSetInputAndPopupFromArray(msgCompFields.SplitRecipients(msgCompFields.GetReplyTo(), false), "addr_reply", newTreeChildrenNode, templateNode);
		awSetInputAndPopupFromArray(msgCompFields.SplitRecipients(msgCompFields.GetTo(), false), "addr_to", newTreeChildrenNode, templateNode);
		awSetInputAndPopupFromArray(msgCompFields.SplitRecipients(msgCompFields.GetCc(), false), "addr_cc", newTreeChildrenNode, templateNode);
		awSetInputAndPopupFromArray(msgCompFields.SplitRecipients(msgCompFields.GetBcc(), false), "addr_bcc", newTreeChildrenNode, templateNode);
		awSetInputAndPopup(msgCompFields.GetOtherRandomHeaders(), "addr_other", newTreeChildrenNode, templateNode);
		awSetInputAndPopup(msgCompFields.GetNewsgroups(), "addr_newsgroups", newTreeChildrenNode, templateNode);
		awSetInputAndPopup(msgCompFields.GetFollowupTo(), "addr_followup", newTreeChildrenNode, templateNode);
		
		//If it's a new message, we need to add an extrat empty recipient.
		var msgComposeType = Components.interfaces.nsIMsgCompType;
		if (msgType == msgComposeType.New || top.MAX_RECIPIENTS == 0)
		    _awSetInputAndPopup("", "addr_to", newTreeChildrenNode, templateNode);
		dump("replacing child in comp fields 2 recips \n");
	    var parent = treeChildren.parentNode;
	    parent.replaceChild(newTreeChildrenNode, treeChildren);
        setTimeout("awFinishCopyNodes();", 0);
	}
}

function awSetInputAndPopupValue(inputElem, inputValue, popupElem, popupValue, rowNumber)
{
	// remove leading spaces
	while (inputValue[0] == " " )
		inputValue = inputValue.substring(1, inputValue.length);
	
  inputElem.setAttribute("value", inputValue);
  inputElem.value = inputValue;

  popupElem.selectedItem = popupElem.childNodes[0].childNodes[awGetSelectItemIndex(popupValue)];
  
  if (rowNumber >= 0)
  {
    inputElem.setAttribute("id", "msgRecipient#" + rowNumber);
  	popupElem.setAttribute("id", "msgRecipientType#" + rowNumber);
	}
  
  _awSetAutoComplete(popupElem, inputElem);
}

function _awSetInputAndPopup(inputValue, popupValue, parentNode, templateNode)
{
    top.MAX_RECIPIENTS++;

    var newNode = templateNode.cloneNode(true);
    parentNode.appendChild(newNode); // we need to insert the new node before we set the value of the select element!

    var input = newNode.getElementsByTagName(awInputElementName());
    var select = newNode.getElementsByTagName(awSelectElementName());

    if (input && input.length == 1 && select && select.length == 1)
      awSetInputAndPopupValue(input[0], inputValue, select[0], popupValue, top.MAX_RECIPIENTS)
}

function awSetInputAndPopup(inputValue, popupValue, parentNode, templateNode)
{
	if ( inputValue && popupValue )
	{
		var addressArray = inputValue.split(",");
		
		for ( var index = 0; index < addressArray.length; index++ )
		    _awSetInputAndPopup(addressArray[index], popupValue, parentNode, templateNode);
	}
}

function awSetInputAndPopupFromArray(inputArray, popupValue, parentNode, templateNode)
{
	if ( inputArray && popupValue )
	{
		for ( var index = 0; index < inputArray.count; index++ )
		    _awSetInputAndPopup(inputArray.StringAt(index), popupValue, parentNode, templateNode);
	}
}

function awRemoveRecipients(msgCompFields, recipientType, recipientsList)
{
  if (!msgCompFields)
    return;

  var recipientArray = msgCompFields.SplitRecipients(recipientsList, false);
  if (! recipientArray)
    return;
  
  for ( var index = 0; index < recipientArray.count; index++ )
    for (var row = 1; row <= top.MAX_RECIPIENTS; row ++)
    {
      var popup = awGetPopupElement(row);
      if (popup.selectedItem.getAttribute("data") == recipientType)
      {
        var input = awGetInputElement(row);
        if (input.value == recipientArray.StringAt(index))
        {
          awSetInputAndPopupValue(input, "", popup, "addr_to", -1);
          break;
        }
      }
    } 
}

function awAddRecipients(msgCompFields, recipientType, recipientsList)
{
  if (!msgCompFields)
    return;

  var recipientArray = msgCompFields.SplitRecipients(recipientsList, false);
  if (! recipientArray)
    return;
  
  for ( var index = 0; index < recipientArray.count; index++ )
  {
    for (var row = 1; row <= top.MAX_RECIPIENTS; row ++)
    {
      if (awGetInputElement(row).value == "")
        break;
    }
    if (row > top.MAX_RECIPIENTS)
      awAppendNewRow(false);
  
    awSetInputAndPopupValue(awGetInputElement(row), recipientArray.StringAt(index), awGetPopupElement(row), recipientType, row);

    /* be sure we still have an empty row left at the end */
    if (row == top.MAX_RECIPIENTS)
    {
      awAppendNewRow(true);
      awSetInputAndPopupValue(awGetInputElement(top.MAX_RECIPIENTS), "", awGetPopupElement(top.MAX_RECIPIENTS), "addr_to", top.MAX_RECIPIENTS);
    }
  }
}

function awTestRowSequence()
{
  /*
    This function is for debug and testing purpose only, normal user should not run it!
    
    Everytime we insert or delete a row, we must be sure we didn't break the ID sequence of
    the addressing widget rows. This function will run a quick test to see if the sequence still ok
    
    You need to define the pref mail.debug.test_addresses_sequence to true in order to activate it
  */
  
	if (! test_addresses_sequence)
	  return true;
 
  /* debug code to verify the sequence still good */
	var body = document.getElementById('addressWidgetBody');
	var treeitems = body.getElementsByTagName('treeitem');
	if (treeitems.length == top.MAX_RECIPIENTS )
	{
	  for (var i = 1; i <= treeitems.length; i ++)
	  {
			var item = treeitems[i - 1];
      var inputID = item.getElementsByTagName(awInputElementName())[0].getAttribute("id").split("#")[1];
      var popupID = item.getElementsByTagName(awSelectElementName())[0].getAttribute("id").split("#")[1];
      if (inputID != i || popupID != i)
      {
	      dump("#ERROR: sequence broken at row " + i + ", inputID=" + inputID + ", popupID=" + popupID + "\n");
	      break;
	    }
		}
    dump("---SEQUENCE OK---\n");
    return true;		
	}
	else
	  dump("#ERROR: treeitems.length(" + treeitems.length + ") != top.MAX_RECIPIENTS(" + top.MAX_RECIPIENTS + ")\n");

	return false;
}

function awCleanupRows()
{
  var maxRecipients = top.MAX_RECIPIENTS;
  var rowID = 1;

  for (var row = 1; row <= maxRecipients; row ++)
  {
    var inputElem = awGetInputElement(row);
    if (inputElem.value == "" && row < maxRecipients)
      awRemoveRow(row);
    else
    {
      inputElem.setAttribute("id", "msgRecipient#" + rowID);
  	  awGetPopupElement(row).setAttribute("id", "msgRecipientType#" + rowID);
      rowID ++;
    }
  }
  
  awTestRowSequence();
}

function awDeleteRow(rowToDelete)
{
  /* When we delete a row, we must reset the id of others row in order to not break the sequence */
  var maxRecipients = top.MAX_RECIPIENTS;
  var rowID = rowToDelete;
  
  awRemoveRow(rowToDelete);

  for (var row = rowToDelete + 1; row <= maxRecipients; row ++)
  {
    awGetInputElement(row).setAttribute("id", "msgRecipient#" + rowID);
  	awGetPopupElement(row).setAttribute("id", "msgRecipientType#" + rowID);
  	rowID ++;
  }

  awTestRowSequence();
}

function awClickEmptySpace(targ, setFocus)
{
  if (targ.localName != 'treechildren')
    return;

	dump("awClickEmptySpace\n");
	var lastInput = awGetInputElement(top.MAX_RECIPIENTS);

	if ( lastInput && lastInput.value )
		awAppendNewRow(setFocus);
	else
		if (setFocus)
			awSetFocus(top.MAX_RECIPIENTS, lastInput);
}

function awReturnHit(inputElement)
{
	var row = awGetRowByInputElement(inputElement);
	
	if ( inputElement.value )
	{
		var nextInput = awGetInputElement(row+1);
		if ( !nextInput )
			awAppendNewRow(true);
		else
			awSetFocus(row+1, nextInput);
	}
}

function awDeleteHit(inputElement)
{
  var row = awGetRowByInputElement(inputElement);
  
  /* 1. don't delete the row if it's the last one remaining, just reset it! */
  if (top.MAX_RECIPIENTS <= 1)
  {
    inputElement.value = "";
    return;
  }

  /* 2. Set the focus to the previous field if possible */
  if (row > 1)
    awSetFocus(row - 1, awGetInputElement(row - 1))
  else
    awSetFocus(1, awGetInputElement(2))   /* We have to cheat a little bit because the focus will */
                                          /* be set asynchronusly after we delete the current row, */
                                          /* therefore the row number still the same! */
                                                
  /* 3. Delete the row */
  awDeleteRow(row);
}

function awInputChanged(inputElement)
{
	dump("awInputChanged\n");
//	AutoCompleteAddress(inputElement);

	//Do we need to add a new row?
	var lastInput = awGetInputElement(top.MAX_RECIPIENTS);
	if ( lastInput && lastInput.value && !top.doNotCreateANewRow)
		awAppendNewRow(false);
	top.doNotCreateANewRow = false;
}

function awAppendNewRow(setFocus)
{
	var body = document.getElementById('addressWidgetBody');
	var treeitem1 = awGetTreeItem(1);
	
	if ( body && treeitem1 )
	{
	    var lastRecipientType = awGetPopupElement(top.MAX_RECIPIENTS).selectedItem.getAttribute("data");

		var newNode = awCopyNode(treeitem1, body, 0);
		top.MAX_RECIPIENTS++;

        var input = newNode.getElementsByTagName(awInputElementName());
        if ( input && input.length == 1 )
        {
   			  input[0].setAttribute("value", "");
    	    input[0].setAttribute("id", "msgRecipient#" + top.MAX_RECIPIENTS);

          // We always clone the first row.  The problem is that the first row
          // could be focused.  When we clone that row, we end up with a cloned
          // XUL textfield that has a focused attribute set.  Therefore we think
          // we're focused and don't properly refocus.  The best solution to this
          // would be to clone a template row that didn't really have any presentation,
          // rather than using the real visible first row of the tree.
          //
          // For now we'll just put in a hack that ensures the focused attribute
          // is never copied when the node is cloned.
          if (input[0].getAttribute('focused') != '')
            input[0].removeAttribute('focused');
    	  }
        var select = newNode.getElementsByTagName(awSelectElementName());
        if ( select && select.length == 1 )
        {
            select[0].selectedItem = select[0].childNodes[0].childNodes[awGetSelectItemIndex(lastRecipientType)];
    	    select[0].setAttribute("id", "msgRecipientType#" + top.MAX_RECIPIENTS);
    	    if (input)
            _awSetAutoComplete(select[0], input[0]);
    	}

		// focus on new input widget
		if (setFocus && input[0] )
			awSetFocus(top.MAX_RECIPIENTS, input[0]);
	}
}


// functions for accessing the elements in the addressing widget

function awGetPopupElement(row)
{
    return document.getElementById("msgRecipientType#" + row);
}

function awGetInputElement(row)
{
    return document.getElementById("msgRecipient#" + row);
}

function awGetTreeRow(row)
{
	var body = document.getElementById('addressWidgetBody');
	
	if ( body && row > 0)
	{
		var treerows = body.getElementsByTagName('treerow');
		if ( treerows && treerows.length >= row )
			return treerows[row-1];
	}
	return 0;
}

function awGetTreeItem(row)
{
	var body = document.getElementById('addressWidgetBody');
	
	if ( body && row > 0)
	{
		var treeitems = body.getElementsByTagName('treeitem');
		if ( treeitems && treeitems.length >= row )
			return treeitems[row-1];
	}
	return 0;
}

function awGetRowByInputElement(inputElement)
{
	if ( inputElement )
	{
		var treerow;
		var inputElementTreerow = inputElement.parentNode.parentNode;
		
		if ( inputElementTreerow )
		{
			for ( var row = 1;  (treerow = awGetTreeRow(row)); row++ )
			{
				if ( treerow == inputElementTreerow )
					return row;
			}
		}
	}
	return 0;
}


// Copy Node - copy this node and insert ahead of the (before) node.  Append to end if before=0
function awCopyNode(node, parentNode, beforeNode)
{
	var newNode = node.cloneNode(true);
	
	if ( beforeNode )
		parentNode.insertBefore(newNode, beforeNode);
	else
		parentNode.appendChild(newNode);

    return newNode;
}

// remove row

function awRemoveRow(row)
{
	var body = document.getElementById('addressWidgetBody');
	
	awRemoveNodeAndChildren(body, awGetTreeItem(row));

	top.MAX_RECIPIENTS --;
}

function awRemoveNodeAndChildren(parent, nodeToRemove)
{
	// children of nodes
	var childNode;
	
	while ( nodeToRemove.childNodes && nodeToRemove.childNodes.length )
	{
		childNode = nodeToRemove.childNodes[0];
	
		awRemoveNodeAndChildren(nodeToRemove, childNode);
	}
	
	parent.removeChild(nodeToRemove);

}

function awSetFocus(row, inputElement)
{
	top.awRow = row;
	top.awInputElement = inputElement;
	top.awFocusRetry = 0;
	setTimeout("_awSetFocus();", 0);
}

function _awSetFocus()
{
	var tree = document.getElementById('addressingWidgetTree');
	try
	{
		var theNewRow = awGetTreeRow(top.awRow);
		//temporary patch for bug 26344
		awFinishCopyNode(theNewRow);

    //Warning: firstVisibleRow is zero base but top.awRow is one base!
    var firstVisibleRow = tree.getIndexOfFirstVisibleRow();
    var numOfVisibleRows = tree.getNumberOfVisibleRows();
  
    //Do we need to scroll in order to see the selected row?
    if (top.awRow <= firstVisibleRow)
      tree.scrollToIndex(top.awRow - 1);
    else
      if (top.awRow - 1 >= (firstVisibleRow + numOfVisibleRows))
        tree.scrollToIndex(top.awRow - numOfVisibleRows);

		top.awInputElement.focus();
	}
	catch(ex)
	{
		top.awFocusRetry ++;
		if (top.awFocusRetry < 3)
		{
			dump("_awSetFocus failed, try it again...\n");
			setTimeout("_awSetFocus();", 0);
		}
		else
			dump("_awSetFocus failed, forget about it!\n");
	}
}


//temporary patch for bug 26344 & 26528
function awFinishCopyNode(node)
{
    msgCompose.ResetNodeEventHandlers(node);
    return;
}


function awFinishCopyNodes()
{
  var treeChildren = document.getElementById('addressWidgetBody');
  awFinishCopyNode(treeChildren);
}

function awTabFromRecipient(element, event)
{
	//If we are le last element in the tree, we don't want to create a new row.
	if (element == awGetInputElement(top.MAX_RECIPIENTS))
		top.doNotCreateANewRow = true;
}

function awGetNumberOfRecipients()
{
    return top.MAX_RECIPIENTS;
}

function DragOverTree(event)
{
	var validFlavor = false;
	var dragSession = null;
	var retVal = true;

	var dragService = Components.classes["@mozilla.org/widget/dragservice;1"].getService();
	if (dragService) 
		dragService = dragService.QueryInterface(Components.interfaces.nsIDragService);
	if (!dragService)	return(false);

	dragSession = dragService.getCurrentSession();
	if (!dragSession)	return(false);

	if (dragSession.isDataFlavorSupported("text/nsabcard"))	validFlavor = true;
	//XXX other flavors here...

	// touch the attribute on the rowgroup to trigger the repaint with the drop feedback.
	if (validFlavor)
	{
		//XXX this is really slow and likes to refresh N times per second.
		var rowGroup = event.target.parentNode.parentNode;
		rowGroup.setAttribute ( "dd-triggerrepaint", 0 );
		dragSession.canDrop = true;
		// necessary??
		retVal = false; // do not propagate message
	}
	return(retVal);
}

function DropOnAddressingWidgetTree(event)
{
	dump("DropOnTree\n");
	var rdf = Components.classes["@mozilla.org/rdf/rdf-service;1"].getService();
	if (rdf)   
		rdf = rdf.QueryInterface(Components.interfaces.nsIRDFService);
	if (!rdf) return(false);

	var dragService = Components.classes["@mozilla.org/widget/dragservice;1"].getService();
	if (dragService) 
		dragService = dragService.QueryInterface(Components.interfaces.nsIDragService);
	if (!dragService)	return(false);
	
	var dragSession = dragService.getCurrentSession();
	if ( !dragSession )	return(false);

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

	for ( var i = 0; i < dragSession.numDropItems; ++i )
	{
		dragSession.getData ( trans, i );
		dataObj = new Object();
		bestFlavor = new Object();
		len = new Object();
		trans.getAnyTransferData ( bestFlavor, dataObj, len );
		if ( dataObj )	dataObj = dataObj.value.QueryInterface(Components.interfaces.nsISupportsWString);
		if ( !dataObj )	continue;

		// pull the URL out of the data object
		var sourceID = dataObj.data.substring(0, len.value);
		if (!sourceID)	continue;

		var cardResource = rdf.GetResource(sourceID);
		var card = cardResource.QueryInterface(Components.interfaces.nsIAbCard);
		var address = "\"" + card.name + "\" <" + card.primaryEmail + ">";
		dump("    Address #" + i + " = " + address + "\n");

		DropRecipient(address); 
		
	}

	return(false);
}

function DropRecipient(recipient) 
{ 
    awClickEmptySpace(true);    //that will automatically set the focus on a new available row, and make sure is visible 
    var lastInput = awGetInputElement(top.MAX_RECIPIENTS); 
    lastInput.value = recipient; 
    awAppendNewRow(true); 
}

function _awSetAutoComplete(selectElem, inputElem)
{
	if (selectElem.data != 'addr_newsgroups' && selectElem.data != 'addr_followup')
		inputElem.disableAutocomplete = false;
	else
    inputElem.disableAutocomplete = true;
}

function awSetAutoComplete(rowNumber)
{
    var inputElem = awGetInputElement(rowNumber);
    var selectElem = awGetPopupElement(rowNumber);
    _awSetAutoComplete(selectElem, inputElem)
}

function awRecipientKeyPress(event, element)
{
  switch(event.keyCode) {
  case 13:
    awReturnHit(element);
    break;
  case 9:
    awTabFromRecipient(element, event);
    break;
  }
}

function awRecipientKeyDown(event, element)
{
  switch(event.keyCode) {
  case 46:
  case 8:
    if (!element.value)
      awDeleteHit(element);
    event.preventBubble();  //We need to stop the event else the tree will receive it and the function
                            //awKeyDown will be executed!
    break;
  }
}

function awKeyDown(event, treeElement)
{
  switch(event.keyCode) {
  case 46:
  case 8:
    /* Warning, the treeElement.selectedItems will change everytime we delete a row */
    var selItems = treeElement.selectedItems;
    var length = treeElement.selectedItems.length;
    for (var i = 1; i <= length; i++) {
      var inputs = treeElement.selectedItems[0].getElementsByTagName(awInputElementName());
      if (inputs && inputs.length == 1)
        awDeleteHit(inputs[0]);
    }
    break;   
  }
}
