/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Components project.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * 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.
**   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
**     the names of its contributors may be used to endorse or promote
**     products derived from this software without specific prior written
**     permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
** OWNER 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."
** $QT_END_LICENSE$
**
****************************************************************************/

// Page stack. Items are page containers.
var pageStack = [];

// Page component cache map. Key is page url, value is page component.
var componentCache = {};

// Returns the page stack depth.
function getDepth() {
    return pageStack.length;
}

// Pushes a page on the stack.
function push(page, properties, replace, immediate) {
    // page order sanity check
    if ((!replace && page == currentPage)
        || (replace && pageStack.length > 1
        && page == pageStack[pageStack.length - 2].page)) {
        throw new Error("Cannot navigate so that the resulting page stack has two consecutive entries of the same page instance.");
    }

    // figure out if more than one page is being pushed
    var pages;
    if (page instanceof Array) {
        pages = page;
        page = pages.pop();
        if (page.createObject === undefined && page.parent === undefined && typeof page != "string") {
            properties = properties || page.properties;
            page = page.page;
        }
    }

    // get the current container
    var oldContainer;
    if (pageStack.length) {
        oldContainer = pageStack[pageStack.length - 1];
    }

    // pop the old container off the stack if this is a replace
    if (oldContainer && replace) {
        pageStack.pop();
    }

    // push any extra defined pages onto the stack
    if (pages) {
        var i;
        for (i = 0; i < pages.length; i++) {
            var tPage = pages[i];
            var tProps;
            if (tPage.createObject === undefined && tPage.parent === undefined && typeof tPage != "string") {
                tProps = tPage.properties;
                tPage = tPage.page;
            }
            pageStack.push(initPage(tPage, tProps));
        }
    }

    // initialize the page
    var container = initPage(page, properties);

    // push the page container onto the stack
    pageStack.push(container);

    depth = pageStack.length;
    currentPage = container.page;

    // perform page transition
    //FIXME: this should be in for PageStack, out for PageRow?
    //immediate = immediate || !oldContainer;
    var orientationChange = false;
    if (oldContainer) {
        orientationChange = orientationChanges(oldContainer.page, container.page);
        oldContainer.pushExit(replace, immediate, orientationChange);
    }

    // sync tool bar
    var tools = container.page.tools || null;
    if (toolBar) {
        toolBar.setTools(tools, immediate ? "set" : replace ? "replace" : "push");
    }

    container.pushEnter(immediate, orientationChange);
    return container.page;
}

// Initializes a page and its container.
function initPage(page, properties) {
    var container = containerComponent.createObject(root);

    var pageComp;
    if (page.createObject) {
        // page defined as component
        pageComp = page;
    } else if (typeof page == "string") {
        // page defined as string (a url)
        pageComp = componentCache[page];
        if (!pageComp) {
            pageComp = componentCache[page] = Qt.createComponent(page);
        }
    }
    if (pageComp) {
        if (pageComp.status == Component.Error) {
            throw new Error("Error while loading page: " + pageComp.errorString());
        } else {
            // instantiate page from component
            page = pageComp.createObject(container.pageParent, properties || {});
        }
    } else {
        // copy properties to the page
        for (var prop in properties) {
            if (properties.hasOwnProperty(prop)) {
                page[prop] = properties[prop];
            }
        }
    }

    container.page = page;
    if (page.parent == null || page.parent == container.pageParent) {
        container.owner = container;
    } else {
        container.owner = page.parent;
    }

    // the page has to be reparented if
    if (page.parent != container.pageParent) {
        page.parent = container.pageParent;
    }

    if (page.pageStack !== undefined) {
        page.pageStack = root;
    }

    page.anchors.fill = container.pageParent

    return container;
}

// Pops a page off the stack.
function pop(page, immediate) {
    // make sure there are enough pages in the stack to pop
    if (pageStack.length > 1) {
        //unwind to itself means no pop
        if (page !== undefined && page == pageStack[pageStack.length - 1].page) {
            return
        }
        // pop the current container off the stack and get the next container
        var oldContainer = pageStack.pop();
        var container = pageStack[pageStack.length - 1];
        if (page !== undefined) {
            // an unwind target has been specified - pop until we find it
            while (page != container.page && pageStack.length > 1) {
                pageStack.pop();
                container.popExit(immediate, false);
                container = pageStack[pageStack.length - 1];
            }
        }

        depth = pageStack.length;
        currentPage = container.page;

        // perform page transition
        var orientationChange = orientationChanges(oldContainer.page, container.page);
        oldContainer.popExit(immediate, orientationChange);
        container.popEnter(immediate, orientationChange);

        // sync tool bar
        var tools = container.page.tools || null;
        if (toolBar) {
            toolBar.setTools(tools, immediate ? "set" : "pop");
        }
        return oldContainer.page;
    } else {
        return null;
    }
}

// Checks if the orientation changes between oldPage and newPage
function orientationChanges(oldPage, newPage) {
    return newPage.orientationLock != 0 //PlasmaComponents.PageOrientation.Automatic
           && newPage.orientationLock != 3//PlasmaComponents.PageOrientation.LockPrevious
           && newPage.orientationLock != oldPage.orientationLock
}

// Clears the page stack.
function clear() {
    var container;
    while (container = pageStack.pop()) {
        container.cleanup();
    }
    depth = 0;
    currentPage = null;
}

// Iterates through all pages in the stack (top to bottom) to find a page.
function find(func) {
    for (var i = pageStack.length - 1; i >= 0; i--) {
        var page = pageStack[i].page;
        if (func(page)) {
            return page;
        }
    }
    return null;
}