/* <copyright>
This file contains proprietary software owned by Motorola Mobility, Inc.<br/>
No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.<br/>
(c) Copyright 2011 Motorola Mobility, Inc.  All Rights Reserved.
</copyright> */

var Montage = require("montage/core/core").Montage;
var Component = require("montage/ui/component").Component;
var nj = require("js/lib/NJUtils").NJUtils;

exports.Tree = Montage.create(Component, {

    _treeDepth: {
    	value: 1
    },
    treeDepth: {
    	get: function() {
    		return this._treeDepth;
    	},
    	set: function(value) {
    		this._treeDepth = value;
    	}
    },
    _depthHash: {
    	value: ""
    },
    depthHash: {
    	get: function() {
    		return this._depthHash;
    	},
    	set: function(strValue) {
    		this._depthHash = strValue;
    	}
    },
    _firstLevel: {
    	value: true
    },
    firstLevel: {
    	get: function() {
    		return this._firstLevel;
    	},
    	set: function(value) {
    		this._firstLevel = value;
    	}
    },

    _hasFocus: {
        enumerable: false,
        value: false
    },

    _selectedNode: {
        enumerable: false,
        value: null
    },

    _selectedNodes: {
        enumerable: false,
        value: null
    },

    _dataProvider: {
        enumerable: false,
        value: null
    },

    dataProvider: {
        enumerable: true,
        get: function() {
            return this._dataProvider;
        },
        set: function(dp) {
            this._dataProvider = dp.documentElement;
            this.needsDraw = true;
        }
    },

    _jsonData: {
        enumerable: false,
        value: null
    },

    jsonData: {
        enumerable: true,
        get: function() {
            return this._jsonData;
        },
        set: function(jsonObject) {
            this._jsonData = jsonObject;
            this.needsDraw = true;
        }
    },

    _traverseJson: {
    	value: function(jsonObject, parentElement, intCounter) {
	    	var newLi = document.createElement("li"),
				fileSpan = document.createElement("span"),
				spaceSpan = document.createElement("span"),
				nameSpan = document.createElement("span"),
				sizeSpan = document.createElement("span"),
				dateSpan = document.createElement("span"),
				clearSpan = document.createElement("span"),
				containerSpan = document.createElement("span"),
				textName = document.createTextNode(jsonObject.name),
				textSize = "",
				textDate = "",
				textSpace = document.createTextNode("\u00A0"),
				indent = this.treeDepth * 18,
				strIndent =  indent + "px",
				extension = jsonObject.name.split(".").pop(),
				makeFriendlySize = function(intSize) {
					var strSize = false,
						intRoundedSize = Math.round(intSize/1000);
					strSize = intRoundedSize + " K";
					return strSize;
				},
				makeFriendlyDate = function(intSeconds) {
					// TODO: Localization.
					var myDate = new Date(intSeconds),
						strDate = "";
					strDate = (myDate.getMonth() + 1) + "/" 
							  + myDate.getDate() + "/" 
							  + myDate.getFullYear() + " "
							  + myDate.toLocaleTimeString();
					return strDate;
				}
				
    		// File or directory?
    		if (jsonObject.type === "file") {
    			// Build file item:
    			// Create li, give it attributes and event listeners
    			// and then append it to the DOM
    			// Markup is a little complex in order to handle indention and columns.

    			
    			textSize = document.createTextNode(makeFriendlySize(jsonObject.size));
    			fileSpan.setAttribute("class", "pp-col-files");
    			sizeSpan.setAttribute("class", "pp-col-size");
    			sizeSpan.appendChild(textSize);
    			spaceSpan.setAttribute("class", "span-space");
    			spaceSpan.appendChild(textSpace);
    			spaceSpan.style.width = strIndent;
    			clearSpan.setAttribute("class", "clear");
    			fileSpan.appendChild(spaceSpan);
    			fileSpan.appendChild(textName);
    			
    			dateSpan.setAttribute("class", "pp-col-date");
    			textDate = document.createTextNode(makeFriendlyDate(parseInt(jsonObject.modifiedDate)));
    			dateSpan.appendChild(textDate);
    			
    			// Append elements in order
    			containerSpan.appendChild(fileSpan);
    			containerSpan.appendChild(dateSpan);
    			containerSpan.appendChild(sizeSpan);
    			
    			containerSpan.appendChild(clearSpan);
    			containerSpan.setAttribute("tabindex", 0);
    			containerSpan.setAttribute("class", "pp-span-all");
    			newLi.appendChild(containerSpan);
    			
    			// Loop through the JSON properties and set them as data attributes on the element
    			for (var property in jsonObject) {
					var newAttribute = "data-" + property;
					newLi.setAttribute(newAttribute, jsonObject[property]);
    			}
    			
    			// Set depth hash data
	    		newLi.setAttribute("data-depthhash", this.depthHash + "" + intCounter);
    			
    			// We also need to set the class of the element
    			newLi.setAttribute("class", jsonObject.type);
    			
    			
    			// Get the file extension 
    			newLi.classList.add(extension.toLowerCase());
    			
    		    // Add event listeners. Use the nifty identifier feature.
    		    newLi.identifier="jsontree";
    			newLi.addEventListener("click", this, false);
    			newLi.addEventListener("keydown", this, false);
    			
    			// Add element to the DOM.
    			parentElement.appendChild(newLi);
    			
    		} else {
    			// If it's not a file, it's a directory, so build directory item:
    			// Create li for directory entry, give it properties
    			// If it has children, create a UL for it and recurse.
    			// Markup is a little complex in order to handle indention and columns.

    			fileSpan.setAttribute("class", "pp-col-files");
    			if (this.firstLevel) {
    				fileSpan.setAttribute("title", jsonObject.uri);
    				fileSpan.classList.add("bold");
    				this.firstLevel = false;
    			}
    			sizeSpan.setAttribute("class", "pp-col-size");
    			dateSpan.setAttribute("class", "pp-col-date");
    			spaceSpan.setAttribute("class", "span-space");
    			spaceSpan.appendChild(textSpace);
    			spaceSpan.style.width = strIndent;
    			clearSpan.setAttribute("class", "clear");
    			fileSpan.appendChild(spaceSpan);
    			fileSpan.appendChild(textName);
    			
    			containerSpan.appendChild(fileSpan);
    			containerSpan.appendChild(dateSpan);
    			containerSpan.appendChild(sizeSpan);
    			containerSpan.appendChild(clearSpan);
    			containerSpan.setAttribute("tabindex", 0);
    			containerSpan.setAttribute("class", "pp-span-all");
    			
    			newLi.appendChild(containerSpan);
    			
    			// Loop through the JSON properties and set them as data attributes on the element
    			for (var property in jsonObject) {
    				if (property !== "children") {
						var newAttribute = "data-" + property;
						newLi.setAttribute(newAttribute, jsonObject[property]);
    				}
    			}
    			
    			
    			// Set element classes
    		    newLi.setAttribute("class", jsonObject.type);
    			if (this.treeDepth < 3) {
    				newLi.classList.add("level1");
    			}
    			
    			// Set depth hash data
	    		newLi.setAttribute("data-depthhash", this.depthHash + "" + intCounter);
    		    
    		    // Add event listeners. Use nifty identifier feature.
    		    newLi.identifier="jsontree";
    			newLi.addEventListener("click", this, false);
    			newLi.addEventListener("keydown", this, false);
    			
				// Append element to the DOM.
    			parentElement.appendChild(newLi);
    			
    			// Does the directory have children?
    			if (jsonObject.children != null) {
    				// Yes it does. Create a new UL and recurse.
	    			var newUl = document.createElement("ul"),
	    				jsonObjectLength = jsonObject.children.length,
	    				oldDepthHash = this.depthHash;
	    			
	    			// Only show the first two levels of the list open, otherwise show them as closed.
	    			if (this.treeDepth < 3) {
	    				newLi.classList.add("open");
	    			} else {
	    				newLi.classList.add("closed");
	    			}

					// Extend depthHash:
	    			this.depthHash = this.depthHash + "" + intCounter + ",";
	    			
	    			newLi.appendChild(newUl);
    				for (var i = 0; i < jsonObjectLength; i++) {
    					this.treeDepth = this.treeDepth + 1;
    					this._traverseJson(jsonObject.children[i], newUl, i);
    					this.treeDepth = this.treeDepth -1;
    				}
    				
    				// we're done recursing, so restore depthHash to what it was before we recursed:
    				this.depthHash = oldDepthHash;
    			}
    		}
    	}
    },
    handleJsontreeClick: {
    	value: function(event) {
            event.stopImmediatePropagation();
            var target = event.currentTarget,
            	myType = target.dataset.type,
            	treeClickEvent;
    		// What type of item did we just click on?
    		if (myType === "directory") {
    			// We just clicked on a directory. Toggle it!
    			target.classList.toggle("open");
    			target.classList.toggle("closed");
    			// Dispatch an event that can be used by the Project Panel 
				treeClickEvent = document.createEvent("UIEvents");
				treeClickEvent.initEvent("treeClickEvent", false, false);
				document.dispatchEvent(treeClickEvent);
    		}
    	}

    },
    
    handleJsontreeKeydown: {
    	value: function(event) {
            var target = event.currentTarget,
            	myType = target.dataset.type, 
            	nextSpan = false,
            	mySeebl = false,
            	treeClickEvent = document.createEvent("UIEvents"),
            	getNextSibling = function(el) {
            		// Get the next sibling of a file element.
            		// Returns the sibling if it exists, or false if there is none.
            		
            		// first of all, if we're at the top of the tree we're already done.
            		var myParentUl = nj.queryParentSelector(el, "ul");
            		if (myParentUl.getAttribute("id") === "pp-container-tree") {
            			return false;
            		}
            		var myPar = nj.queryParentSelector(el, "li"),
            			mySeebl = myPar.nextSibling;
            		if (mySeebl === null) {
            			mySeebl = getNextSibling(myPar);
            		}
            		if (mySeebl === false) {
            			return false;
            		}
            		return mySeebl;
            	}, 
            	drillDown = function (ptrLi) {
            		// Drill down into a subtree
            		var returnSibling = false;
            		if ((ptrLi.classList.contains("directory")) && (ptrLi.classList.contains("open"))) {
            			returnSibling = drillDown(ptrLi.querySelector("li:last-child"));
            		} else {
            			returnSibling = ptrLi;
            		}
					return returnSibling;
            	},
				goUp = function (ptrLi, isRecursing) {
					// Get the previous item in a tree.
            		var testSibling = ptrLi.previousSibling,
            			newSibling = "",
						returnSibling = false;
					if (isRecursing) {
						testSibling = ptrLi;
					}
	            	if ((testSibling !== null) && (testSibling.querySelector)) {
	            		// exists. If it's a open directory, we need to drill down into it.
	            		if ((testSibling.classList.contains("directory"))&&(testSibling.classList.contains("open")) &&(!isRecursing)) {
	            			returnSibling = drillDown(testSibling);
	            		} else {
	            			// We can just use it;
	            			returnSibling = testSibling;
	            		}
	            	} else {
	            		// It doesn't exist, so we need to go up a level.
	            		newSibling = nj.queryParentSelector(ptrLi, "li");
	            		returnSibling = goUp(newSibling, true);
	            	}
	            	
	            	return returnSibling;
            	};
            	
    		// Stop propagation.
            event.stopImmediatePropagation();

            if (event.keyCode === 40) {
				// Down arrow pressed.
				// Prevent scroll.
            	event.preventDefault();

            	if (myType === "directory") {
            		// The keypress happened on a directory.
            		// Is it open or closed?
            		if (target.classList.contains("open")) {
            			// Go into the subdirectory
            			var myPar = nj.queryParentSelector(event.target, "li");
            			
            			nextLi = myPar.querySelector("ul li");
            			nextSpan = nextLi.querySelector("span");
            			// But the subdirectory might be empty...if so, get
            			// the next element
            			if (nextSpan === null) {
            				nextSpan = target.nextSibling.querySelector(".pp-span-all");
            			}
            		} else if (target.classList.contains("closed")) {
            			var myParentUl = nj.queryParentSelector(target, "ul");
            			if (myParentUl.getAttribute("id") !== "pp-container-list") {
            				// Closed directory, so get the next sibling element.
            				nextSpan = target.nextSibling.querySelector(".pp-span-all");
            			}
            		}
            	} else {
            		// The keypress happened on a file, so we need to get the next 
            		// element and focus it.
            		mySeebl = getNextSibling(event.target);
            		if (mySeebl) {
            			nextSpan = mySeebl.querySelector(".pp-span-all");
            		}
            	}

            	// If the next element isn't null or false, focus it
            	if ((nextSpan !== null) && (nextSpan !== false)) {
            		nextSpan.focus();
            	}
            }
            
            if (event.keyCode === 38) {
            	// Up arrow pressed.
            	// Prevent scroll.
            	event.preventDefault();
            	
            	var myLi = nj.queryParentSelector(event.target, "li"),
            	myUl = nj.queryParentSelector(myLi, "ul"),
            	nextSibling = "";
            	
            	// If we're not already at the top of the tree, we need to 
            	// goUp.
            	if (myUl.getAttribute("id") !== "pp-container-tree") {
            		nextSibling = goUp(myLi, false);
	            	nextSpan = nextSibling.querySelector(".pp-span-all");
	    
	            	// If the next element isn't null or false, focus it
	            	if ((nextSpan !== null) && (nextSpan !== false)) {
	            		nextSpan.focus();
	            	}
            	}
            }
            
            if (event.keyCode === 37) {
            	// Left arrow pressed.
            	// Prevent scroll.
            	event.preventDefault();
            	
            	var projectPanel = nj.queryParentSelector(event.target, "#projectPanel"),
            		firstButton = projectPanel.querySelector(".button-project");
            		firstButton.focus();
            }
            
            if (event.keyCode === 13) {
            	// return pressed.

	    		if (myType === "directory") {
	    			target.classList.toggle("open");
	    			target.classList.toggle("closed");
	    		}
				
    			// Dispatch an event that can be used by the Project Panel 
				treeClickEvent.initEvent("treeClickEvent", false, false);
				document.dispatchEvent(treeClickEvent);
            }
            
            if (event.keyCode === 39) {
            	// Right arrow key pressed
            	event.preventDefault();
            }
    	}
    },

    // TODO This should be more flexible - it should accept strings and objects as well.
    // Adds a node to root tree node
    addTreeNode: {
        value: function(treeNode) {
            if(this.dataProvider)
            {
                // TODO This should set the dataProvider instead so we draw on the next frame.
                this._element.appendChild(treeNode);
                this.needsDraw = true;
            }
            else
            {
                // TODO create a new dataProvider
            }
        }
    },

    // arg1 = new tree node's id
    // arg2 = label
    // should also allow users to specify an object that is the "data" for that tree
    addTreeNode2: {
        value: function(treeID, treeLabel) {
            var curNode = document.createElement("li");
            curNode.id = treeID;
            curNode.addEventListener("click", this, false);

            var leafIcon = document.createElement("img");
            leafIcon.src = "js/components/tree.reel/treeItem.png";
            leafIcon.width = 16;
            leafIcon.height = 16;
            leafIcon.addEventListener("click", this, false);

            var textNode = document.createElement("text");
            textNode.textContent = treeLabel;
            textNode.insertBefore(leafIcon, textNode.firstChild);
            curNode.appendChild(textNode);

            this.addTreeNode(curNode);
        }
    },


    // add a new tree node to an existing parent tree node 
    addTreeNode3: {
        value: function(treeNode, parentNode) {
            if(parentNode)
            {
                // TODO This should set the dataProvider instead so we draw on the next frame.
                // TODO If parentNode is an LI element, we need to convert it to an UL element
                parentNode.appendChild(treeNode);
                this.needsDraw = true;
            }
            else
            {
                
            }
        }
    },

    // TODO This should be more flexible - it should accept strings and objects as well.
    removeTreeNode: {
        value: function(treeNode) {
            var nodeToDelete = document.getElementById(treeNode.id);
            if(nodeToDelete)
            {
                this._element.removeChild(nodeToDelete);
                this.needsDraw = true;
            }
        }
    },

    draw: {
        value: function() {

        }
    },

    _createFolderNode: {
        value: function(folderID, folderLabel, isFolder, isExpanded)
        {
            var parNode = document.createElement("li");
            parNode.id = folderID;
            parNode.setAttribute("isFolder", isFolder);
            parNode.setAttribute("isExpanded", isExpanded);
            parNode.addEventListener("click", this, false);

            var folderIcon = document.createElement("img");
            folderIcon.src = "js/components/tree.reel/treeFolderOpen.png";
            folderIcon.width = 16;
            folderIcon.height = 16;
            
            var textNode = document.createElement("text");
            textNode.textContent = folderLabel;

            var disclosureIcon = document.createElement("img");
            disclosureIcon.src = "js/components/tree.reel/treeDisclosure.png";
            disclosureIcon.width = 16;
            disclosureIcon.height = 16;
            disclosureIcon.addEventListener("click", this, false);

            textNode.insertBefore(folderIcon, textNode.firstChild);
            textNode.insertBefore(disclosureIcon, textNode.firstChild);

            parNode.appendChild(textNode);

            var curNode = document.createElement("ul");
            curNode.id = folderID + "folderItems";
            parNode.appendChild(curNode);

            return parNode;
        }
    },

    _setNodeStyle: {
        value: function(dp, par) {
            var dpLen = dp.length;

            
            for(var i=0; i < dpLen; i++)
            {
                var treeNode = dp[i];
                if(treeNode.nodeType !== 1)
                {
                    continue;
                }

                var newNode;

                if(treeNode.childNodes.length === 0)
                {
                    if(treeNode.nodeName === "folder")
                    {
                        newNode = this._createFolderNode(treeNode.getAttribute("id"),
                                                treeNode.getAttribute("label"),
                                                "true",
                                                "true");
                        
                        par.appendChild(newNode);
                    }
                    else if(treeNode.nodeName === "leaf")
                    {
                        var leafIcon = document.createElement("img");
                        leafIcon.src = "js/components/tree.reel/treeItem.png";
                        leafIcon.width = 16;
                        leafIcon.height = 16;
                        leafIcon.addEventListener("click", this, false);

                        newNode = document.createElement("li");
                        newNode.id = treeNode.getAttribute("id");
                        newNode.addEventListener("click", this, false);
                        newNode.draggable = true;

                        // test code for component panel needed by our DragDropManager
                        newNode.ondragstart = function(event){
                            event.dataTransfer.setData ("text/plain", event.currentTarget.id + "-Component");
                        };

                        var textNode = document.createElement("text");
                        textNode.textContent = treeNode.getAttribute("label");
                        textNode.insertBefore(leafIcon, textNode.firstChild);
                        newNode.appendChild(textNode);

                        par.appendChild(newNode);
                    }
                    else
                    {
                        console.log("Did not handle tree node " + treeNode.nodeName);
                    }
                }
                else
                {
                    newNode = this._createFolderNode(treeNode.getAttribute("id"),
                                                treeNode.getAttribute("label"),
                                                "true",
                                                "true");

                    par.appendChild(newNode);

                    this._setNodeStyle(treeNode.childNodes, newNode.lastChild);

                }
            }
        }
    },

    prepareForDraw: {
        value: function() {
            if(this.dataProvider) {
                this._setNodeStyle(this.dataProvider.childNodes, this._element);
            } else if (this.jsonData) {
            	this._traverseJson(this.jsonData, this._element, 0);
            }
        }
    },

    handleClick: {
        value: function(event) {
//            this._acknowledgeIntent();
            event.stopImmediatePropagation();
            var target = event.currentTarget;

            switch(target.nodeName)
            {
                case "LI":
                {
                    console.log("clicked " + target.id);
                    target.classList.add("selected");
                    if( this._selectedNode && (this._selectedNode !== target) )
                    {
                        this._selectedNode.classList.remove("selected");
                    }
                    this._selectedNode = target;
                    var actionEvent = document.createEvent("CustomEvent");
                    actionEvent.initEvent("change", true, true);
                    actionEvent.type = "change";
                    actionEvent.treeNode = target;
                    actionEvent.mouseEvent = event;
                    this.dispatchEvent(actionEvent);
                    break;
                }
                case "IMG":
                {
                    var _parent = target.parentElement.parentElement;
                    if(_parent.getAttribute("isFolder"))
                    {
                        // toggle the items in the UL node
                        if(_parent.getAttribute("isExpanded") === "true")
                        {
                            _parent.setAttribute("isExpanded", "false");
                            _parent.classList.remove("expanded");
                            _parent.children[0].children[0].style.webkitTransform = "rotate(-90deg)";
                            _parent.children[0].children[1].src = "js/components/tree.reel/treeFolderClosed.png";
                            this.toggleFolderState(_parent.children[1], false);
                        }
                        else
                        {
                            _parent.setAttribute("isExpanded", "true");
                            _parent.classList.add("expanded");
                            _parent.children[0].children[0].style.webkitTransform = "rotate(0deg)";
                            _parent.children[0].children[1].src = "js/components/tree.reel/treeFolderOpen.png";
                            this.toggleFolderState(_parent.children[1], true);                            
                        }
                    }
                    break;
                }
            }

            // TODO - This is just for testing
            if(target.id === "addItem")
            {
                var curNode = document.createElement("li");
                var uniqueID = Math.floor(Math.random()*9999);
                curNode.id = "newItem_" + uniqueID;
                curNode.addEventListener("click", this, false);

                var leafIcon = document.createElement("img");
                leafIcon.src = "Tree.reel/treeItem.png";
                leafIcon.width = 16;
                leafIcon.height = 16;
                leafIcon.addEventListener("click", this, false);

                var textNode = document.createElement("text");
                textNode.textContent = "New Item " + uniqueID;
                textNode.insertBefore(leafIcon, textNode.firstChild);
                curNode.appendChild(textNode);
                this.addTreeNode(curNode);
            }
            else if(target.id === "addItem2")
            {
                this.addTreeNode2("TestItem2", "This is a test item");
            }
            else if(target.id === "addItem3")
            {
                var curNode = document.createElement("li");
                var uniqueID = Math.floor(Math.random()*9999);
                curNode.id = "newItem_" + uniqueID;
                curNode.addEventListener("click", this, false);

                var leafIcon = document.createElement("img");
                leafIcon.src = "js/components/tree.reel/treeItem.png";
                leafIcon.width = 16;
                leafIcon.height = 16;
                leafIcon.addEventListener("click", this, false);

                var textNode = document.createElement("text");
                textNode.textContent = "New Sub Item " + uniqueID;
                textNode.insertBefore(leafIcon, textNode.firstChild);
                curNode.appendChild(textNode);
                
                this.addTreeNode3(curNode, this._element.children[0].children[1]);
            }
            else if(target.id === "removeItem")
            {
                // Get last node
                var curNode = this._element.children[this._element.children.length-1];
                curNode.removeEventListener("click", this, false);
                this.removeTreeNode(curNode);
            }
        }
    },


    toggleFolderState : {
        value : function(folderNode, expand)
        {
            var i = 0;
            var len = folderNode.children.length;
            
            if(!expand)
            {
                for(i=0; i<len; i++)
                {
                    folderNode.children[i].style.display = "none";
                }
            }
            else
            {
                for(i=0; i<len; i++)
                {
                    if(folderNode.children[i].nodeName === "LI")
                    {
                        folderNode.children[i].style.display = "list-item";
                    }
                    else
                    {
                        // special case folders
                        folderNode.children[i].style.display = "block";
                    }
                }
            }
        }
    },

	init: {
		value: function() {
			this.needsDraw = true;
			this.prepareForDraw();
		}
		
	}

});