// Service
X.createClass('X.mvcs.ServiceEvent',
	// Constructor
	function ServiceEvent(type, target, data)
	{
		this.base(type, target);
		if (type === 'success')
		{
			this.data = data || null;
		}
		else if (type === 'error')
		{
			this.message = data || 'An unknown service error has occurred.';
		}
		return this;
	},
	// Prototype Members
	null,
	// Static Members
	null,
	// Base Class
	X.Event
);

X.createClass('X.mvcs.Service',
	// Constructor
		// settings.timeout
	function Service(id, urlTemplate, settings)
	{
		this.base();
		
		this.id = id;
		this.urlTemplate = urlTemplate;
		
		this.settings = settings || {};
		this.settings.cache = false;
		this.settings.success = this._onSuccess.delegate(this);
		this.settings.error = this._onError.delegate(this);
		
		this.mappings = {};
		
		X.mvcs.Service.instances[this.id] = this;
		
		return this;
	},
	// Prototype Members
	{
		// Override this
		map: function(params)
		{
			return params;
		},
		callService: function(params, post)
		{
			// Resolve params
			params = this.map(params);
			
			this.dispatchEvent(new X.mvcs.ServiceEvent('serviceCall', this));
			
			this.settings.url = X.ConfigTemplate.parse(this.urlTemplate, params);
			if (post)
			{
				this.settings.type = 'POST';
				this.settings.data = post;
			}
			else
			{
				this.settings.type = 'GET';
				this.settings.data = null;
			}
			X.json.RequestManager.call(this.id, this.settings);
		},
		
		_onSuccess: function(data)
		{
			this.dispatchEvent(new X.mvcs.ServiceEvent('success', this, data));
		},
		_onError: function(textStatus, message)
		{
			this.dispatchEvent(new X.mvcs.ServiceEvent('error', this, message));
		}
	},
	// Static Members
	{
		instances: {}
	},
	// Base Class
	X.EventDispatcher
);

// Model
X.createClass('X.mvcs.ModelEvent',
	// Constructor
	function ModelEvent(type, target, data)
	{
		this.base(type, target);
		if (type === 'serviceError')
		{
			this.message = data;
		}
		return this;
	},
	// Prototype Members
	null,
	// Static Members
	null,
	// Base Class
	X.Event
);

X.createClass('X.mvcs.Model',
	// Constructor
	function Model()
	{
		this.base();
		
		this.data = null;
		this.params = null;
		this.post = null;
		this.services = {};
		
		return this;
	},
	// Prototype Members
	{
		loadData: function(serviceId, params, post)
		{
			var service = this.services[serviceId];
			if (!service)
			{
				service = X.mvcs.Service.instances[serviceId];
				if (!service)
				{
					throw new Error('No service with the id "' + serviceId + '" exists.')
				}
				this.services[serviceId] = service;
				
				service.addEventListener('serviceCall', this._onCall.delegate(this));
				service.addEventListener('success', this._onSuccess.delegate(this));
				service.addEventListener('error', this._onError.delegate(this));
			}
			
			this.params = params;
			this.post = post;
			
			service.callService(params, post);
		},
		
		setData: function(data)
		{
			this.data = data;
			
			this.dispatchEvent(new X.mvcs.ModelEvent('update', this));
		},
		
		_onCall: function(evt)
		{
			this.dispatchEvent(new X.mvcs.ModelEvent('serviceCall', this, evt.message));
		},
		
		_onSuccess: function(evt, templateFunc)
		{
			this.setData(evt.data);
		},
		
		_onError: function(evt)
		{
			var data = evt.data || { data: {} };
			data.error = evt.message;
			this.setData(data);
			
			this.dispatchEvent(new X.mvcs.ModelEvent('serviceError', this, evt.message));
		}
	},
	// Static Members
	null,
	// Base Class
	X.EventDispatcher
);

// View
X.createClass('X.mvcs.ViewEvent',
	// Constructor
	function ViewEvent(type, target)
	{
		this.base(type, target);
		return this;
	},
	// Prototype Members
	null,
	// Static Members
	null,
	// Base Class
	X.Event
);
X.createClass('X.mvcs.View',
	// View
	function View(model)
	{
		this.base();
		
		this.model = model;
		
		this.model.addEventListener('update', this.onModelUpdate.delegate(this));
		this.model.addEventListener('serviceCall', this.onModelServiceCall.delegate(this));
		this.model.addEventListener('serviceError', this.onModelServiceError.delegate(this));
		
		return this;
	},
	// Prototype Members
	{
		onModelUpdate: function(evt)
		{
			this.dispatchEvent(new X.mvcs.ViewEvent('update', this));
		},
		
		onModelServiceCall: function(evt)
		{
		},
		
		onModelServiceError: function(evt)
		{
		}
	},
	// Static Members
	null,
	// Base Class
	X.EventDispatcher
);


// Controller
X.createClass('X.mvcs.Controller',
	// Constructor
	function Controller(model, views)
	{
		this.model = model;
		
		if (views)
		{
			if (typeof(views.length) === 'number')
			{
				this.views = views;
				for (var idx = 0, len = this.views.length; idx < len; idx++)
				{
					this.views[idx].addEventListener('update', this.onViewUpdate.delegate(this));
				}
			}
			else
			{
				this.views = views;
				for (var viewName in this.views)
				{
					this.views[viewName].addEventListener('update', this.onViewUpdate.delegate(this));
				}
			}
		}
		else
		{
			throw new Error('X.mvcs.Controller (constructor):: The whole point of an MVC framework is to have views.');
		}
		
		return this;
	},
	// Prototype Members
	{
		init: function()
		{
			
		},
		
		registerView: function(view, key)
		{
			if (typeof(key) !== 'undefined')
			{
				if (this.views[key])
				{
					throw new Error('X.mvcs.Controller.registerView:: The view/key already exists in this controller');
				}
				
				if (typeof(key) === 'number')
				{
					if (!jQuery.isArray(this.views))
					{
						throw new Error('X.mvcs.Controller.registerView:: The views collection for this controller is key-value based, passing a number based key is invalid.');
					}
					if (key < 0)
					{
						throw new Error('X.mvcs.Controller.registerView:: The key is an index and must be a positive number');
					}
					if (key > this.views.length)
					{
						throw new Error('X.mvcs.Controller.registerView:: The key is an index and must not be greater than the number of currently registered views.');
					}
				}
				else if (typeof(key) === 'string' && key.length > 0)
				{
					if (jQuery.isArray(this.views))
					{
						throw new Error('X.mvcs.Controller.registerView:: The views collection for this controller is index based - passing a string based key is invalid.');
					}
				}
				else
				{
					throw new Error('X.mvcs.Controller.registerView:: Invalid key.');
				}
			}
			else
			{
				if (!jQuery.isArray(this.views))
				{
					throw new Error('X.mvcs.Controller.registerView:: The views collection for this controller is key-value based, passing a key is required.');
				}
				
				key = this.views.length;
			}
			
			this.views[key] = view;
			
			view.addEventListener('update', this.onViewUpdate.delegate(this));
		},
		
		bindEventListeners: function()
		{
			// TODO - bind DOM event listeners to controller's methods
		},
		
		/*rebindEventListeners: function(view)
		{
			// TODO - rebind view specific DOM event listeners to controller's methods
			switch (view)
			{
				
			}
		},*/
		
		onViewUpdate: function(evt)
		{
			//this.rebindEventListeners(evt.target);
		}
	},
	// Static Members
	null,
	// Base Class
	null
);
