
/**
 * @class Ext.ux.ip2.dmd.uiExplorer.Tree
 * Treepanel that displays the UI's of entities in a DMD.
 * All entities of the DMD and the UI types of the entities are listed as folders from which you can select an UI model
 *
 * @extend {@link Ext.tree.Panel}
 *
 * @alias dmdUiExplorer
 *
 */

Ext.define('Ext.ux.dmd.uiexplorer.Tree', {

	requires: ['Ext.ux.dmd.uiexplorer.Menu'],

	extend: 'Ext.tree.Panel',

	alias: ['widget.dmdUiExplorer'],

	iconClasses: {
		add: 'add16',
		remove: 'delete16',
		edit: 'cog_edit16'
	},

	config: {

		/**
		 * @config {String} [clickType='double'] 'single' or 'double'
		 */
		clickType: 'double',

		/**
		 * @config {String} [dmd=null] Refer to DMD to load on initialization
		 */
		dmd: null,

		/**
		 * @config {String} [pathSeperator='/'] Seperator used for emitting selected paths to UI
		 */
		pathSeperator: '/'

	},

	/**
	 * @config {Boolean} [rootVisible=false] True to show the Entities root
	 */
	rootVisible: true,

	/**
	 * @method addEntities
	 * Feed the explorer an entities object from a DMD to build a tree showing the UI's of the entities
	 *
	 * @params {Object} [entities=null] The data object to add to the explorer, null or empty cleans tree
	 * @chainable
	 */
	addEntities: function(entities) {

		entities || ( entities = null );

		var uiExplorer = this,
			node, config;

		// Get a converted configuration object back to set as root
		config = uiExplorer.buildRootNode(entities);

		// Create node object by setting a new Root Node configuration
		node = uiExplorer.setRootNode(config);

		// Remember to commit the new (root) node
		node.commit();

		// Return this for chainability
		return uiExplorer;
	},

	addDmd: function(dmd) {

	    var self = this;

	    self.setDmd(dmd);

	    self.addEntities(dmd.model.entities);

	    return self;
	},

	/**
	 * @method buildEntityNode
	 *
	 * @param {String} name
	 * @param {Array} children
	 *
	 * @return {Object} node
	 */
	buildEntityNode: function(name, data, children) {
		return {
			id: Ext.id() + '.' + name,
			name: name,
			text: name,
			data: data || {},
			children: children || [],
			type: 'entity'
		};
	},

	/**
	 * @method buildEntityNodes
	 *
	 * @param {Object} entities
	 *
	 * @return {Array} nodes
	 */
	buildEntityNodes: function(entities) {

		var uiExplorer = this,
			nodes = [];

		Ext.Object.each(entities, function(name, entity) {

			var children = uiExplorer.buildTypeNodes(entity.ui),
				node = uiExplorer.buildEntityNode(name, entity, children);

			nodes.push(node);
		});

		return nodes;
	},

	/**
	 * @method buildRootNode
	 *
	 * @param {Object} entities The DMD entity object to explore UI's from
	 *
	 * @return {Object} node The root node to set in tree
	 */
	buildRootNode: function(entities) {

		var uiExplorer = this;

		return {
			id: 'entities',
			text: 'Entities',
			children: uiExplorer.buildEntityNodes(entities)
		};
	},

	/**
	 * @method buildTypeNode
	 *
	 * @param {String} name
	 * @param {Array} children
	 *
	 * @return {Object} node
	 */
	buildTypeNode: function(name, data, children) {
		return {
			id: Ext.id() + '.' + name,
			name: name,
			text: name,
			data: data || {},
			children: children || [],
			type: 'uitype'
		};
	},

	/**
	 * @method buildTypeNodes
	 *
	 *
	 * @param {Object} uitype
	 *
	 * @return {Array} nodes
	 */
	buildTypeNodes: function(type) {

		var uiExplorer = this,
			nodes = [];

		Ext.Object.each(type, function(name, ui) {

			var children = uiExplorer.buildUiNodes(ui),
				node = uiExplorer.buildTypeNode(name, ui, children);

			nodes.push(node);
		});

		return nodes;
	},

	/**
	 * @method buildUiNode
	 *
	 * @param {String} name
	 *
	 * @return {Object} node
	 */
	buildUiNode: function(name, data) {
		return {
			id: Ext.id() + '.' + name,
			name: name,
			text: name,
			type: 'ui',
			data: data || {},
			leaf: true
		};
	},

	/**
	 * @method buildUiNodes
	 *
	 *
	 * @param {Object} ui
	 *
	 * @return {Array} nodes
	 */
	buildUiNodes: function(ui) {

		var uiExplorer = this,
			nodes = [];

		Ext.Object.each(ui, function(name, uidata) {

			var node = uiExplorer.buildUiNode(name, uidata);

			nodes.push(node);
		});

		return nodes;
	},

	/**
	 * @method empty
	 * Empty the tree explorer
	 *
	 * @chainable
	 */
	empty: function() {

		return this.addEntities();
	},

	getConvertedPath: function(path, seperator) {

		seperator || ( seperator = this.getPathSeperator() );

		var uiExplorer = this,
			split = path.split(seperator),
			store = uiExplorer.getSelectionModel().getStore(),
			record,
			converted = '';

		for ( var i = 0; i < split.length; i++ ) {

			// First value of array = entity
			if ( i === 0 ) {
				Ext.Array.each(store.data.items, function(item) {
					if ( item.raw.name == split[i] ) {
						record = item;
						converted += item.data.id;
					}
				});
			}
			else  {
				Ext.Array.each(record.childNodes, function(item) {
					if ( item.raw.name == split[i] ) {
						record = item;
						converted += '/' + item.data.id;
					}
				});
			}
		}

		return converted;
	},

	/**
	 * @method getObjectByPath
	 * Get an object in entities by a record path
	 *
	 * @param {String} path The path to the object in question
	 *
	 * @return {Object} object The retrieved object by path
	 */
	getObjectByPath: function(path) {

		var uiExplorer = this,
			sep = uiExplorer.getPathSeperator(),
			dmd = uiExplorer.getDmd(),
			entities = dmd.model.entities,
			path = path.split(sep),
			object;

		for ( var i = 0; i < path.length; i++ ) {
			if ( i === 0 ) {
				object = entities[path[i]];
			}
			else if ( i === 1 ) {
				object = object.ui[path[i]];
			}
			else {
				object = object[path[i]];
			}
		}

		return object;
	},

	getPathByRecord: function(record) {

		var uiExplorer = this,
			seperator = uiExplorer.getPathSeperator(),
			sep,
			split = record.getPath('id', seperator).split(seperator).slice(2),
			filtered = '';

		for ( var i = 0; i < split.length; i++ ) {
			sep = !i ? '' : seperator;
			filtered += sep + split[i].split('.').pop();
		}

		return filtered;
	},

	getRecordByPath: function(path) {

		var uiExplorer = this,
			seperator = uiExplorer.getPathSeperator(),
			split = path.split(seperator),
			store = uiExplorer.getSelectionModel().getStore(),
			record;

		for ( var i = 0; i < split.length; i++ ) {

			// First value of array = entity
			if ( i === 0 ) {
				Ext.Array.each(store.data.items, function(item) {
					if ( item.raw.name == split[i] )
						record = item;
				});
			}
			else if ( i === 1 ) {
				Ext.Array.each(record.childNodes, function(item) {
					if ( item.raw.name == split[i] )
						record = item;
				});
			}
			else {
				Ext.Array.each(record.childNodes, function(item) {
					if ( item.raw.name == split[i] )
						record = item;
				});
			}
		}

		return record;
	},

	updateRecord: function(record, name) {

		var uiExplorer = this,
			path = uiExplorer.getPathByRecord(record),
			entities = uiExplorer.entities,
			parent,
			object;

		// Get the parent object so we have something to delete and add from
		parent = record.parentNode.raw.data;

		// Get the current object in entities by the path
		object = uiExplorer.getObjectByPath(path);

		// Set the new named object in parent
		parent[name] = object;

		// Delete the old named object in parent
		delete parent[record.raw.name]

		// Update the record id
		var id = record.getId().split('.')[0] + '.' + name;
		record.set('id', id);

		console.log(record, record.raw);

		// Update the record raw data
		record.set('text', name);
		record.raw.name = name;
		record.raw.text = name;

		// Remember to commit the record
		record.commit();

		return record;
	},

	initComponent: function() {

		// 1. Init arguments and variable declarations first
		// 2. Adding and binding of events
		// 4. Loading of configured entities on initialization

		this.callParent(arguments);

		var uiExplorer = A = this;

		// Bind the 'uiselect' event on click's
		var clickType = ( uiExplorer.clickType != 'single' ) ? 'itemdblclick' : 'itemclick';

		uiExplorer.on(clickType, function(view, record) {
			// Use uiExplorer's own getPathByRecord method to get a filtered path from record
			uiExplorer.fireEvent('itemselect', uiExplorer.getPathByRecord(record), record.raw.type, record);
		});

		// Bind the context menu
		uiExplorer.getView().on('itemcontextmenu', function(view, record, htmlEl, index, e, eOpts) {

			// Prevent default action on right click from browser
			e.preventDefault();

			// Set the clicked on node as selected
			view.getSelectionModel().select(record);

			// Expand record if possible
			if ( record.isExpandable() )
				record.expand();

			// Create a menu
			var menu = Ext.create('IP2.dmd.uiExplorer.Menu', {
				renderTo: Ext.getBody(),
				record: record,
				iconClasses: uiExplorer.iconClasses,
				addHandler: function(button) {

					var raw = record.raw,
						type = raw.type;

					Ext.Msg.prompt(button.text, 'Enter name:', function(btn, name) {

						var ok = ( btn === 'ok' ),
							exists,
							newNode, config,
							insertTo;

						exists = type == 'ui' ? record.parentNode : record;
						exists = exists.findChild('text', name);


						if ( ok && !exists) {

							// If type entity add a new uitype node to record
							if ( type == 'entity' ) {

								// Get the node config
								config = uiExplorer.buildTypeNode(name);

								// Get the path of the clicked on record
								path = uiExplorer.getPathByRecord(record);

								// Create a new node to insert to the tree
								newNode = record.createNode(config);

								// Append the new node to the clicked node
								record.appendChild(newNode);

								// Find the corresponding object to the record clicked on to insert to
								insertTo = uiExplorer.getObjectByPath(path);

								// Insert the new object from configuration to the object found above
								insertTo.ui[name] = config.data;

							}

							// If type uitype add a new ui node to record
							if ( type == 'uitype' ) {

								// Get the node config
								config = uiExplorer.buildUiNode(name);

								// Get the path of the clicked on record
								path = uiExplorer.getPath(record);

								// Create a new node to insert to the tree
								newNode = record.createNode(config);

								// Append the new node to the clicked node
								record.appendChild(newNode);

								// Find the corresponding object to the record clicked on to insert to
								insertTo = uiExplorer.getObjectByPath(uiExplorer.getPathByRecord(record));

								// Insert the new object from configuration to the object found above
								insertTo[name] = config.data;

							}

							// If type ui insert a new ui node to record.parentNode
							if ( type == 'ui' ) {

								// Get the node config
								config = uiExplorer.buildUiNode(name);

								// We clicked on a ui (leaf), use parent as reference for insert object
								path = uiExplorer.getPathByRecord(record.parentNode);

								// Create a new node to insert to the tree
								newNode = record.createNode(config);

								// Append the new node to the clicked node
								record.parentNode.appendChild(newNode);

								// Find the corresponding object to the record clicked on to insert to
								insertTo = uiExplorer.getObjectByPath(path);

								// Insert the new object from configuration to the object found above
								insertTo[name] = config.data;

							}

							// New node created bypassing the store needs raw data set
							newNode.raw = config;

						}

						if ( ok && exists ) {

							Ext.Msg.show({
								title:'Name taken!',
								msg: 'This name is already in use',
								icon: Ext.Msg.INFO,
								buttons: Ext.Msg.OK,
								fn: function(btn) {
									if ( btn == 'ok' ) {
										button.up().addHandler(button);
									}
								}
							});

						}

					});
				},
				removeHandler: function(button) {
					Ext.Msg.show({
						title:'Remove UI?',
						msg: 'You are about to remove a UI from the DMD - Do you really wish to proceed?',
						buttons: Ext.Msg.YESNOCANCEL,
						icon: Ext.Msg.WARNING
					});
				},
				renameHandler: function(button) {

					Ext.Msg.prompt(button.text, 'Enter new name:', function(btn, name) {

						// Does this already exist?
						var ok = ( btn == 'ok' ),
							exists;

						exists = record.parentNode.findChild('text', name);

						// OK button hit and no such UI exists
						if ( ok && !exists ) {
							uiExplorer.updateRecord(record, name);
						}

						// OK button hit, but such a named UI exists already
						if ( ok && exists ) {
							Ext.Msg.show({
								title:'Name taken!',
								msg: 'This name is already in use',
								icon: Ext.Msg.INFO,
								buttons: Ext.Msg.OK,
								fn: function(btn) {
									if ( btn == 'ok' ) {
										button.up().renameHandler(button);
									}
								}
							});
						}

					});
				}
			}).showAt(e.getXY());

		});

		//We have a entity declared in configuration - load it!
		if ( uiExplorer.entities )
			uiExplorer.addEntities(uiExplorer.entities);

	}
});
