/*
* Copyright (c) 2009-2010 ATMAIL. All rights reserved
* See http://atmail.com/license.php for license agreement
*
* @package Atmail
* @subpackage Quickview
* @copyright Copyright (C) 2011 Atmail inc. All rights reserved.
* @author Atmail
*
*/
(function($)
{
	var quickviewNS = function(element, userOptions)
	{	
		// ***** default options *****
		var options = $.extend(
		{
			eventHeight: 65,
			calendarHrefs : {},
			loadingText : jsTranslate('Loading'),
			ellipsisText : "...",
			noEventsText : jsTranslate('No Upcoming Events'),
			loadingSpriteSettings : {
				fps : 8,
				no_of_frames : 8
			},
			timezone: 0
		}, userOptions);

		// ***** private variables *****
		var events =
		{
			inView: Math.ceil(element.height() / options.eventHeight),
			total: 0,
			cache: []
		};

		var eventListSelector = undefined;
		var listTimeout = false;
		var toggleCount = 0;

		// ***** private methods *****
		var privateMethods =
		{
			initalize : function()
			{
				// first init the container
				if(!element.find('#eventList').length)
				{
					element.append('<div id="eventList"></div>');
				}

				eventListSelector = element.find('#eventList');
				element.unbind('scroll').bind("scroll", privateMethods.handleScroll);
			}, // initalize
			handleScroll : function()
			{
				if($(this).data('scrollDisabled'))
				{
					return;
				}

				var lastScrollPos = $(this).data('lastScrollPos');
				var currentScrollPos = privateMethods.getListTopPosition($(this));

				if(lastScrollPos == undefined)
				{
					lastScrollPos = 0;
				}

				if(currentScrollPos == lastScrollPos) return;

				$(this).data('lastScrollPos', currentScrollPos);
				if( currentScrollPos + events.inView < events.cache.length)
				{
					// the scroll doesn't contain unfetched events
					return;
				}

				var scrollTimeoutFunction = function(_this)
				{
					return function()
					{
						this.context = _this;
						var callback = privateMethods.process({
							refreshAll: false,
							fetchTotal: true,
							fetchEvents: true
						});
						callback.call(this,
						{
							total: events.total
						});
					};
				};

				if (listTimeout)
				{
					clearTimeout(listTimeout);
				}
				listTimeout = window.setTimeout(scrollTimeoutFunction($(this)), 500);
			},
			toggleLoader : function(show)
			{
				if(show)
				{
					toggleCount++;
				}
				else
				{
					toggleCount--;
				}

				if(toggleCount < 0) toggleCount = 0;

				if(toggleCount)
				{
					this.data('scrollDisabled', true);
					this.block(
					{
						message: "<div id='loading-text'><span class='loading'></span></div>",
						overlayCSS:
						{
							backgroundColor: 'transparent',
							opacity: 0.4
						},
						css:
						{
							backgroundColor: 'transparent',
							color: '#575757',
							border: '0'
						}
					});
					this.find('#loading-text').sprite(options.loadingSpriteSettings);
				}
				else
				{
					this.unblock();
					this.find("#loading-text").destroy();
					this.find("#loading-text").remove();
					this.data('scrollDisabled', false);
				}
			}, // toggleLoader
			getListTopPosition: function(context)
			{
				return Math.ceil(context.scrollTop() / options.eventHeight);
			},
			displayPlaceHolders: function()
			{
				var totalPlaceHolders = events.total - events.cache.length;
				var currentPlaceHolders = eventListSelector.find('#event').length;
				
				if(totalPlaceHolders > currentPlaceHolders)
				{
					for(var a=0;a<(totalPlaceHolders - currentPlaceHolders);a++)
					{
						// placeholder for event thats about to load
						eventListSelector.append("<div id=\"event\" class=\"eventListItem\" style=\"line-height: 64px; width: 100%; height: " + (options.eventHeight - 1) + "px; text-align: center;\">" + options.loadingText + options.ellipsisText + "</div>");
					}

				// we've added placeholder(s) so lets check that the total events in this slice still match
				}
				else if (currentPlaceHolders > totalPlaceHolders )
				{
					var blankEventSelectors = eventListSelector.find("#event");
					if(blankEventSelectors.length)
					{
						for(var a=totalPlaceHolders;a<currentPlaceHolders;a++)
						{
							// placeholder for event thats about to load
							blankEventSelectors.eq(0).remove();
						}
					}
				}

				var currentEventsSelector = eventListSelector.find('.eventListItem:not(#event)')
				var currentEventsLength = currentEventsSelector.length;

				if(currentEventsLength != events.cache.length)
				{
					// prune the total events to match the cache
					if( currentEventsLength > events.cache.length )
					{
						// prune from the bottom up
						for(var a=events.cache.length;a<currentEventsLength;a++)
						{
							currentEventsSelector.eq(currentEventsSelector.length - 1).remove();
						}
					}
				}
			},
			displayBlank: function()
			{
				// ***** test overall amount of events
				if(!events.total)
				{
					// no events to display, ensure container cleared and notice displayed
					eventListSelector.html('<center id="blankevent" class="eventListItem" style="cursor: normal;">' + options.noEventsText + '</center>');
					return true;
				}
				else
				{
					var blankEventSelector = eventListSelector.find('#blankevent');
					if(blankEventSelector.length)
					{
						blankEventSelector.remove();
					}
				}

				return false;
			},
			displayEvents: function(startPosition, endPosition)
			{
				if(privateMethods.displayBlank()) return;
				if(endPosition > events.total) endPosition = events.total;
				for(var i = startPosition; i < endPosition; i++)
				{
					var currentEvent = events.cache[i];
					var currentEventType = typeof(currentEvent);
					if(currentEventType === "object")
					{
						// real event, attempt replacement
						var eventSelector = eventListSelector.children("#event" + currentEvent.domId);

						if( !eventSelector.length )
						{
							// no existing event, lets try and find the blank event holder
							eventSelector = eventListSelector.children("#event");
							if(!eventSelector.length)
							{
								// no blank selector
								// placeholder for event thats about to load
								eventListSelector.append("<div id=\"event\" class=\"eventListItem\" style=\"line-height: 64px; width: 100%; height: " + (options.eventHeight - 1) + "px; text-align: center;\">" + options.loadingText + options.ellipsisText + "</div>");
								eventSelector = eventListSelector.children("#event");							
							}

							// grab the last selector available, so we dont upset the order
							eventSelector = eventSelector.eq(eventSelector.length - 1);
						}
						eventSelector.replaceWith(
							"<div id=\"event" + currentEvent.domId + "\" class=\"eventListItem\" style=\"width: 100%; height: " + (options.eventHeight - 1) + "px;\">" +
							"<div class=\"checkboxHolder\" style=\"top: 15px; left: 15px;\">" +
							"<div class=\"checkboxBackground" + ( currentEvent.recurring ? ' fc-event-recurring' : '' ) + "\" style=\"background-color: " + currentEvent.color + ";\">" +
							"</div>" +
							"<div class=\"checkboxFrame\"></div>" +
							"</div>" +
							"<div class=\"eventListItemTitle\">" + htmlentities(currentEvent.title) + "</div>" +
							"<div class=\"eventListItemTime\">" + (currentEvent.allday ? "" : currentEvent.time) + "</div>" +
							"<div class=\"eventListItemDate\">" + currentEvent.date + "</div>" +
							"</div>");
						// reselect new dom object
						eventSelector = eventListSelector.children("#event" + currentEvent.domId);
						eventSelector.data("id", currentEvent.id);
						eventSelector.data("relative_href", currentEvent.relative_href);
						eventSelector.click(function() {
							displayPopup($(this).data("id"), $(this).data("relative_href"), false, $(this));
						});

						// make sure its in the correct location
						//eventListSelector.eq(i).insertAfter(eventSelector);
						if(eventListSelector.children().eq(i).attr('id') != eventSelector.attr('id'))
						{
							eventListSelector.children().eq(i).before(eventSelector);
						}
					}
				}

				// ensure we have the correct count of loaded items
				privateMethods.displayPlaceHolders();
			}, // displayEvents
			process: function(userOptions)
			{
				return function(json)
				{
					var startPosition = privateMethods.getListTopPosition(this.context);
					var eventsToLoad = events.inView;

					// update the total amount of events
					events.total = json['total'];
					if(userOptions.refreshAll)
					{
						// we are updating all already fetched events
						startPosition = 0;
						eventsToLoad = events.cache.length + userOptions.forceAdd;
						if(eventsToLoad < events.inView) eventsToLoad = events.inView;
						if(userOptions.clearCache)
						{
							delete events.cache;
							events.cache = [];
						}
					}

					if(!eventsToLoad)
					{
						// nothing to display, update display and finish
						privateMethods.displayEvents.call(this.context);
						privateMethods.toggleLoader.call(this.context, false);
						return;
					}

					privateMethods.displayEvents.call(this.context);

					// calculate position and total to load for amount in view
					if(userOptions.fetchEvents)
					{
						var postData =
						{
							func: 'ajaxcal',
							json: '1',
							start: Math.round(utcDate(new Date()).getTime() / 1000),
							position: startPosition,
							length: eventsToLoad,
							calendarhref: options.calendarHrefs.join()
						}
						$.ajax({
							context: this.context,
							type: "POST",
							data: postData,
							url: com.atmail.calendar.baseUrl + "/index.php/mail/calendar/caldavglue",
							dataType: "json",
							success: function(json)
							{
								var eventId = startPosition;
								for (var i in json)
								{
									var eventStart = parseDate(json[i]["start"], false, options.timezone);
									var item = json[i];
									events.cache[eventId++] =
									{
										id: item["id"],
                                        domId: escapeExpression(item["id"]),
										title: item["title"],
										color: item["color"],
										relative_href: item["relative_href"],
										date: eventStart.getLongDate(),
										time: ((eventStart.getHours() > 12) ? eventStart.getHours() - 12 : ((eventStart.getHours() == 0) ? 12 : eventStart.getHours())) + ((eventStart.getMinutes() < 10) ? ((eventStart.getMinutes() > 0) ? ":0" + eventStart.getMinutes() : "") : ":" + eventStart.getMinutes()) + " " + ((eventStart.getHours() < 12) ? "AM" : "PM"),
										allday: item["allDay"],
										recurring: (item["id"].indexOf('_') != -1 ? true : false)
									};
								}

								privateMethods.toggleLoader.call(this.context, false);
								privateMethods.displayEvents.call(this.context, startPosition, (userOptions.refreshAll ? events.cache.length : startPosition + events.inView));
							}
						});
					}
					else
					{
						privateMethods.toggleLoader.call(this.context, false);
						privateMethods.displayEvents.call(this.context, startPosition, (userOptions.refreshAll ? events.cache.length : startPosition + events.inView));
					}
				}; // magic wrapper
			} // process
		}; // privateMethods
	
		// ***** public methods *****
		this.refresh = function(userOptionsIn)
		{
			var userOptions = $.extend({
				refreshAll: false,
				fetchEvents: true,
				fetchTotal: true,
				forceAdd: 0,
				calendarHrefs: undefined,
				clearCache: false
			}, userOptionsIn);

			if(userOptions.calendarHrefs != undefined)
			{
				options.calendarHrefs = userOptions.calendarHrefs;
			}

			// show the loader
			privateMethods.toggleLoader.call(element, true);

			if(userOptions.fetchTotal)
			{
				var postData =
				{
					func: 'total',
					start: Math.round(utcDate(new Date()).getTime() / 1000),
					calendarhref : options.calendarHrefs.join()
				};

				$.ajax({
					context: element,
					type: "POST",
					url: com.atmail.calendar.baseUrl + '/index.php/mail/calendar/caldavglue',
					data: postData,
					dataType: "json",
					success: privateMethods.process(userOptions)
				});
			}
			else
			{
				this.context = element;
				var callback = privateMethods.process(userOptions);
				callback.call(this,
				{
					total: events.total
				});
			}
		};

		this.remove = function(removeOptions)
		{
			var eventsToRemove = {};

			if( typeof(removeOptions.id) != 'undefined')
			{
				// removing a single event
				eventsToRemove = eventListSelector.find('#event' + escapeExpression(removeOptions.id));
			}
			else if( typeof(removeOptions.relativeHref) != 'undefined' )
			{
				// removing events based on calendar href
				eventsToRemove = eventListSelector.find(".eventListItem").filter(function() {
					return $(this).data('relative_href') == removeOptions.relativeHref;
				} );
				for(var z in options.calendarHrefs)
				{
					if(options.calendarHrefs[z] == removeOptions.relativeHref)
					{
						options.calendarHrefs.splice(z, 1);
						break;
					}
				}
			}
		
			if(!eventsToRemove.length)
			{
				// can't find the event
				// its not loaded, so lets nuke a total event
				events.total--;
			}

			if (eventsToRemove.length)
			{
				eventsToRemove.each(function()
				{
					id = $(this).data('id');
					events.cache = $.grep(events.cache, function(val) { return val.id != id; });
					events.total--;
					$(this).remove();
				});
			}
			this.refresh({
				fetchTotal: false,
				fetchEvents: true
			});
		};

		// ***** object construction *****
		privateMethods.initalize();
	};

	$.fn.quickview = function(userOptions)
	{
		// support mutltiple elements
		return this.each(function()
		{
			var element = $(this);
			if(element.data('quickview')) return;
			var quickview = new quickviewNS($(this), userOptions);
			element.data('quickview', quickview);
		});
	};
})( jQuery ); 
