var PROPERTIES_OPERATION_SUCCESS = 1;
var PROPERTIES_OPERATION_FAILED = 2;
var PROPERTIES_VALIDATION_ERROR = 3;

/**
 * Add events to Notifier
 */
Notifier.addEvents([
	'property_added',
	'property_edited',
	'property_removed'
]);

/**
 * Properties class
 *
 * For managing properties.
 */
var Properties = {};

// Member Variables

Properties.propertyCounts = {};

// Static Methods

/**
 * Takes a hash representing a Property and applies the proper classes to it
 * Use on, for example, JSON Property objects
 *
 * @param array propertyAddons
 * @return array
 */
Properties.objectifyPropertyAddon = function(propertyAddon) {
	propertyAddon['_fields'] = FormFields.objectifyFormFieldGroups(propertyAddon['_fields']);
	return propertyAddon;
}

/**
 * Returns the Property Counts
 *
 * @deprecated
 */
Properties.getPropertyCounts = function (property_types) {
	if (property_types != undefined) {
		propertyCounts = {};
		$A(property_types).each(function (property_type) {
			if (Properties.propertyCounts[property_type] != undefined) {
				propertyCounts[property_type] = Properties.propertyCounts[property_type];
			}
		});
		return propertyCounts;
	}
	return Properties.propertyCounts;
}

/**
 * Adds a property using AJAX
 *
 * The options parameter may contain the following callback functions
 * - onSuccess(message): Called when a property is successfully added
 * - onFailure(message): Called when a property cannot be added
 *
 * The Notifier event 'property_added' is raised if the property is successfully added
 *
 * @param array property Hash containing the property details
 * @param element form (optional) The form containing the property fields (used for highlighting erroneous fields)
 * @param array options (optional) Callbacks
 */
Properties.addProperty = function (property, form, options) {
	if (options == undefined) {
		options = {};
	}
	
	var postVars = property;
	
	new Ajax.Request ('/ajax/properties/add_property.php', {
		method: 'post',
		parameters: postVars,
		onSuccess: function(transport, json) {
			try {
				// Remove error class from fields previously marked as erroneous
				if (form) {
					$(form).select('.error').invoke('removeClassName', 'error');
				}
				try {
					// Parse response
					var data = transport.responseText.evalJSON();
				} catch (err) {
					if (handleAjaxFailure(transport, json)) {
						if (options.onFailure != undefined) {
							options.onFailure('The property could not be created.');
						}
					}
					return;
				}
				if (data.result == 'ok') {
					// Success
					Properties.propertyCounts = data.propertyCounts;
					if (options.onSuccess != undefined) {
						options.onSuccess(data.details);
					}
					Notifier.raise('property_added');
				} else {
					// Error
					var result = true;
					switch (data.status) {
						case PROPERTIES_VALIDATION_ERROR:
							// Add error class to erroneous fields
							$A(data.errorfields).each(function(item) {
								var id = FormField.generateID(item);
								if ($(id)) {
									$(id).addClassName('error');
								}
							});
							break;
						case PROPERTIES_OPERATION_FAILED:
							break;
						default:
							// Unexpected Error
							result = handleAjaxFailure(transport, json);
					}
					result = handleAjaxFailure(transport, json);
					if (result) {
						if (options.onFailure != undefined) {
							if (data.details) {
								options.onFailure(data.details);
							} else {
								options.onFailure('The property could not be created.');
							}
						}
					}
				}
			} catch (err) {
				alert ('caught error: ' + err);
			}
		},
		onFailure: function(transport, json) {
			if (handleAjaxFailure(transport, json)) {
				if (options.onFailure != undefined) {
					options.onFailure('The property could not be created.');
				}
			}
		}
	});
}

/**
 * Edits a property using AJAX
 *
 * The options parameter may contain the following callback functions
 * - onSuccess(message): Called when a property is successfully edited
 * - onFailure(message): Called when a property cannot be edited
 *
 * The Notifier event 'property_edited' is raised if the property is successfully edited
 *
 * @param array property Hash containing the property details
 * @param element form (optional) The form containing the property fields (used for highlighting erroneous fields)
 * @param array options (optional) Callbacks
 */
Properties.editProperty = function (property, form, options) {
	if (options == undefined) {
		options = {};
	}
	
	var postVars = property;
	
	new Ajax.Request ('/ajax/properties/edit_property.php', {
		method: 'post',
		parameters: postVars,
		onSuccess: function(transport, json) {
			try {
				// Remove error class from fields previously marked as erroneous
				if (form) {
					$(form).select('.error').invoke('removeClassName', 'error');
				}
				try {
					// Parse response
					var data = transport.responseText.evalJSON();
				} catch (err) {
					if (handleAjaxFailure(transport, json)) {
						if (options.onFailure != undefined) {
							options.onFailure('The property could not be edited.');
						}
					}
					return;
				}
				if (data.result == 'ok') {
					// Success
					Properties.propertyCounts = data.propertyCounts;
					if (options.onSuccess != undefined) {
						options.onSuccess(data.details);
					}
					Notifier.raise('property_edited');
				} else {
					// Error
					var result = true;
					switch (data.status) {
						case PROPERTIES_VALIDATION_ERROR:
							// Add error class to erroneous fields
							$A(data.errorfields).each(function(item) {
								var id = FormField.generateID(item);
								if ($(id)) {
									$(id).addClassName('error');
								}
							});
							break;
						case PROPERTIES_OPERATION_FAILED:
							break;
						default:
							// Unexpected Error
							result = handleAjaxFailure(transport, json);
					}
					if (result) {
						if (options.onFailure != undefined) {
							if (data.details) {
								options.onFailure(data.details);
							} else {
								options.onFailure('The property could not be edited.');
							}
						}
					}
				}
			} catch (err) {
				alert ('caught error: ' + err);
			}
		},
		onFailure: function(transport, json) {
			if (handleAjaxFailure(transport, json)) {
				if (options.onFailure != undefined) {
					options.onFailure('The property could not be edited.');
				}
			}
		}
	});
}

/**
 * Removes a property using AJAX
 *
 * The options parameter may contain the following callback functions
 * - onSuccess(message): Called when a property is successfully removed
 * - onFailure(message): Called when a property cannot be removed
 *
 * The Notifier event 'property_removed' is raised if the property is successfully removed
 *
 * @param int property_id The property_id of the property to remove
 * @param array options (optional) Callbacks
 */
Properties.removeProperty = function (property_id, options) {
	if (options == undefined) {
		options = {};
	}
	
	var postVars = {
		property_id: property_id
	}
	
	new Ajax.Request ('/ajax/properties/remove_property.php', {
		method: 'post',
		parameters: postVars,
		onSuccess: function(transport, json) {
			try {
				// Remove error class from fields previously marked as erroneous
				try {
					// Parse response
					var data = transport.responseText.evalJSON();
				} catch (err) {
					if (handleAjaxFailure(transport, json)) {
						if (options.onFailure != undefined) {
							options.onFailure('The property could not be removed.');
						}
					}
					return;
				}
				if (data.result == 'ok') {
					// Success
					Properties.propertyCounts = data.propertyCounts;
					if (options.onSuccess != undefined) {
						options.onSuccess(data.details);
					}
					Notifier.raise('property_removed');
				} else {
					// Error
					var result = true;
					switch (data.status) {
						case PROPERTIES_OPERATION_FAILED:
							break;
						default:
							// Unexpected Error
							result = handleAjaxFailure(transport, json);
					}
					if (result) {
						if (options.onFailure != undefined) {
							if (data.details) {
								options.onFailure(data.details);
							} else {
								options.onFailure('The property could not be removed.');
							}
						}
					}
				}
			} catch (err) {
				alert ('caught error: ' + err);
			}
		},
		onFailure: function(transport, json) {
			if (handleAjaxFailure(transport, json)) {
				if (options.onFailure != undefined) {
					options.onFailure('The property could not be removed.');
				}
			}
		}
	});
}

Properties.saveAvailability = function(cCalendars, options) {
	if (options == undefined) {
		options = {};
	}
	
	var postVars = {};
	var availability = [];
	for (var key in cCalendars) {
		var availability = cCalendars[key].serialize({
			modified: true,
			states: {
				unavailable: true,
				available: true,
				pending: true,
				booked: true
			}
		});
		for (var key2 in availability) {
			for (var key3 in availability[key2]) {
				if (typeof(availability[key2][key3]) == 'object') {
					for (var key4 in availability[key2][key3]) {
						postVars['properties[' + cCalendars[key].property._fieldvalues.reference + '][' + cCalendars[key].room_number + '][' + key2 + '][' + key3 + '][' + key4 + ']'] = availability[key2][key3][key4];
					}
				} else {
					postVars['properties[' + cCalendars[key].property._fieldvalues.reference + '][' + cCalendars[key].room_number + '][' + key2 + '][' + key3 + ']'] = availability[key2][key3];
				}
			}
		}
	}
	
	new Ajax.Request('/ajax/properties/edit_availability.php', {
		method: 'post',
		parameters: $H(postVars).toQueryString(),
		onSuccess: function(transport, json) {
			try {
				// Parse response
				var data = transport.responseText.evalJSON();
			} catch (err) {
				if (handleAjaxFailure(transport, json)) {
					$('debug').innerHTML = transport.responseText;
					if (options.onFailure != undefined) {
						options.onFailure('Sorry, your changes could not be saved.<br />Please try again in a moment.');
					}
				}
				return;
			}
			if (data.result == 'ok') {
				// Success
				try {
					if (data.properties != undefined) {
						for (var property_ref in data.properties) {
							for (var date in data.properties[property_ref]) {
								cCalendars[property_ref].dates[date].setBookingInfo(data.properties[property_ref][date]);
							}
						}
					}
				} catch (err) {
					alert (dump(err));
				}
				for (var key in cCalendars) {
					cCalendars[key].resetModified();
				}
				if (options.onSuccess != undefined) {
					options.onSuccess(data);
				}
			} else {
				$('debug').innerHTML = dump(data);
				if (options.onFailure != undefined) {
					options.onFailure('Sorry, your changes could not be saved.<br />Please try again in a moment.');
				}
			}
		},
		onFailure: function(transport) {
			if (options.onFailure != undefined) {
				options.onFailure('Sorry, your changes could not be saved.<br />Please try again in a moment.');
			}
		}
	});	
}

Properties.validatePropertyForm = function (form, options) {
	return FormFields.validateForm(form, options);
}

Properties.renderImages = function(container, options) {
	if (!$(container)) {
		return;
	}
	if (options == undefined) {
		options = {};
	}
	if (options.imageList == undefined) {
		return;
	}
	if (options.room_number == undefined) {
		options.room_number = 0;
	}
	if (options.images == undefined) {
		options.images = [];
	}
	
	var imageList = $(options.imageList);
	
	for (var i = 0; i < options.images.length; i++) {
		options.image = options.images[i];
		Properties.renderImage(imageList, options);
	}
	
	var addList = createExtendedElement('ul', {
		parent: container
	});
	
	Properties.renderAddImage(addList, options);
	
	createExtendedElement('div', {
		className: 'clear',
		parent: container
	});
	
	Properties.makeImagesSortable(imageList, options);
}

Properties.makeImagesSortable = function(container, options) {
	Threads.queue(function() {
		try {
			Sortable.create(container, {
				handle: '.property_image',
				constraint: false,
				onUpdate: function() {
					Properties.orderImages(options.property_id, {
						room_number: options.room_number,
						images: Sortable.sequence(container),
						onSuccess: function(message) {
							Dialog.closeInfo();
						},
						onFailure: function(message) {
							Dialog.closeInfo();
							if (message) {
								Dialog.alert(message, {
									className: 'cityred',
									width: 300
								});
							}
						}
					});
				}
			});
		} catch (err) {
			//alert (dump(err));
		}
	});
}

Properties.renderImage = function(container, options) {
	if (!$(container)) {
		return;
	}
	if (options == undefined) {
		options = {};
	}
	if (options.room_number == undefined) {
		options.room_number = 0;
	}
	if (options.image == undefined) {
		return;
	}
	
	var imageContainer = createExtendedElement('li', {
		id: 'imagelist_' + options.image.image_id,
		className: 'image',
		parent: container
	});
	
	var image = createExtendedElement('img', {
		className: 'property_image',
		src: options.image_path + options.image.filename,
		parent: imageContainer
	});
	
	image.observe('mousedown', function(event) {
		if (Event.isLeftClick(event)) {
			imageContainer.addClassName('dragging');
		}
	});
	image.observe('mouseup', function(event) {
		imageContainer.removeClassName('dragging');
	});
	
	Properties.renderRemoveButton(imageContainer, options.image.image_id, options);
}

Properties.renderRemoveButton = function(container, image_id, options) {
	if (options.room_number == undefined) {
		options.room_number = 0;
	}
	var removeButton = createExtendedElement('img', {
		className: 'remove',
		src: '/images/button-delete.png',
		parent: container
	});
	removeButton.observe('mouseover', function(event) {
		removeButton.src = '/images/button-delete-hover.png';
	});
	removeButton.observe('mouseout', function(event) {
		removeButton.src = '/images/button-delete.png';
	});
	removeButton.observe('click', function(event) {
		Dialog.confirm('Are you sure you want to remove this image?', {
			className: 'cityred',
			width: 300,
			okLabel: 'Yes',
			cancelLabel: 'No',
			onOk: function(win) {
				Dialog.info('Removing image...<br /><br /><img src=\'/images/loading.gif\' alt=\'\' />', {
					className: 'cityred',
					width: 300,
					hideEffect: Element.hide
				});
				Properties.removeImage(options.property_id, image_id, options.room_number, {
					onSuccess: function(message) {
						Dialog.closeInfo();
						container.remove();
					},
					onFailure: function(message) {
						Dialog.closeInfo();
						if (message) {
							Dialog.alert(message, {
								className: 'cityred',
								width: 300
							});
						}
					}
				});
				return true;
			}
		});
	});
	container.observe('mouseover', function(event) {
		if (!removeButton.visible()) {
			Effect.Queues.get('removeButtons').each(function(effect) {
				effect.cancel();
			});
			removeButton.appear({
				queue: {
					scope: 'removeButtons'
				},
				duration: 0.25
			});
		}
	});
	container.observe('mouseout', function(event) {
		var targetElement = event.relatedTarget || event.toElement;
		if ($(targetElement)) {
			if (!$(targetElement).descendantOf(container)) {
				Effect.Queues.get('removeButtons').each(function(effect) {
					effect.cancel();
				});
				removeButton.hide();
			}
		} else {
			Effect.Queues.get('removeButtons').each(function(effect) {
				effect.cancel();
			});
			removeButton.hide();
		}
	});
	
	removeButton.hide();
	
	return removeButton;
}

Properties.addImageCount = 0;
Properties.uploadingImages = {};
Properties.renderAddImage = function(container, options) {
	if (!$(container)) {
		return;
	}
	if (options == undefined) {
		options = {};
	}
	if (options.room_number == undefined) {
		options.room_number = 0;
	}
	
	var imageContainer = createExtendedElement('li', {
		className: 'add',
		parent: container
	});
	
	var addButton = createExtendedElement('img', {
		className: 'add',
		src: '/images/button-add.png',
		parent: imageContainer
	});
	
	imageContainer.observe('mouseover', function(event) {
		addButton.src = '/images/button-add-hover.png';
	});
	imageContainer.observe('mouseout', function(event) {
		var targetElement = event.relatedTarget || event.toElement;
		if ($(targetElement)) {
			if (!$(targetElement).descendantOf(imageContainer)) {
				addButton.src = '/images/button-add.png';
			}
		} else {
			addButton.src = '/images/button-add.png';
		}
	});
	imageContainer.observe('click', function(event) {
		Properties.addImageCount++;
		
		var iframe = createExtendedElement('iframe', {
			id: 'add_image_iframe_' + Properties.addImageCount,
			name: 'add_image_iframe_' + Properties.addImageCount,
			src: '#',
			parent: document.body
		});
		iframe.style.width = 800;
		iframe.style.height = 800;
		iframe.style.border = 'none';
		
		Dialog.confirm(
			'<div id="addimage_' + Properties.addImageCount + '"></div>'
			, {
			className: 'cityred',
			width: 300,
			height: 105,
			okLabel: 'Upload',
			cancelLabel: 'Cancel',
			onBeforeShow: function(win) {
				var messageContainer = $('addimage_' + Properties.addImageCount);
				messageContainer.innerHTML = 'Add an Image<br /><br />' +
			'	<form id="addimage_form_' + Properties.addImageCount + '" action="ajax/properties/upload_image.php" method="post" enctype="multipart/form-data" target="add_image_iframe_' + Properties.addImageCount + '" onsubmit="return false;">' +
			'		<input type="hidden" name="property_id" value="' + options.property_id + '" />' +
			'		<input type="hidden" name="room_number" value="' + options.room_number + '" />' +
			'		<input type="hidden" name="upload_id" value="' + Properties.addImageCount + '" />' +
			'		<input type="file" name="image" />' +
			'	</form>'
			},
			onOk: (function(win) {
				$('addimage_form_' + Properties.addImageCount).submit();
				Properties.uploadingImages['i' + Properties.addImageCount] = options;
				options.upload_id = Properties.addImageCount;
				this.renderLoadingImage(container, options);
				return true;
			}).bind(this),
			onCancel: function(win) {
				iframe.remove();
				return;
			}
		});
	}.bindAsEventListener(this));
}

Properties.renderLoadingImage = function(container, options) {
	if (!$(container)) {
		return;
	}
	if (options == undefined) {
		options = {};
	}
	
	var imageContainer = createExtendedElement('li', {
		id: 'loading_image_' + options.upload_id,
		className: 'loading',
		parent: container
	});
	
	var addButton = createExtendedElement('img', {
		className: 'loading',
		src: '/images/uploading.gif',
		parent: imageContainer
	});
	
	$(container).insert({
		top: imageContainer
	});
}

Properties.addImageComplete = function(data) {
	//alert(dump(data));
	
	var options = Properties.uploadingImages['i' + data.upload_id]
	
	options.image = data.image;
	$('loading_image_' + data.upload_id).remove();
	Properties.renderImage(options.imageList, options);
	
	Properties.makeImagesSortable(options.imageList, options);
	
	$('add_image_iframe_' + data.upload_id).remove();
}

Properties.removeImage = function(property_id, image_id, room_number, options) {
	if (room_number == undefined) {
		room_number = 0;
	}
	if (options == undefined) {
		options = {};
	}
	
	var postVars = {
		property_id: property_id,
		room_number: room_number,
		image_id: image_id
	}
	
	new Ajax.Request ('/ajax/properties/remove_image.php', {
		method: 'post',
		parameters: postVars,
		onSuccess: function(transport, json) {
			try {
				// Remove error class from fields previously marked as erroneous
				try {
					// Parse response
					var data = transport.responseText.evalJSON();
				} catch (err) {
					if (handleAjaxFailure(transport, json)) {
						if (options.onFailure != undefined) {
							options.onFailure('The image could not be removed.');
						}
					}
					return;
				}
				if (data.result == 'ok') {
					// Success
					if (options.onSuccess != undefined) {
						options.onSuccess(data.details);
					}
				} else {
					// Error
					if (options.onFailure != undefined) {
						if (data.details) {
							options.onFailure(data.details);
						} else {
							options.onFailure('The image could not be removed.');
						}
					}
				}
			} catch (err) {
				alert ('caught error: ' + err);
			}
		},
		onFailure: function(transport, json) {
			if (handleAjaxFailure(transport, json)) {
				if (options.onFailure != undefined) {
					options.onFailure('The image could not be removed.');
				}
			}
		}
	});
}

Properties.orderImages = function(property_id, options) {
	if (options == undefined) {
		options = {};
	}
	if (options.room_number == undefined) {
		options.room_number = 0;
	}
	
	var postVars = {
		property_id: property_id,
		room_number: options.room_number,
		'images[]': options.images
	}
	
	new Ajax.Request ('/ajax/properties/order_images.php', {
		method: 'post',
		parameters: postVars,
		onSuccess: function(transport, json) {
			try {
				// Remove error class from fields previously marked as erroneous
				try {
					// Parse response
					var data = transport.responseText.evalJSON();
				} catch (err) {
					if (handleAjaxFailure(transport, json)) {
						if (options.onFailure != undefined) {
							options.onFailure('The images could not be reordered.');
						}
					}
					return;
				}
				if (data.result == 'ok') {
					// Success
					if (options.onSuccess != undefined) {
						options.onSuccess(data.details);
					}
				} else {
					// Error
					if (options.onFailure != undefined) {
						if (data.details) {
							options.onFailure(data.details);
						} else {
							options.onFailure('The images could not be reordered.');
						}
					}
				}
			} catch (err) {
				alert ('caught error: ' + err);
			}
		},
		onFailure: function(transport, json) {
			if (handleAjaxFailure(transport, json)) {
				if (options.onFailure != undefined) {
					options.onFailure('The images could not be reordered.');
				}
			}
		}
	});
}

Properties.reserveReference = function(options) {
	if (options == undefined) {
		options = {};
	}
	if (options.reference == undefined) {
		options.reference = '';
	}
	if (options.source == undefined) {
		options.source = 'edit_property';
	}
	
	if (options.showLoading != undefined) {
		options.showLoading();
	}
	
	new Ajax.Request('/ajax/bookings/reserve_reference.php', {
		method: 'post',
		parameters: {
			reference: options.reference,
			source: options.source
		},
		onSuccess: function(transport, json) {
			try {
				// Parse response
				var data = transport.responseText.evalJSON();
			} catch (err) {
				if(options.onFailure != undefined) {
					options.onFailure('Sorry, a booking reference could not be requested. Please try again in a moment.');
				}
				return;
			}
			if (data.result == 'ok') {
				// Success
				options.onSuccess(data.reference);
			} else {
				// Error
				if(options.onFailure != undefined) {
					options.onFailure('Sorry, a booking reference could not be requested. Please try again in a moment.');
				}
				return;
			}
		},
		onFailure: function(transport) {
			if(options.onFailure != undefined) {
				options.onFailure('Sorry, a booking reference could not be requested. Please try again in a moment.');
			}
		}
	});
}