/**
 * @fileOverview
 * @name Slider.js
 * @Author Hans Hillen hhillen@paciellogroup.com
 * @version 0.6
 *
 * Copyright (c) 2008 The Paciello Group, Inc
 * Licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0
 * http://creativecommons.org/licenses/by-nc-sa/3.0/us/
 */

/*global TPG */
var TPG;
if (!TPG) {
    TPG = {};
}
if (!TPG.Control) {
    TPG.Control = {};
}

(function() {
/**
 * Script configuration
 */

function T(id, rootNode, cfg) {
    var that = this;
    var openedItems = new Array();

    if (!rootNode) {
        TPG.log("Specified element not found, couldn't apply tree markup");
        return false;
    }
    if (!rootNode.id)
        rootNode.id = id;
    that.id = id;
    var _sourceElem = rootNode;

    //set config vars
    if (!cfg) {
        cfg = TPG.getVarsFromClass(rootNode, "tpg_");
    }
    if (!cfg) {
        //at the moment there are no config vars for this tree
        cfg= {};
    }


    /* private vars */

    var _activeNode;
    var _anchorNode;
    var _selectedNodes = [];

    /* Tree check functions */

    function _isTree(elem) {
        return elem && elem.getAttribute('role') == "tree";
    }

    function _isTreeItem(elem) {
        return elem && elem.getAttribute('role') == "treeitem";
    }

    function _isGroup(elem) {
        return elem && (elem.getAttribute('role') == "group" || elem.getAttribute('role') == "tree");
    }

    function _isCollapsed(elem) {
        return elem.getAttribute('aria-expanded') == "false";
    }

    function _isExpandable(elem) {
        return elem.getAttribute('aria-expanded') !== null;
    }

    function _isLabel(node) {
        return _isTreeItem(node) ? node : null;
    }

    function _isCheckBox(node) {
        //this check will need to be modified if custom aria checkboxes are used.
        return node.nodeName.toLowerCase() == 'input' && node.getAttribute('type') == 'checkbox' ? node : null;
    }

    function _isTwisty(node) {
        return node.nodeName.toLowerCase() == "img" && TPG.hasClassName(node, 'twisty')
    }

    /* Tree behavior functions */

    function _create() {

        var i;
        var firstNode = _findFirstBranchChild(_sourceElem);
        TPG.Focus.makeFocusable(firstNode, true);
        _activeNode = firstNode;

        that.addHandlers();
        //get all branch checkboxes
        var checkNodes = TPG.getElementsByClassName(_sourceElem, 'treeCheck', 'input');
        for (i = 0; i < checkNodes.length; i++) {
            checkNodes[i].setAttribute('tabindex', '-1');
            if (TPG.hasClassName(checkNodes[i], 'branchCheck')) {
                checkNodes[i].onclick = function() {
                    return _toggleBranchCheck(this);
                }
            }
        }
        var twisties = TPG.getElementsByClassName(_sourceElem, 'twisty', 'img');
        for (i in twisties) {
            twisties[i].onclick = function() {
                return _toggleBranch(TPG.Dom.findSpecialSibling(this, _isLabel, true));
            }
        }
    }

    that.addHandlers = function(tree) {
        var tree = _sourceElem;
        tree.onkeydown = _handleKeyDown;
        tree.onmousedown = _handleMouseDown;
        if (document.addEventListener) {
            tree.addEventListener('focus', _handleFocus, true);
            tree.addEventListener('blur', _handleBlur, true);
        }
        tree.onfocusin = _handleFocus;
        tree.onfocusout = _handleBlur;
    }

    that.removeHandlers = function() {
        var tree = _sourceElem;
        tree.onkeydown = null;
        tree.onmousedown = null;
        if (document.addEventListener) {
            tree.removeEventListener('focus', _handleFocus, true);
            tree.removeEventListener('blur', -handleBlur, true);
        }
        tree.onfocusin = null;
        tree.onfocusout = null;
    }

    function _handleKeyDown(event) {
        event = event || window.event;
        var t = event.target || event.srcElement;
        var c = event.keyCode || event.charCode;
        var treeItem;
        //console.info('keydown: %s on %s.%s', c, t.nodeName, t.className);
        if (!_isTreeItem(t)) {
            return;
        }
        var letItPass = false;
        switch (c) {
            case 37: // left
                if (!event.ctrlKey) {
                    _destroySelection();
                }
                if (!_isExpandable(t) || _isCollapsed(t)) {
                    treeItem = _findParentBranch(t);
                    _focusTreeItem(treeItem);
                }
                else if (!_isCollapsed(t)) {
                    _collapseBranch(t);
                }
            break;
            case 38: // up
                if (!event.ctrlKey) {
                    _destroySelection();
                }
                //console.warn('going up');
                treeItem = _findPreviousTreeNode(t, false);
                _focusTreeItem(treeItem);
            break;
            case 39: // right
                if (!event.ctrlKey) {
                    _destroySelection();
                }
                if (_isExpandable(t)) {
                    if (_isCollapsed(t)) {
                        _expandBranch(t);
                    }
                    else {
                        treeItem = _findFirstBranchChild(t);
                        _focusTreeItem(treeItem);
                    }
                }
            break;
            case 40: // down
                treeItem = _findNextTreeItem(t, true);
                if (!event.ctrlKey) {
                    _destroySelection();
                }
                else {
                    if (event.shiftKey) {
                        //_createSelectionRange(treeItem);
                    }
                }
                _focusTreeItem(treeItem);
            break;
            default:
                letItPass = true;
            break;
            case 32: // space
                if (event.ctrlKey && _isTreeItem(t)) {
                    _toggleSelectedItem(t);
                }
                _toggleBranchCheck(TPG.Dom.findSpecialSibling(t, _isCheckBox, false),true);
            break;
            case 33: // pgup
            break;
            case 34: // pgdn
            break;
            case 35: // end
            break;
            case 36: // home
            break;
        }
        if (!letItPass) {
            return false;
        }
    }


    function _destroySelection() {
        //console.log('destroying selection');
        for (var i = 0; i < _selectedNodes.length; i++) {
            TPG.removeClassName(_selectedNodes[i], 'selected');
        }
        _selectedNodes = [];
    }

    function _removeSelectedNode(index) {
        //console.log('removing node:');
        //console.log(_selectedNodes[index]);
        TPG.removeClassName(_selectedNodes[index], 'selected');
        _selectedNodes.splice(index, 1);
    }

    function _addNodeToSelection(node) {
        //console.log('added node:');
        //console.log(node);
        TPG.addClassName(node, 'selected');
        _selectedNodes.push(node);


    }

    function _toggleSelectedItem(node) {
        if (TPG.hasClassName(node, 'selected')) {
            for (var i = 0; i < _selectedNodes.length; i++) {
                if (_selectedNodes[i] == node) {
                    _removeSelectedNode(i);
                    break;
                }
            }
        }
        else {
            _addNodeToSelection(node);
        }
    }

    function _handleMouseDown(event) {
        event = event || window.event;
        var t= event.target || event.srcElement;
        if (!event.ctrlKey) {
            _destroySelection();
        }
        else {
            _toggleSelectedItem(t);
        }

        _focusTreeItem(t);
        return false;
        //console.info('mousedown: on %s.%s', t.nodeName, t.className);
    }

    function _handleBlur(event) {
        event = event || window.event;
        var t= event.target || event.srcElement;
        //console.log('blur: on %s.%s', t.nodeName, t.className);
        TPG.removeClassName(t, 'focused');
    }

    function _handleFocus(event) {
        event = event || window.event;
        var t= event.target || event.srcElement;
        TPG.addClassName(t, 'focused');
        if (TPG.Dom.isElementNode(_activeNode)) {
            TPG.Focus.makeFocusable(_activeNode, false);
            TPG.Focus.makeFocusable(t, true);
            _activeNode = t;
        }
        //console.log('focus: on %s.%s', t.nodeName, t.className);
        return false;
    }

    function _findTreeItemInElement(elem, backwards) {
        var type;
        if (!elem || !elem.hasChildNodes()) {
            return null;
        }
        var treeItem = null;
        var func = backwards ? TPG.Dom.previousElement : TPG.Dom.nextElement;

        var child = elem[backwards ? 'lastChild' : 'firstChild'];
        //console.log("checking children (backwards = %s) for :", backwards);
        //console.log(elem);
        do {
            if (!TPG.Dom.isElementNode(child)) {
                continue;
            }
        //console.log('looking at child node:');
        //console.log(child);
            if (_isTreeItem(child)) {
                //console.log('It is a tree item');
                return child;
            }
            type = child.nodeName.toLowerCase();
            if ((type=="ul" || type=="li") && child.hasChildNodes()) {
                //console.log('It not a tree item but it might contain one. Look deeper');
                treeItem = _findTreeItemInElement(child);
                if (treeItem) {
                    return treeItem;
                }
            }
            else {
                //console.log('It\'s not a (potential) tree item. Moving to sibling');
            }
        }
        while (child = func(child));
        //console.log('no treeitem found');
    }

    function _findPreviousTreeNode(elem, includeSelf, noDeferring) {
        //console.info('Select previous ('+(includeSelf ? 'including self': 'not including self' ) + ' ), noDeferring = '+noDeferring+', starting node is:');
        //console.log(elem);
        if (!elem || _isTree(elem)) {
            //console.log('Tree root reached or no valid element given.  Giving up.');
            return false;
        }
        var tmpElem = elem;
        var treeItem;
        if (!includeSelf) {
            tmpElem = TPG.Dom.previousElement(tmpElem);
        }
        if (!tmpElem) {
            if (elem.parentNode) {
                //console.log('No siblings left, going to parent');
                return _findPreviousTreeNode(elem.parentNode, false, true);
            }
            else {
                return;
            }
        }
        do {
            //console.log('checking candidate:');
            //console.log(tmpElem)
            if (_isTreeItem(tmpElem)) {
                treeItem = tmpElem;
                //console.log('Found a treeitem, stop looping');
                break;
            }
            else if (_isGroup(tmpElem)) {
                if (noDeferring) {
                    //console.log('found group, trying to find the last last item');
                    treeItem = _findTreeItemInElement(tmpElem, true);
                    noDeferring = false;
                }
                else {
                    //console.log('found group, attempting to find expanded branch item');
                    treeItem = TPG.Dom.findSpecialSibling(tmpElem, _isLabel, false);
                }
                break;
            }
            else {
                //console.log("it wasn't a leaf, branch, or group item, look deeper");
                treeItem = _findTreeItemInElement(tmpElem);
                if (treeItem) {
                    return _findPreviousTreeNode(treeItem, true);
                }
            }
        }
        while (tmpElem = TPG.Dom.previousElement(tmpElem));

        if (!treeItem){
            //console.log('no suitable items found. go to previous sibling of parent node');
            return _findPreviousTreeNode(elem.parentNode, false);
        }
        else {
            if (_isExpandable(treeItem) && !_isCollapsed(treeItem) && !noDeferring) {
                //console.log('found expanded branch item, moving to group...');
                return _findPreviousTreeNode(TPG.Dom.findSpecialSibling(treeItem, _isGroup, true), true, true);
            }
            //console.log('Found item to select: ')
            //console.log(treeItem)
            return treeItem;
        }
    }

    function _findNextTreeItem(elem, includeSelf) {
        var treeItem;
        var tmpElem = elem;
        var skipSiblings = false;
        //console.log('Starting select next with %s self %s included', tmpElem.nodeName, includeSelf ? " " : " not ");
        if (includeSelf) {
            if (!_isCollapsed(tmpElem)) {
        //console.log('element was not collapsed: %s', tmpElem.getAttribute('role'));
                treeItem = _findTreeItemInElement(tmpElem)
            }
            else {
                skipSiblings = true;
            }
        }
        if (!skipSiblings){
            while (tmpElem = TPG.Dom.nextElement(tmpElem)) {
        //console.log('found sibling!');
        //console.log(tmpElem);
                if (_isTreeItem(tmpElem)) {
                    treeItem = tmpElem;
                    break;
                }
                treeItem = _findTreeItemInElement(tmpElem)
                if (treeItem) {
                    break;
                }
            }
        }
        if (!treeItem) {
            tmpElem = elem.parentNode;
            if (tmpElem && tmpElem.getAttribute('role') != 'tree') {
        //console.log('treeitem not found, going to parent: %s (%s)', tmpElem.nodeName, tmpElem.textContent);
                return _findNextTreeItem(tmpElem, false);
            }
        }
        else {
            return treeItem;
        }
    }

    function _focusTreeItem(treeItem) {
        if (treeItem && _isTreeItem(treeItem)) {
            treeItem.focus();
        }
    }

    function _toggleBranch(t) {
        if (_isTreeItem(t) || _isExpandable(t)) {
            if (_isCollapsed(t)) {
                _expandBranch(t);
                openedItems.push(t.id);
            }
            else {
                _collapseBranch(t);
                for (var i = 0 ; i < openedItems.length; i++) {
                    if (openedItems[i] == t.id) openedItems.splice(i, 1);
                }
            }
            setCookie(that.id, openedItems.join(", "), 365);
        }
    }

    function _init() {
        // Reload prefs
        var openedStr = getCookie(that.id);
        openedItems = openedStr.split(", ");
        for (var i = 0 ; i < openedItems.length; i++) {
            var node = $(openedItems[i]);
            _expandBranch(node);
        }
    }

    function _expandBranch(t) {
        var group = TPG.Dom.findSpecialSibling(t, _isGroup, true);
        if (!group) {
            return;
        }
        t.className = t.className.replace(/collapsed/, 'expanded' )
        t.parentNode.className = t.parentNode.className.replace(/collapsed/, 'expanded' )
        group.className = group.className.replace(/collapsed/, 'expanded' )
        t.setAttribute('aria-expanded', 'true');
        group.setAttribute('aria-hidden', 'false');
        var twisty = TPG.Dom.findSpecialSibling(t, _isTwisty, false);
        if (twisty) {
            twisty.src = twisty.src.replace(/collapsed/, 'expanded' )
            twisty.alt = twisty.alt.replace(/collapse/, 'expand' )
        }

        var groupfirst = TPG.Dom.getFirstChild(group);
        if (groupfirst.className == 'loading') {
            getTreeData(group,t.id, t.getAttribute('ext'));
        }
    }

    function _collapseBranch(t) {
        var group = TPG.Dom.findSpecialSibling(t, _isGroup, true);
        if (!group) {
            return;
        }
        t.className = t.className.replace(/expanded/, 'collapsed' )
        t.parentNode.className = t.parentNode.className.replace(/expanded/, 'collapsed' );
        group.className = group.className.replace(/expanded/, 'collapsed' );
        t.setAttribute('aria-expanded', 'false');
        group.setAttribute('aria-hidden', 'true');
        var twisty = TPG.Dom.findSpecialSibling(t, _isTwisty, false);
        if (twisty) {
            twisty.src = twisty.src.replace(/expanded/, 'collapsed' );
            twisty.alt = twisty.alt.replace(/expand/, 'collapse' );
        }
    }

    function _findFirstBranchChild(t) {
        return _findTreeItemInElement(TPG.Dom.getFirstChild(TPG.Dom.findSpecialSibling(t, _isGroup, true)));
    }

    function _findLastBranchChild(t) {
        return _findTreeItemInElement(getLastChild(TPG.Dom.findSpecialSibling(t, _isGroup, true)));
    }

    function _findParentBranch(t) {
        _focusTreeItem(_getParent(t));
    }

    function _getParent(t) {
        var p = TPG.Dom.previousElement(t.parentNode.parentNode);
        return _isTreeItem(p) && _isExpandable(p) ? p : null;
    }

    //checkbox handling

    //checks/unchecks all children checkboxes
    function _toggleBranchCheck(node, simulateClick) {
        var checkBoxes = TPG.getElementsByClassName(node.parentNode, 'treeCheck', 'input');
        if (simulateClick) {
            node.checked = !node.checked;
        }
        var checked = false;
        if (node.checked == true) {
            checkAll = true;
            node.title = node.title.replace(/(de)?select/, 'deselect');
        }
        else {
            checkAll = false;
            node.title = node.title.replace(/(de)?select/, 'select');
        }
        for (var i in checkBoxes) {
            checkBoxes[i].checked = checkAll;
            checkBoxes[i].title = checkAll ? checkBoxes[i].title.replace(/(de)?select/, 'deselect'): checkBoxes[i].title.replace(/(de)?select/, 'select');
        }
    }
   _create();
   _init();
};
/**

 * Handles the creation of trees. Maintains a list of tree objects (manager.sliders) which can be used to

 * target all existing trees (e.g. for recalibration or removal).

 */

T.manager = {
    add : function(id, cfg) {
        var newTree = new T(id, cfg);
        if (newTree.complete) {
            T.instances.add(newTree);
        }
        return newTree;
    },

    createAll : function() {
        var i;
        var idCounter = 0;
        TPG.Aria.checkNameSpacedAria();
        T.instances = new TPG.Instance.list('tree');
        var treeNodes = TPG.getElementsByClassName(document, "tpgTree", "*");
        for (i=treeNodes.length - 1; i >= 0 ; i--) { // loop backwards, as root node may be removed during tree creation
            newTree = new T(treeNodes[i].id ?  treeNodes[i].id : "tpgTree_"+ ++idCounter, treeNodes[i]);
            T.instances.add(newTree, newTree.id);
        }
        T.instances._enumItems.reverse();
    },

    destroyAll : function() {
        if (!T.instances || T.instances.length === 0) {
            return;
        }
        var i;
        for (i = 0; i < T.instances.length; i++) {
            T.instances[i].removeHandlers();
        }
        TPG.Event.removeHandler(window, 'load', T.manager.createAll);
        TPG.Event. removeHandler(window, 'unload', T.manager.destroyAll);
    }
};

TPG.Event.addHandler(window, 'load', T.manager.createAll);
TPG.Event.addHandler(window, 'unload', T.manager.destroyAll);
TPG.Control.Tree = T;
})();

/* AJAX */

function getTreeData(root, id, ext) {

            // Create xmlhttprequest object
        var xmlhttp = null;
        if (window.XMLHttpRequest) {
            xmlhttp = new XMLHttpRequest();
            //make sure that Browser supports overrideMimeType
            if (typeof xmlhttp.overrideMimeType != 'undefined') {
                xmlhttp.overrideMimeType('application/json');
            }
        }
        else if (window.ActiveXObject) {
            xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
        }
        else {
            alert('Perhaps your browser does not support xmlhttprequests?');
        }

        // Create an HTTP GET request
        xmlhttp.open('GET', 'tree.json?id=' + ext, true);

        // Set the callback function
        xmlhttp.onreadystatechange = function() {
            if (xmlhttp.readyState == 4) {
                var data = eval('(' + xmlhttp.responseText + ')');
                displayTreeData(root, data);
            } else {
                // waiting for the call to complete
            }
        };

        // Make the actual request
        xmlhttp.send(null);
}

function displayTreeData(root, data) {

    // Remove loading message
        root.removeChild(TPG.Dom.getFirstChild(root));

    for (var i = 0; i < data.subitems.length; i++) {
        var li = document.createElement('li');
        li.className = 'leafMid';
        li.setAttribute('role', 'presentation');
        var a = document.createElement('a');
        a.className = 'leafLabel';
        a.setAttribute('role', 'treeitem');
        a.setAttribute('tabindex', '-1');
        a.setAttribute('href', data.subitems[i].url);
        var text = document.createTextNode(data.subitems[i].name + " (" + data.subitems[i].number + ")");
        a.appendChild(text);
        li.appendChild(a);
        root.appendChild(li);
    }
}
