/*
    @license Angular Treeview version 0.1.6
    ⓒ 2013 AHN JAE-HA http://github.com/eu81273/angular.treeview
    License: MIT
*/

'use strict';

angular.module('angularTreeview')
    .directive('treeview', function($compile, $log, $filter, $timeout) {
        return {
            restrict: 'AE',
            link: function(scope, element, attrs) {

                //tree id
                var treeId = attrs.treeId;

                //tree model
                var treeview = attrs.treeview || 'model';

                //node id
                var nodeId = attrs.nodeId || 'id';

                var nodeLeafIds = attrs.nodeLeafIds || 'leafIds';

                //node label
                var nodeLabel = attrs.nodeLabel || 'label';

                //children
                var nodeChildren = attrs.nodeChildren || 'items';

                var numOfChildren = attrs.numOfChildren || 'numberOfItems';

                var selectedName = attrs.selectedName || '';

                var selectorIndex = attrs.selectorIndex || '0';

                var levelDepth = parseInt(attrs.levelDepth) + 1;

                var parentId = parseInt(attrs.parentId) || '0';

                var selectedShowCondition = '(node.isChecked || !selectedOnly) && node.isShow';
                var showExpandCollapseCondition = '(node.' + numOfChildren + ' || node.' + nodeChildren + '.length)';

                //tree template
                var template =
                    '<ul>' +
                    '<li ng-repeat="node in ' + treeview + '" ng-class="{\'first-depth\' : ' + levelDepth + ' === 1}">' +
                    '<span class="blank" ng-show="' + levelDepth + ' === 1 && !' + showExpandCollapseCondition + ' && ' + selectedShowCondition + '"><span class="blank fonticon icon-chevron-left"></span></span>' +
                    '<span class="elbow" ng-show="' + levelDepth + ' != 1 && !node.' + nodeChildren + '.length && !node.' + numOfChildren + ' && ' + selectedShowCondition + '"><span class="elbow fonticon icon-chevron-left"></span></span>' +
                    '<span class="collapsed" ng-show="' + showExpandCollapseCondition + ' && !node.expanded && ' + selectedShowCondition + '" ng-click="' + treeId + '.selectNode(node)"><span class="collapsed fonticon icon-dropdown-arrow"></span></span>' +
                    '<span class= "expanded" ng-show="' + showExpandCollapseCondition + ' && node.expanded  && ' + selectedShowCondition + '" ng-click="' + treeId + '.selectNode(node)"><span class="expanded fonticon icon-dropdown-arrow"></span></span>' +
                //'<span class="triCheckbox" ng-show="' + selectedShowCondition + '" ng-class="{\'mid\' : node.isMid, \'checked\' : node.isChecked}" ng-click="(' + scope.useLeafsOnly + ' && node.' + numOfChildren + ') ||' + treeId + '.selectCheckbox(node);">' +
                //'<span class="fonticon" ng-show="' + !scope.useLeafsOnly + ' || !node.' + numOfChildren + '" ng-class="{\'icon-checkbox\' : !node.isChecked && !node.isMid, \'icon-checkbox-tick\' : node.isChecked && !node.isMid, \'icon-select-yes\' : node.isChecked && node.isMid}"></span>' +
                '<span class="fonticon" ng-show="' + scope.useLeafsOnly + ' && node.' + numOfChildren + '" ng-class="{\'icon-folder\' : !node.expanded, \'icon-folder-open\' : node.expanded}" ng-click="' + treeId + '.selectNode(node)"></span></span>' +
                    '<span ng-class="{selected : node.isSelected}" ng-show="' + selectedShowCondition + '" ng-click="' + treeId + '.selectNode(node)"><span class="nodeLabel" ng-class="{primarySearchResult: node.primarySearchResult, secondarySearchResult: node.secondarySearchResult}">{{node.' + nodeLabel + '}}</span><span ng-show="node.' + numOfChildren + '"> ({{node.' + numOfChildren + '}})</span><span ng-if="node.type===\'Service\'" ng-click="deleteCallback()(node)" class="fonticon icon-small-close"></span></span>' +
                    '<span ng-show="node.expanded && ' + selectedShowCondition + '" tree-id="' + treeId + '" treeview="node.' + nodeChildren + '" node-id="' + nodeId + '" node-label="' + nodeLabel + '" node-children="' + nodeChildren + '" selected-name="' + selectedName + '" level-depth="' + levelDepth + '" selector-index = "' + selectorIndex + '"></span>' +
                    '</li>' +
                    '</ul>';

                var expandCollapseNode = function(selectedNode) {
                    if (selectedNode[nodeChildren] && selectedNode.expanded) {
                        for (var i = 0; i < selectedNode[nodeChildren].length; i++) {
                            selectedNode[nodeChildren][i].isShow = true;
                            selectedNode[nodeChildren][i].expanded = false;
                        }
                    }
                };

                scope.resetCheckboxes = function(treeRoot) {

                    for (var i = 0; i < treeRoot.length; i++) {
                        scope[treeId].uncheckBox(treeRoot[i]);
                        scope[treeId].uncheckAllChildren(treeRoot[i]);
                    }
                };

                var addSelectedNodesToModel = function(nodesList) {
                    if (!angular.isArray(nodesList)) {
                        return;
                    }

                    angular.forEach(nodesList, function(node, index) {
                        if (!scope.useLeafsOnly) {
                            if (node.isChecked && !node.isMid) {
                                if (scope.useLeafIds) {
                                    angular.forEach(node[nodeLeafIds], function(id) {
                                        scope.model.values.push(id);
                                    });
                                } else {
                                    scope.model.values.push(node[nodeId]);
                                }
                                scope.model.names.push(node[nodeLabel]);
                            } else if (node.isMid) {
                                addSelectedNodesToModel(node[nodeChildren]);
                            }

                            // useLeafsOnly: do not add ids of nodes that have children (leaf nodes only)
                        } else {
                            if (node.isChecked) {
                                if (!node.numberOfItems) {
                                    scope.model.values.push(node[nodeId]);

                                    if (node.externalId) {
                                        scope.model.names.push(node.externalId);
                                    } else {
                                        scope.model.names.push(node[nodeLabel]);
                                    }
                                } else {
                                    addSelectedNodesToModel(node[nodeChildren]);
                                }
                            }
                        }

                    });

                };

                //check tree id, tree model
                if (treeId && treeview) {

                    //root node
                    if (attrs.filterTreeview) {

                        //create tree object if not exists
                        scope[treeId] = scope[treeId] || {};

                        //if node head clicks,
                        scope[treeId].selectNode = scope[treeId].selectNode || function(selectedNode) {

                            //remove in ark code
                            scope.setSelection(selectedNode);

                            //Collapse or Expand
                            selectedNode.expanded = !selectedNode.expanded;

                            var hasChildrenToLoad = (selectedNode[numOfChildren] > 0) && (!selectedNode[nodeChildren] || !selectedNode[nodeChildren].length);

                            if (selectedNode.expanded && hasChildrenToLoad) {
                                // lazy load node children
                                filterDataService.getNodeChildren(selectedName, selectedNode[nodeId], function(nodeChildrenItems) {
                                    angular.forEach(nodeChildrenItems, function(node, key) {
                                        node.isChecked = false;
                                        node.isMid = false;
                                        node.isShow = true;
                                        node.expanded = false;
                                        node.parentId = selectedNode[nodeId];

                                        if (selectedNode.isChecked) {
                                            node.isChecked = true;
                                        }

                                        if (!node.items) {
                                            node.items = [];
                                        }
                                    });
                                    selectedNode[nodeChildren] = nodeChildrenItems;
                                    expandCollapseNode(selectedNode);
                                });
                            } else {

                            }
                        };

                        //update data in service (excuted any time a checkbox state is toggled)
                        if (selectedName) {
                            scope[treeId].updateSelected = scope[treeId].updateSelected || function() {
                                if (scope.model.values && scope.model.names) {
                                    // clear the data model:
                                    scope.model.values.splice(0, scope.model.values.length);
                                    scope.model.names.splice(0, scope.model.names.length);


                                    // add all the selected items back to the data model:
                                    addSelectedNodesToModel(scope[treeview]);
                                }
                            };
                        }


                        //checkbox helper functions
                        scope[treeId].checkBox = scope[treeId].checkBox || function(selectedNode) {
                            selectedNode.isChecked = true;
                            selectedNode.isMid = false;
                            scope[treeId].updateSelected();
                        };

                        scope[treeId].uncheckBox = scope[treeId].uncheckBox || function(selectedNode) {
                            selectedNode.isChecked = false;
                            selectedNode.isMid = false;
                            scope[treeId].updateSelected();
                        };

                        scope[treeId].midcheckBox = scope[treeId].midcheckBox || function(selectedNode) {
                            selectedNode.isChecked = true;
                            selectedNode.isMid = true;
                            scope[treeId].updateSelected();
                        };

                        //recursive functions
                        scope[treeId].checkAllChildren = function(node) {
                            for (var i = 0; i < node[nodeChildren].length; i++) {
                                scope[treeId].checkBox(node[nodeChildren][i]);
                                scope[treeId].checkAllChildren(node[nodeChildren][i]);
                            }

                        };

                        scope[treeId].uncheckAllChildren = function(node) {
                            for (var i = 0; i < node[nodeChildren].length; i++) {
                                scope[treeId].uncheckBox(node[nodeChildren][i]);
                                scope[treeId].uncheckAllChildren(node[nodeChildren][i]);
                            }

                        };

                        scope[treeId].selectCheckbox = scope[treeId].selectCheckbox || function(selectedNode) {

                            //if box is unchecked or midchecked
                            if (!selectedNode.isChecked || selectedNode.isMid) {

                                scope[treeId].checkBox(selectedNode);

                                //recursively check all children
                                scope[treeId].checkAllChildren(selectedNode);

                                //recursive function that updates parent checkboxes when a child is checked
                                scope[treeId].checkParent = function(node, nodeChild) {
                                    var isChild = false;

                                    //go through every child and verify if nodeChild is one of them
                                    for (var i = 0; i < node[nodeChildren].length; i++) {
                                        if (node[nodeChildren][i] === nodeChild) {
                                            isChild = true;
                                            break;
                                        }
                                    }

                                    //we're studying the children of the parent of the node
                                    //we know that the nodeChild is checked, but his siblings might not be
                                    //go through every child and verify if they are all checked
                                    if (isChild) {
                                        var allChecked = true;
                                        for (var i = 0; i < node[nodeChildren].length; i++) {
                                            if (!node[nodeChildren][i].isChecked || node[nodeChildren][i].isMid) {
                                                allChecked = false;
                                                break;
                                            }
                                        }

                                        if (allChecked) {
                                            scope[treeId].checkBox(node);

                                            //if the checked node isn't at the root, repeat
                                            var isRoot = false;
                                            for (var i = 0; i < scope[treeview].length; i++) {
                                                if (node === scope[treeview][i]) {
                                                    isRoot = true;
                                                    break;
                                                }
                                            }

                                            if (!isRoot) {
                                                for (var k = 0; k < scope[treeview].length; k++) {
                                                    scope[treeId].checkParent(scope[treeview][k], node);
                                                }
                                            }

                                        } else {
                                            scope[treeId].midcheckBox(node);

                                            //if the checked node isn't at the root, repeat
                                            isRoot = false;
                                            for (var i = 0; i < scope[treeview].length; i++) {
                                                if (node === scope[treeview][i]) {
                                                    isRoot = true;
                                                    break;
                                                }
                                            }

                                            if (!isRoot) {
                                                for (var k = 0; k < scope[treeview].length; k++) {
                                                    scope[treeId].checkParent(scope[treeview][k], node);
                                                }
                                            }
                                        }

                                        //if child isn't found then proceed a level deeper
                                    } else {
                                        for (var i = 0; i < node[nodeChildren].length; i++) {
                                            scope[treeId].checkParent(node[nodeChildren][i], nodeChild);
                                        }
                                    }
                                };

                                //goes through each child of the root
                                for (var j = 0; j < scope[treeview].length; j++) {
                                    scope[treeId].checkParent(scope[treeview][j], selectedNode);
                                }

                                //if box is checked
                            } else if (!selectedNode.isMid) {

                                scope[treeId].uncheckBox(selectedNode);

                                //recursively uncheck all children
                                scope[treeId].uncheckAllChildren(selectedNode);

                                //recursive function that updates parent checkboxes when a child is checked
                                scope[treeId].uncheckParent = function(node, nodeChild) {
                                    var isChild = false;

                                    //go through every child and check if nodeChild is one of them
                                    for (var i = 0; i < node[nodeChildren].length; i++) {
                                        if (node[nodeChildren][i] === nodeChild) {
                                            isChild = true;
                                            break;
                                        }
                                    }

                                    //go through every child and check if they are all unchecked
                                    if (isChild) {
                                        var allUnchecked = true;
                                        for (var i = 0; i < node[nodeChildren].length; i++) {
                                            if (node[nodeChildren][i].isChecked) {
                                                allUnchecked = false;
                                                break;
                                            }
                                        }

                                        if (allUnchecked) {
                                            scope[treeId].uncheckBox(node);

                                            //if the unchecked node isn't at the root, repeat
                                            var isRoot = false;
                                            for (var i = 0; i < scope[treeview].length; i++) {
                                                if (node === scope[treeview][i]) {
                                                    isRoot = true;
                                                    break;
                                                }
                                            }

                                            if (!isRoot) {
                                                for (var k = 0; k < scope[treeview].length; k++) {
                                                    scope[treeId].uncheckParent(scope[treeview][k], node);
                                                }
                                            }

                                        } else {
                                            scope[treeId].midcheckBox(node);

                                            //if the unchecked node isn't at the root, repeat
                                            isRoot = false;
                                            for (var i = 0; i < scope[treeview].length; i++) {
                                                if (node === scope[treeview][i]) {
                                                    isRoot = true;
                                                    break;
                                                }
                                            }

                                            if (!isRoot) {
                                                for (var k = 0; k < scope[treeview].length; k++) {
                                                    scope[treeId].uncheckParent(scope[treeview][k], node);
                                                }
                                            }

                                        }
                                        //if child isn't found then proceed a level deeper
                                    } else {
                                        for (var i = 0; i < node[nodeChildren].length; i++) {
                                            scope[treeId].uncheckParent(node[nodeChildren][i], nodeChild);
                                        }
                                    }
                                };

                                //goes through each child of the root
                                for (var j = 0; j < scope[treeview].length; j++) {
                                    scope[treeId].uncheckParent(scope[treeview][j], selectedNode);
                                }
                            }
                        };
                    }

                    //Rendering template.
                    element.html('').append($compile(template)(scope));


                    //scope.nodeLabels = angular.element(element).find('.nodeLabel');
                }
            }
        };
    });