/* Session data functions */
function navGetSessionData()
{
    var options = new Object;
    options.path = "/";    
    return $.cookie("luuveeNavigation", options);
}

function navSetSessionData(data)
{
    var options = new Object;
    options.path = "/";
    $.cookie("luuveeNavigation", options, data);
}

function navResetSessionData()
{
    navSetSessionData("");
}

/* Page object functions */
var navPageObjectDelimiter = ":";
var navPageObjectArrayDelimiter = ";";
var navPageHistoryDelimiter = "$";

function navSerializePageObject(pageObject)
{
    var serializedPageObject = "";
    
    if (pageObject == null) return "";
    
    try
    {
        serializedPageObject = 
            pageObject.pageName.toString() + navPageObjectDelimiter +
            pageObject.pageIdentity.toString() + navPageObjectDelimiter +
            pageObject.historyIndex.toString();
    }   
    catch(ex)
    {
        serializedPageObject = "";
    }    
    
    return serializedPageObject;
}

function navDeserializePageObject(serializedPageObject)
{
    var pageObject = new NavPageObject();
    
    if (serializedPageObject == null) return null;
    
    try
    {
        var serializedPageObjectFields = serializedPageObject.split(navPageObjectDelimiter);        
        pageObject.pageName = serializedPageObjectFields[0].toString();
        pageObject.pageIdentity = serializedPageObjectFields[1].toString();
        pageObject.historyIndex = serializedPageObjectFields[2].toString();
    }
    catch(ex)
    {
        pageObject = null;
    }
    
    return pageObject;
}

function navSerializePageObjectArray(pageObjectArray)
{
    var serializedPageObjectArray = "";
    
    if (pageObjectArray == null) return "";
    
    try
    {
        for(var i = 0; i < pageObjectArray.length; i++)
        {
            var pageObject = pageObjectArray[i];
            
            if (serializedPageObjectArray.length != 0)
            {
                serializedPageObjectArray += navPageObjectArrayDelimiter;
            }
            serializedPageObjectArray += navSerializePageObject(pageObject);
        }
    }
    catch(ex)
    {
        serializedPageObjectArray = "";
    }
    
    return serializedPageObjectArray;
}

function navDeserializePageObjectArray(serializedPageObjectArray)
{
    var pageObjectArray = new Array;
    
    if (serializedPageObjectArray == null || serializedPageObjectArray == "") return null;
    
    try
    {    
        var serializedPageObjects = serializedPageObjectArray.split(navPageObjectArrayDelimiter);
        for(var i = 0; i < serializedPageObjects.length; i++)
        {
            var serializedPageObject = serializedPageObjects[i];
            var pageObject = navDeserializePageObject(serializedPageObject);
            pageObjectArray[pageObjectArray.length] = pageObject;
        }
    }
    catch(ex)
    {
        pageObjectArray = new Array;
    }
    
    return pageObjectArray;
}

function navSerializePageHistory(pageHistory)
{
    var serializedPageHistory = "";
    
    if (pageHistory == null) return "";
    
    try
    {
        serializedPageHistory = 
            pageHistory.currentHistoryIndex + 
            navPageHistoryDelimiter +
            pageHistory.currentHistoryLength + 
            navPageHistoryDelimiter +
            navSerializePageObjectArray(pageHistory.pageObjectArray);            
            navPageHistoryDelimiter +
            pageHistory.hasInitialState;
    }
    catch(ex)
    {
        serializedPageHistory = "";
    }
    
    return serializedPageHistory;
}

function navDeserializePageHistory(serializedPageHistory)
{
    if (serializedPageHistory == null || serializedPageHistory == "") return null;
    
    var pageHistory = null;
    
    try
    {    
        var serializedPageHistoryParts = serializedPageHistory.split(navPageHistoryDelimiter);
        var currentHistoryIndex = (serializedPageHistoryParts.length > 0) ? serializedPageHistoryParts[0] : 0;
        var currentHistoryLength = (serializedPageHistoryParts.length > 1) ? serializedPageHistoryParts[1] : history.length;
        var serializedPageObjectArray = (serializedPageHistoryParts.length > 2) ? serializedPageHistoryParts[2] : "";
        var hasInitialState = (serializedPageHistoryParts.length > 3) ? serializedPageHistoryParts[3] : false;
        
        var pageObjectArray = navDeserializePageObjectArray(serializedPageObjectArray);
        pageHistory = new NavPageHistory(currentHistoryIndex, currentHistoryLength, pageObjectArray, hasInitialState);
    }
    catch(ex)
    {
        pageHistory = new NavPageHistory("", new Array);
    }
    
    return pageHistory;
}

function NavPageHistory(currentHistoryIndex, currentHistoryLength, pageObjectArray, hasInitialState)
{
    this.load = function()
    {
        var pageHistory = navDeserializePageHistory(navGetSessionData());
        this.currentHistoryIndex = (pageHistory != null) ? pageHistory.currentHistoryIndex : 0;
        this.currentHistoryLength = (pageHistory != null) ? pageHistory.currentHistoryLength : history.length;
        this.pageObjectArray = (pageHistory != null) ? pageHistory.pageObjectArray : new Array;
        this.hasInitialState = (pageHistory != null) ? pageHistory.hasInitialState : true;
    }
    this.save = function()
    {
        navSetSessionData(navSerializePageHistory(this));
    }
    
    if (currentHistoryIndex != null && pageObjectArray != null &&
        currentHistoryLength != null && hasInitialState != null)
    {
        this.currentHistoryIndex = currentHistoryIndex;
        this.currentHistoryLength = currentHistoryLength;
        this.pageObjectArray = pageObjectArray;
        this.hasInitialState = hasInitialState;
    }
    else
    {
        this.currentHistoryIndex = 0;    
        this.currentHistoryLength = history.length;
        this.pageObjectArray = new Array;
        this.hasInitialState = true;
    }
}

function NavPageObject(pageObject)
{    
    this.clone = function() 
    {
        return new NavPageObject(this);
    }
    
    if (pageObject != null)
    {
        this.pageName = pageObject.pageName;
        this.pageIdentity = pageObject.pageIdentity;
        this.historyIndex = pageObject.historyIndex;        
    }
    else
    {
        this.pageName = "";
        this.pageIdentity = "";
        this.historyIndex = 0;        
    }
}

/* Utility functions */
function navToProperCase(s)
{
    var result = s;
    
    try
    {
        result = s.toLowerCase().replace(/^(.)|\s(.)/g, function($1) { return $1.toUpperCase(); });
    }
    catch(ex)
    {
        result = s;
    }
    
    return result;    
}

function navGetArrayValue(array, index, onError)
{
    try
    {
        if (index < array.length)
        {       
            return array[index];
        }
        else
        {
            return onError;
        }        
    }
    catch(ex)
    {
        return onError;
    }
}

function navRemoveArrayValue(array, from, to) {
    
    var result = array;
    
    try
    {
        var rest = array.slice((to || from) + 1 || array.length);
        array.length = from < 0 ? array.length + from : from;
        result = array.push.apply(array, rest);
    }
    catch(ex)
    {
        result = array;
    }
    
    return result;
};


function navGetUrlInfo(url, fileNameIfNotFound)
{
    var urlInfo = new Object;
    
    try
    {
        var urlSplit1 = url.split("://");
        urlInfo.protocol = navGetArrayValue(urlSplit1, 0, null);
        urlInfo.pathAndQuery = navGetArrayValue(urlSplit1, 1, null);
        urlInfo.isInvalid = (urlInfo.protocol == null || urlInfo.fullPath == null);

        if (urlInfo.pathAndQuery != null)
        {
            // Handle path and query string
            var urlSplit2 = urlInfo.pathAndQuery.split("?");
            urlInfo.path = navGetArrayValue(urlSplit2, 0, null);
            urlInfo.query = navGetArrayValue(urlSplit2, 1, null);
            urlInfo.isInvalid = (urlInfo.path == null);
            
            // Handle special case, url ending in ?
            if (urlInfo.pathAndQuery == urlInfo.path + "?")
            {
                urlInfo.query = "";
            }
            
            // Handle path
            if (urlInfo.path != null)
            {
                urlInfo.pathParts = urlInfo.path.split("/");            
                urlInfo.isInvalid = (urlInfo.pathParts == null);
                
                if (urlInfo.pathParts != null)
                {
                    if (urlInfo.pathParts.length == 0)
                    {
                        urlInfo.isInvalid = true;
                    }
                    else if (urlInfo.pathParts.length == 1)
                    {
                        urlInfo.directories = urlInfo.pathParts.clone();
                        urlInfo.fileName = (fileNameIfNotFound != null) ? fileNameIfNotFound : "";
                    }
                    else
                    {
                        urlInfo.directories = urlInfo.pathParts.clone();
                        urlInfo.directories = navRemoveArrayValue(urlInfo.directories, -1);
                        urlInfo.fileName = urlInfo.pathParts[urlInfo.pathParts.length - 1];                    
                    }
                    
                    if (urlInfo.fileName != null)
                    {
                        urlInfo.fileParts = urlInfo.path.split(".");
                        urlInfo.isInvalid = (urlInfo.fileParts == null);
                        
                        if (urlInfo.fileParts != null)
                        {
                            if (urlInfo.fileParts.length == 0)
                            {
                                urlInfo.fileExtension = null;
                                urlInfo.fileTitle = urlInfo.fileName;
                            }
                            else if (urlInfo.fileParts.length > 1)
                            {
                                urlInfo.fileExtension = urlInfo.fileParts[urlInfo.fileParts.length - 1];
                                urlInfo.fileTitle = urlInfo.fileName.substring(0, urlInfo.fileName.length - (urlInfo.fileExtension.length + 1));
                            }
                        }
                    }
                }
            }
                
            if (urlInfo.query != null)
            {
                urlInfo.parameters = urlInfo.path.split("&");
            }
            
            // Get utility values
            urlInfo.fileNameAndQuery = (urlInfo.query != null) ? urlInfo.fileName + "?" + urlInfo.query : urlInfo.fileName;
        }
    }
    catch(ex)
    {
        return null;
    }
    
    return urlInfo;   
}

/* Current Page functions */
function navGetPageName(url)
{
    var urlInfo = navGetUrlInfo(url);
    
    var result = document.title;
    if (urlInfo == null) return result;
    if (urlInfo.fileTitle == null) return result;
    
    result = urlInfo.fileTitle;
    result = result.replace(/-/g, " "); // Remove spaces
    result = navToProperCase(result);
    
    return result;
}

function navGetPageIdentity(url)
{
    var urlInfo = navGetUrlInfo(url);    
    return urlInfo.fileNameAndQuery;
}

function navGetCurrentPageObject()
{
    var pageObject = new NavPageObject();
    pageObject.pageName = navGetPageName(location.href);
    pageObject.pageIdentity = navGetPageIdentity(location.href);
    pageObject.historyIndex = 0;
    return pageObject;
}

function navRegisterCurrentPage(maxPageHistoryLength)
{   
    // Load page history
    var pageHistory = new NavPageHistory();
    pageHistory.load();
    if (pageHistory == null) pageHistory = new NavPageHistory();
    
    // Check for new page
    // Cases : 
    // browser is on the first page and the page history data has initial state, 
    // browser is currently back in history and has requested new page (creating new history), 
    // browser history changes from what was recorded on the last page
    if (pageHistory.currentHistoryLength != history.length || pageHistory.hasInitialState == true)
    {
        var currentPageObject = navGetCurrentPageObject();
        var lastPageObject = (pageHistory.pageObjectArray.length > 0) ? pageHistory.pageObjectArray[pageHistory.pageObjectArray.length - 1] : null;
        if (lastPageObject != null)
        {
            // Register new page and prevent duplicates
            if (currentPageObject.pageIdentity != lastPageObject.pageIdentity)
            {
                // Update history globally
                for(var i = 0; i < pageHistory.pageObjectArray.length; i++)
                {
                    pageHistory.pageObjectArray[i].historyIndex -= 1;                
                }
                
                // Add page to end
                pageHistory.pageObjectArray[pageHistory.pageObjectArray.length] = currentPageObject;                
            }
        }
        else
        {
            // First page
            pageHistory.pageObjectArray[pageHistory.pageObjectArray.length] = currentPageObject;            
        }
        
        // Update page history statistics
        pageHistory.currentHistoryLength = history.length;
        pageHistory.hasInitialState = false;
    }
    
    // Trim page history to prevent it from getting too long
    if (maxPageHistoryLength != null)    
    {
        if (pageHistory.pageObjectArray.length > maxPageHistoryLength)
        {
            // Remove first page object
            navRemoveArrayValue(pageHistory.pageObjectArray, 0, 0);
        }
    }
    
    // Save page history
    pageHistory.save();
}

function navGoto(historyIndex)
{
    // NOTE: This is intended to only go back, historyIndex:elemOf(-x .. 0)   
    if (historyIndex > 0) return;
    
    // Load page history
    var pageHistory = new NavPageHistory();
    pageHistory.load();
    if (pageHistory == null) pageHistory = new NavPageHistory();
    
    // Update history globally, 
    // Remove all pageObjects that are newer or match historyIndex
    var tempPageObjectArray = new Array;
    for(var i = 0; i < pageHistory.pageObjectArray.length; i++)
    {
        var pageObject = new NavPageObject(pageHistory.pageObjectArray[i]);
        if (pageObject.historyIndex <= historyIndex)
        {
            tempPageObjectArray[tempPageObjectArray.length] = pageObject.clone();
        }        
    }    
    pageHistory.pageObjectArray = tempPageObjectArray;
    
    // Update the current index and calculate the number of history moves
    var historyMoves = 0;
    historyMoves = historyIndex - pageHistory.currentHistoryIndex;
    pageHistory.currentHistoryIndex = historyIndex;
    pageHistory.needsResetOnRegister = true;
    
    // Save page history
    pageHistory.save();
    
    // Navigate
    history.go(historyMoves);
}

function navPrintPageHistory()
{
    var pageHistory = new NavPageHistory();
    pageHistory.load();
    if (pageHistory == null) pageHistory = new NavPageHistory();
    
    var result = "Luuvee";
    
    for(var i = 0; i < pageHistory.pageObjectArray.length; i++)
    {
        var pageObject = pageHistory.pageObjectArray[i];
        
        if (result.length != 0)
        {
            result += " > ";
        }
        
        if (i != pageHistory.pageObjectArray.length - 1)
        {
            var href = "javascript:navGoto(" + pageObject.historyIndex + ")";
            var anchor = "<a href='" + href + "'>" + pageObject.pageName + "</a>";
            result += anchor;
        }
        else
        {
            // Last page
            result += pageObject.pageName;
        }
    }
    
    document.write("<div class='navPageHistory'>" + result + "</div>");
}

function navPrintContainerStart()
{
    document.write("<div class='navContainer'>");
}

function navPrintContainerEnd()
{
    document.write("</div>");
}

function navPrintBackButton(backButtonImageUrl)
{
    var pageHistory = new NavPageHistory();
    pageHistory.load();
    if (pageHistory == null) pageHistory = new NavPageHistory();
    
    if (pageHistory.pageObjectArray.length > 1)
    {
        var pageObject = pageHistory.pageObjectArray[pageHistory.pageObjectArray.length - 2];
        var href = "javascript:navGoto(" + pageObject.historyIndex + ")";
        var img = "<img src='" + backButtonImageUrl + "' alt='<< Go Back' />";        
        document.write("<div class='navBackButton'> <a href='" + href + "'>" + img + "</a> </div>" );       
    }
}

function navPrintSessionData()
{
    document.write("<br />Session Data = " + navGetSessionData());
}