/**
 * Django Ajax Form jQuery Plug-in definition
 *
 */
(function($) {	
	$.fn.djangoAjaxForm = function(options) {
		var defaults = {
			submit : function(){ return true; }			
		};
		var options = $.extend(defaults, options);  
		
		/**
		 * Sigleton with all possible AJAX commands from backend
		 */
  		var Commands = (function(){
  			return {
  				redirect : function(object, data){
  					document.location = data.url
  				},
  				replace : function(object, data){
  					if(data.object != null){
  						object = data.object;
  					}
  					$(object).replaceWith(data.body);
  				},
  				insert : function(object, data){
  					if(data.block != null){
  						data.object = data.block;
  					}
  					
  					Commands.url(object, data);
  				},
  				url : function(object, data){
  					if(data.object != null){
  						html_object = data.object;
  					}
  					
  					Helper.showProgress();
  					// load and replace url
  					$.ajax({
  						'url' : data.url,
  						'type' : 'GET',
  						'dataType' : 'text',
  						'success' : function(response_data){
  							// skip all previous blocks with same ID
  							if(data.replace_object){
  								if(data.replace_object.constructor.toString().indexOf("Array") == -1){
  									data.replace_object = [data.replace_object];
  								}
  								for(var i = 0; i < data.replace_object.length; i++){
  									$(data.replace_object[i]).remove();
  								}
  							}  							
  							
  							$(html_object).replaceWith(response_data);
  							if(data.reset_form){
  								Helper.resetForm(object);
  							}
  							Helper.hideProgress();
  						}
  					});  				
  					
  				},
  				ajaxform : function(object, data){
  					var form = $(data.formBody);
  					$(object).replaceWith(form);
  					$(form).djangoAjaxForm();
  				}
  			}
  		})();
  		
		/**
		 * Helper singleton to make more accessible django ajax form
		 */
		var Helper = (function(){
			return {
				/**
				 * disable all controls of current form:
				 * 	> set attribute disabled to true
				 *  > add "disabled" class
				 */	
				disableControls : function(object){
					$(object).find('textarea,input,select').attr("readonly", true).addClass('disabled');
					$(object).find('.ajax-visual-submit').each(function(){
						var disable = $(this).data('disable');
						if(disable != null)
							disable();							
					});				
				},
				resetForm : function(object){
					$(object)[0].reset();
				},				
				/**
				 * enable all controls of current form:
				 * 	> set attribute disabled to false
				 *  > remove "disabled" class
				 */
				enableControls : function(object){
					$(object).find('textarea,input,select').attr("readonly", false).removeClass('disabled');
					$(object).find('.ajax-visual-submit').each(function(){
						var enable = $(this).data('enable');
						if(enable != null)
							enable();							
					});						
				},
				// simple method to add custom hidden fields
				addHiddenField : function(object, name, value){
					var field = $("input[name='" + name + "']", object); 
					if(field.length > 0){
						field.val(value);
						return;
					}
					// add hidden field which to the object
					$(object).append('<input type="hidden" name="' + name + '" value="' + value + '" />')
				},
				showProgress : function(object){					
					$(object).queue(function(){
						$('.ajax-loading', $(object)).slideDown('normal');
						$(this).dequeue();
					});
				},
				hideProgress : function(object, callback){
					$(object).queue(function(){
						$(object).find('.ajax-loading').slideUp('normal', callback);
						$(this).dequeue();
					});
				},
				connectionError : function(object){
					alert('Connection error. Try again later...');
				}
			}
		})();
		
		/**
		 * Queue of controls which have been changed
		 */
		var ControlsQueue = (function(){
			var controls = {};	
			var activity = {};
			return {
				push : function(id, f, timeout){					
					if(controls[id] != true){
						controls[id] = true;
					}else{
						return;
					}
					
					var func = function(){						
						activity[id] = activity[id] || true;						
						if(activity[id] == false){
							console.log('Activity is disabled');
							return;
						}
						// skip queue if isn't available in queue list
						if(controls[id] == null)return;
						f(id);
						setTimeout(func, timeout);
						activity[id] = false;
					};
					func();
				},
				del : function(id){
					controls[id] = null;
				}
			};
		})();
		
		var changeControls = [];
		var changeRequestStatus = false;
		
		var changeRequestChain = function(object){
			if(changeRequestStatus == false && changeControls.length > 0){
				var fields = [];
				// get all unique fields which have been changed
				console.log(changeControls);
				for(var i = 0; i < changeControls.length; i++){
					if(fields.indexOf(changeControls[i]['name']) < 0){
						fields.push(changeControls[i]['name']);
					}
				}
				
				changeRequestStatus = true;
				makeAjaxRequest(object, { 'customFields' : fields, '__dafAjaxValidation' : 'True' });
			}
			setTimeout(Global.wrapFunc(changeRequestChain, object), 3000);
		};
		
		
		//Global.wrapFunc(makeAjaxRequest, object, customParams)
		//var changeControls
		var changeHandler = function(object, control, id){	
			var val = $(control).val();
			var prev = $(control).data('prev') || '';
			if(val != prev){				
				$(control).data('prev', $(control).val());
				
				console.log('ID: ' + id + ' / Value was changed from [' + prev + '] to [' + val + ']');
				changeControls[changeControls.length] = { 'name' : $(control).attr('name'), 'value' : val };
			}			
		}
		/**
		 * Register control to make some validation during typing
		 */
		var registerChange = function(id, object, control, timeout){
			ControlsQueue.push(id, Global.wrapFunc(changeHandler, object, control), timeout);
		};
		var unRegisterChange = function(id){
			ControlsQueue.del(id);
		};
		
		/**
		 * Handle request CALLBACK
		 */
		var receiveHandler = function(object, data){
			console.log(data);
			changeRequestStatus = false;
			changeControls = [];
						
			// clear old errors from onchange customFields set
			
			if(data.customFields){
				var fields = [];
				for(var i in data.customFields){
					fields.push('errors-' + data.customFields[i]);					
				}
				$('.errorsHolder', object).each(function(){
					var id = $(this).attr('id');
					console.log('Remove element with id: ' + id);
					if(fields.indexOf(id) >= 0){
						$(this).empty();					
					}
				});
				
			}
			
			// clear all previous errors
			$('.errorlist', object).remove();
			
			// show all errors
			for(var i in data.errors){
				var name = i, value = data.errors[i];
				var id = '#errors-' + name;
				$(id, object).empty().append(value);
			};
			
			// execute command
			if(data.command){
				var cmd = Commands[data.command];
				// invoke command if it's available
				if(cmd != null)
					cmd(object, data);
			}
			Helper.hideProgress(object, function(){ Helper.enableControls(object); });
			
			return false;
		};
		
		/**
		 * Make AJAX request instead <form>.
		 * @param {Object} form object instance
		 * @param {Dictionary} dictionary with custom params or null
		 */
		var makeAjaxRequest = function(object, customParams){
			var data = {};
			$(':input, :hidden', object).each(function(){
				var name = $(this).attr('name');
				/**
				var type = this.type, tag = this.tagName.toLowerCase(), name = $(this).attr('name');
				if (type == 'text' || type == 'password' || tag == 'textarea' || tag == 'hidden'){
      				data[name] = $(this).val();
				} else
				if(type == 'checkbox' || type == 'radio'){
      				data[name] = this.checked;
				}else
				if (tag == 'select'){
					// TODO: check it on select boxes
      				data[name] = $(this).val();
				}*/
				if(name == null || name == "")return true;
				data[name] = $(this).val();
			});			
						
			// add custom params
			if(customParams != null){
				for(var n in customParams){
					data[n] = customParams[n];
				}
			}
			
			var action = $(object).attr('action') == '' ? location.href : $(object).attr('action');
			
			$.ajax({
				'type' : $(object).attr('method'),
				'url' : action,
				'data' : data,
				'dataType' : 'json',
				'success' : Global.wrapFunc(receiveHandler, object),
				'error' : Global.wrapFunc(Helper.connectionError, object)
			});
		};
		
		/**
		 *  Handle SUBMIT event
		 */
		var submitHandler = function(object, event){
			if(!options.submit())return false;
			Helper.disableControls(object);
			Helper.showProgress(object);
			makeAjaxRequest(object);
			return false;
		};
		
		var options = $.extend(defaults, options);  
		/**
		 * Find all of ajax-forms and handle obsubmit event to submitHandler
		 */
		return this.each(function(){
			var that = this;
			Helper.addHiddenField(that, '__dafAjax', 'True')
			
			$(this).unbind("submit").submit(Global.wrapFunc(submitHandler, that));
			var onchangeNum = 0;
			
			// find all buttons with class .ajax-form-submit, replace it to
			// <a href="...><span style="width:..."></span></a>
			// add handler to <a>
			// generate a `disabled` class with next logic:
			// 1) remove all spaces and class name 'ajax-form-submit',
			// 2) add -disabled suffix to class name
			// It's not so simple but it's easy for generating new form submit buttons
			$(this).find('.ajax-form-submit').each(function(){
				var size_parsed =$(this).attr('name').split('-');
				var width = size_parsed[1], height = size_parsed[2];
				var id = $(this).attr('id');
				var classes = $(this).attr('class');
				
				var styleEnabled = classes.replace('ajax-form-submit', '').replace(' ', '');
				var styleDisabled =  styleEnabled + '-disabled';
							
				// add code for new button	
				$(this).replaceWith(
							'<a href="javascript:void(0);" class="ajax-visual-submit" id="' + id + '">' + 
							'<span id="' + id + '-inline" style="display:inline-block; width:' + width + 
							'px; height:' + height + 'px; overflow:hidden;" class="' + 
							$(this).attr('class') + '">&nbsp;</span></a>'
				);
				
				// create disable/enable handlers with closres
				var disable = function(){
					$('#' + id + '-inline').addClass(styleDisabled);
				};
				var enable = function(){
					$('#' + id + '-inline').removeClass(styleDisabled);
				};
				// add disable/enable functions to cache
				$('#' + id).data('enable', enable);
				$('#' + id).data('disable', disable);
				
				$('#' + id).click(function(){ $(that).submit(); });
			});
			$(this).find('.daf-onchange').each(function(){
				$(this).unbind("focus").unbind("blur");
				var control = this;
				$(this).focus(Global.wrapFunc(registerChange, onchangeNum, that, control, 500));// -> add onchange				
				$(this).blur(Global.wrapFunc(unRegisterChange, onchangeNum));// -> remove onchange				
				onchangeNum++;
				//				
			});
			// enable change request chain
			if(onchangeNum > 0){
				changeRequestChain(that);
			}
		});
	};
})(jQuery);


$(document).ready(function(){
	//$('.ajax-form').djangoAjaxForm();
});