
Ext.define('Ext.ux.plugin.data.Tracker', {

	alias: ['plugin.datatracker', 'plugin.DataTracker'],

	extend: 'Ext.AbstractPlugin',

	statics: {
		datatracks: {
			items: [],
			getTrack: function(filterFn) {

				var tracks = Ext.ux.plugin.data.Tracker.datatracks,
					items = tracks.items,
					response = [];

				Ext.Array.each(items, function(track) {
					Ext.Array.each(track, function(trackItem) {
						if (Ext.isFunction(filterFn)) {
							if (filterFn.call(tracks, trackItem))
								response.push(trackItem);
						}
						else {
							response.push(trackItem);
						}
					});
				});

				return response;
			}
		}
	},

	/**
	 * @private
	 *
	 */
	_dataTrack: null,

	/**
	 * @private
	 *
	 */
	_dataParent: null,

	init: function(component) {

		var plugin = this;

		plugin.pluginId = 'datatracker';

		plugin.initDataTracker(component);

		// Make sure this is rendered
		//component.on('afterrender', plugin.initDataTracker, plugin, {single:true});
	},

	initDataTracker: function(component) {

		var plugin = this,
			configParent,
			parent;

		component._isDataTrackerReady = false;

		// TODO: Remove dataParent - conflict on automatic getters and setters in initalConfig!!
		parent = component.initialConfig.dataParent;

		if (component.initialConfig.trackerParent)
			parent = component.initialConfig.trackerParent;

		if (parent) {
			plugin._dataParent = parent;
		}

		// Apply getter methods for the component
		Ext.apply(component, {
			getDataTrack: function (filterFn) {

				var self = this,
					dataParent;

				dataParent = self.getDataParent();

				function DataTracks() {

					this.length = 0;

					this.items = [];

					if (dataParent) this.initialize(dataParent);
				}

				DataTracks.prototype = {

					initialize: function(item) {

						var self = this,
							parentItem;

						self.items.push(item);

						self[self.length] = item;

						self.length++;

						if (Ext.isFunction(item.getParent)) {

							parentItem = item.getParent();

							if (parentItem)
								self.initialize(parentItem);
						}
					},

					each: function(fn, scope) {

						scope = scope || this;

						var self = this,
							items = self.items,
							i = 0,
							len = items.length;

						for (; i < len; i++) {

							var item = items[i];

							fn.call(scope, item, i, items);
						}

						return self;
					},

					some: function(fn, scope) {

						scope = scope || this;

						var self = this,
							items = self.items,
							i = 0,
							len = items.length;

						for (; i < len; i++) {

							var item = items[i];

							if (fn.call(scope, item, i, items)) return true;
						}

						return false;
					},

					findBy: function (f_handler) {

						var m_return = null;

						this.each(function (i_item) {

							var m_temp = f_handler.apply(this, arguments);

							if (m_temp) {

								m_return = i_item;

								return false;
							}
						});

						return m_return;
					},

					getKeys: function() {

						var self = this,
							keys = {};

						self.each(function(item) {

							var parentKeys = item.getKeys();

							if (parentKeys) {

								parentKeys = Ext.decode(parentKeys);

								Ext.Object.merge(keys, parentKeys);
							}
						});

						return keys;
					},

					getKeyNames: function() {

						var self = this,
							namesArray = [],
							names;

						self.each(function(item) {

							var keys = item.getKeys(),
								prop;

							if (keys) {

								keys = Ext.decode(keys);

								for (prop in keys) {

									namesArray.push(prop);
								}
							}
						});

						// Remove doublets from array
						names = Ext.Array.filter(namesArray, function(item, index) {

							return Ext.Array.indexOf(namesArray, item) == index;
						});

						return names;
					},

					getKeyValues: function() {

						var self = this,
							valuesArray = [],
							values;

						self.each(function(item) {

							var keys = item.getKeys(),
								prop;

							if (keys) {

								keys = Ext.decode(keys);

								for (prop in keys) {

									valuesArray.push(keys[prop]);
								}
							}
						});

						// Remove doublets from array
						values = Ext.Array.filter(valuesArray, function(item, index) {

							return Ext.Array.indexOf(valuesArray, item) == index;
						});

						return values;
					},

					filter: function() {

						var self = this;
					},

					toJson: function() {

						var self = this;
					}
				};

				return new DataTracks();
			},
			getDataParent: function () {

				var self = this,
					plugin = self.getPlugin('datatracker'),
					parent = plugin.getDataParent(),
					response = null;

				if (!parent)
					return response;

				response = {
					self: parent,
					children: [],
					getParent: function() {
						return null;
					},
					getStore: function() {
						return null;
					},
					getModel: function() {
						return null;
					},
					getRecord: function() {
						return null;
					},
					getKeys: function() {
						return null;
					}
				};

				// Look for record getting method or other means of getting a record/records
				// Apply method getRecord to response object
				if (parent.getRecord || parent.record) {

					if (Ext.isFunction(parent.getRecord)) {
						response.getRecord = function() {
							return parent.getRecord.call(parent);
						};
					}
					else if (parent.record) {
						response.getRecord = function() {
							return parent.record;
						};
					}
				}

				// Look for a store getting method or other means of getting a store
				// Apply method getStore to response object
				if (parent.getStore || parent.store) {

					if (Ext.isFunction(parent.getStore)) {
						response.getStore = function() {
							return parent.getStore.call(parent);
						};
					}
					else if (parent.store) {
						response.getStore = function() {
							return parent.store;
						};
					}
				}

				// Look for a model getting method or other means of getting a model
				// Apply method getModel to response object
				if (parent.getModel || parent.model) {

					if (Ext.isFunction(parent.getModel)) {
						response.getModel = function() {
							return parent.getModel.call(parent);
						};
					}
					else if (parent.model) {
						response.getModel = function() {
							return parent.model;
						};
					}
				}

				if (parent.getKeys || parent.keys) {

					if (Ext.isFunction(parent.getKeys)) {
						response.getKeys = function() {
							return parent.getKeys.call(parent);
						};
					}
					else if (parent.model) {
						response.getKeys = function() {
							return parent.keys;
						};
					}
				}

				if (Ext.isFunction(parent.getDataParent)) {
					response.getParent = function() {
						return parent.getDataParent.call(parent);
					};
				}

				return response;
			},
			getDataChildren: function (filterFn) {

				var self = this,
					plugin = self.getPlugin('datatracker'),
					roottrack = plugin.getRootTrack(),
					response = [];

				return response;
			},
			setDataParent: function (parent) {

				var self = this;

				plugin.setDataParent(parent);

				return self;
			},
			getDataRoot: function () {

				var self = this,
					dataTrack = self.getDataTrack(),
					root;

				dataTrack.some(function (track) {

					if (!track.getParent()) {
						root = track;
					}
					return true;
				});
			}
		});

		component._isDataTrackerReady = true;

		component.fireEvent('datatrackerinit', component, plugin);
	},

	// Only handles ID for now!
	_queryDataParent: function(request) {

		var response;

		// Determine type of request

		// Is this an Ext component?
		// TODO: Do some actual checking if it is an Ext component
		if (Ext.isObject(request))
			response = request;

		// This is a string, treat is as a query
		if (Ext.isString(request)) {
			response = Ext.getCmp(request);
		}

		return response;
	},

	getDataParent: function() {

		return this._queryDataParent(this._dataParent);
	},

	getDataTrack: function() {

		return this._dataTrack;
	},

	getRootTrack: function() {

		return Ext.ux.plugin.data.Tracker.datatracks;
	},

	setDataParent: function(parent) {

		var plugin = this,
			_dataParent;

		_dataParent = plugin._queryDataParent(parent);

		plugin._dataParent = _dataParent;

		return plugin;
	}
});
