/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * 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 Communicator client code, released
 * March 31, 1998.
 * 
 * The Initial Developer of the Original Code is Netscape
 * Communications Corporation. Portions created by Netscape are
 * Copyright (C) 1998-1999 Netscape Communications Corporation. All
 * Rights Reserved.
 */

/*  This file contains the js functions necessary to implement view navigation within the 3 pane. */

//NOTE: gMessengerBundle must be defined and set or this Overlay won't work

// These are the types of navigation you can do
var navigateAny=0;
var navigateUnread = 1;
var navigateFlagged = 2;
var navigateNew = 3;

var commonDialogs = Components.classes["@mozilla.org/appshell/commonDialogs;1"].getService();
commonDialogs = commonDialogs.QueryInterface(Components.interfaces.nsICommonDialogs);
var accountManager = Components.classes["@mozilla.org/messenger/account-manager;1"].getService(Components.interfaces.nsIMsgAccountManager);


function FindNextFolder(originalFolderURI)
{
    if (!originalFolderURI) return null;

    var originalFolderResource = RDF.GetResource(originalFolderURI);
    var folder = originalFolderResource.QueryInterface(Components.interfaces.nsIFolder);
    if (!folder) return null;
    dump("folder = " + folder.URI + "\n");

    try {
       var subFolderEnumerator = folder.GetSubFolders();
       var done = false;
       while (!done) {
         var element = subFolderEnumerator.currentItem();
         var currentSubFolder = element.QueryInterface(Components.interfaces.nsIMsgFolder);
         dump("current folder = " + currentSubFolder.URI + "\n");
         if (currentSubFolder.getNumUnread(false /* don't descend */) > 0) {
           dump("if the child has unread, use it.\n");
           return currentSubFolder.URI;
         }
         else if (currentSubFolder.getNumUnread(true /* descend */) > 0) {
           dump("if the child doesn't have any unread, but it's children do, recurse\n");
           return FindNextFolder(currentSubFolder.URI);
         }

         try {
           subFolderEnumerator.next();
         } 
         catch (ex) {
           done=true;
         }
      } // while
    }
    catch (ex) {
        // one way to get here is if the folder has no sub folders
    }
 
    if (folder.parent && folder.parent.URI) {
      dump("parent = " + folder.parent.URI + "\n");
      return FindNextFolder(folder.parent.URI);
    }
    else {
      dump("no parent\n");
    }
    return null;
}

/*GoNextMessage finds the message that matches criteria and selects it.  
  nextFunction is the function that will be used to detertime if a message matches criteria.
  It must take a node and return a boolean.
  nextResourceFunction is the function that will be used to determine if a message in the form of a resource
  matches criteria.  Takes a resource and returns a boolean
  nextThreadFunction is an optional function that can be used to optimize whether or not a thread will have a
  message that matches the criteria.  Takes the top level message in the form of a node and returns a boolean.
  startFromBeginning is a boolean that states whether or not we should start looking at the beginning
  if we reach the end 
*/
function GoNextMessage(type, startFromBeginning )
{
	var beforeGoNextMessage;
    if (showPerformance) {
	    beforeGoNextMessage = new Date();
    }

	var tree = GetThreadTree();
	
	var selArray = tree.selectedItems;
	var length = selArray.length;

	if ( selArray && ((length == 0) || (length == 1)) )
	{
		var currentMessage;

		if(length == 0)
			currentMessage = null;
		else
			currentMessage = selArray[0];

		var nextMessage = msgNavigationService.FindNextMessage(type, tree, currentMessage, RDF, document, startFromBeginning, messageView.showThreads);
		//Only change the selection if there's a valid nextMessage
		if(nextMessage && (nextMessage != currentMessage)) {
			ChangeSelection(tree, nextMessage);
        }
        else if (type == navigateUnread) {
	        var treeFolder = GetThreadTreeFolder();
	        var originalFolderURI = treeFolder.getAttribute('ref');
            var nextFolderURI = null;
            var done = false;
            var startAtURI = originalFolderURI;
            var i = 0;
            var allServers = accountManager.allServers;
            var numServers = allServers.Count();

            var nextMode = pref.GetIntPref("mailnews.nav_crosses_folders");
            // 0: "next" goes to the next folder, without prompting
            // 1: "next" goes to the next folder, and prompts (the default)
            // 2: "next" does nothing when there are no unread messages

            // not crossing folders, don't find next
            if (nextMode == 2)
                done=true;
            
            // todo:  
            // this will search the originalFolderURI server twice
            // prevent that.
            while (!done) {
                dump("start looking at " + startAtURI + "\n");
                nextFolderURI = FindNextFolder(startAtURI);
                if (!nextFolderURI) {
                    if (i == numServers) {
                        // no more servers, we're done
                        done = true;
                    }
                    else {
                        // get the uri for the next server and start there
                        startAtURI = allServers.GetElementAt(i).QueryInterface(Components.interfaces.nsIMsgIncomingServer).serverURI;
                        i++;
                    }
                }
                else {
                    // got a folder with unread messages, start with it
                    done = true;    
                }
            }
            if (nextFolderURI && (originalFolderURI != nextFolderURI)) {
                var nextFolderResource = RDF.GetResource(nextFolderURI);
                var nextFolder = nextFolderResource.QueryInterface(Components.interfaces.nsIMsgFolder);

                switch (nextMode) {

                case 0:
                    // do this unconditionally
                    gNextMessageAfterLoad = true;
                    SelectFolder(nextFolderURI);
                    break;
                    
                case 1:
                    var promptText = gMessengerBundle.getFormattedString("advanceNextPrompt",
                                                                         [ nextFolder.name ]); 
                    if (commonDialogs.Confirm(window, promptText, promptText)) {
                        gNextMessageAfterLoad = true;
                        SelectFolder(nextFolderURI);
                    }
                    break;
                default:
                    dump("huh?");
                }
            }
        }
	}

    if (showPerformance) {
	  var afterGoNextMessage = new Date();
	  var timeToGetNext = (afterGoNextMessage.getTime() - beforeGoNextMessage.getTime())/1000;
	  dump("time to GoNextMessage is " + timeToGetNext + "seconds\n");
    }
}


/*GoPreviousMessage finds the message that matches criteria and selects it.  
  previousFunction is the function that will be used to detertime if a message matches criteria.
  It must take a node and return a boolean.
  startFromEnd is a boolean that states whether or not we should start looking at the end
  if we reach the beginning 
*/
function GoPreviousMessage(type, startFromEnd)
{
	var tree = GetThreadTree();
	
	var selArray = tree.selectedItems;
	if ( selArray && (selArray.length == 1) )
	{
		var currentMessage = selArray[0];
		var previousMessage = msgNavigationService.FindPreviousMessage(type, tree, currentMessage, RDF, document, startFromEnd, messageView.showThreads);
		//Only change selection if there's a valid previous message.
		if(previousMessage && (previousMessage != currentMessage))
			ChangeSelection(tree, previousMessage);
	}
}





// type is the the type of the next thread we are looking for.
// startFromBeginning is true if we should start looking from the beginning after we get to the end of the thread pane.
// gotoNextInThread is true if once we find an unrad thread we should select the first message in that thread that fits criteria
function GoNextThread(type, startFromBeginning, gotoNextInThread)
{
	if(messageView.showThreads)
	{
		var tree = GetThreadTree();
		
		var selArray = tree.selectedItems;
		var length = selArray.length;

		if ( selArray && ((length == 0) || (length == 1)) )
		{
			var currentMessage;

			if(length == 0)
				currentMessage = null;
			else
				currentMessage = selArray[0];

			var nextMessage;
			var currentTopMessage;
			var checkCurrentTopMessage;
			//Need to get the parent message for the current selection to begin to find thread
			if(currentMessage)
			{
				//need to find its top level message and we don't want it to be checked for criteria
				currentTopMessage = FindTopLevelMessage(currentMessage);
				checkCurrentTopMessage = false;
			}
			else
			{
				//currentTopmessage is the first one in the tree and we want it to be checked for criteria.
				currentTopMessage = msgNavigationService.FindFirstMessage(tree);
				checkCurrentTopMessage = true;
			}

			var nextTopMessage = msgNavigationService.FindNextThread(type, tree, currentTopMessage, RDF, document, startFromBeginning, checkCurrentTopMessage);

			var changeSelection = (nextTopMessage != null && ((currentTopMessage != nextTopMessage) || checkCurrentTopMessage));
			if(changeSelection)
			{
				if(gotoNextInThread)
				{
					nextMessage = msgNavigationService.FindNextInThread(type, tree, nextTopMessage, RDF, document);
					ChangeSelection(tree, nextMessage);
				}
				else
					ChangeSelection(tree, nextTopMessage);	
			}
		}
	}

}

function FindTopLevelMessage(startMessage)
{
	var currentTop = startMessage;
	var parent = startMessage.parentNode.parentNode;

	while(parent.localName == 'treeitem')
	{
		currentTop = parent;
		parent = parent.parentNode.parentNode;
	}

	return currentTop;
}


function ScrollToFirstNewMessage()
{
	var tree = GetThreadTree();
	var treeFolder = GetThreadTreeFolder();

	var folderURI = treeFolder.getAttribute('ref');
	var folderResource = RDF.GetResource(folderURI);
	var folder = folderResource.QueryInterface(Components.interfaces.nsIMsgFolder);
	var hasNew = folder.hasNewMessages;
	if(hasNew)
	{
		var newMessage = folder.firstNewMessage;

		if(messageView.showThreads)
		{
			//if we're in thread mode, then we need to actually make sure the message is showing.
			var topLevelMessage = GetTopLevelMessageForMessage(newMessage, folder);
			var topLevelResource = topLevelMessage.QueryInterface(Components.interfaces.nsIRDFResource);
			var topLevelURI = topLevelResource.Value;
			var topElement = document.getElementById(topLevelURI);
			if(topElement)
			{
				msgNavigationService.OpenTreeitemAndDescendants(topElement);
			}

		}
		
		var messageResource = newMessage.QueryInterface(Components.interfaces.nsIRDFResource);
		var messageURI = messageResource.Value;
		var messageElement = document.getElementById(messageURI);

		if(messageElement)
		{
			tree.ensureElementIsVisible(messageElement); 
		}
	}
}

function GetTopLevelMessageForMessage(message, folder)
{
	if(!folder)
		folder = message.msgFolder;

	var thread = folder.getThreadForMessage(message);
    var outIndex = new Object();
	var rootHdr = thread.GetRootHdr(outIndex);

	var topMessage = folder.createMessageFromMsgDBHdr(rootHdr);

	return topMessage;

}


