/**
 * Custom JS script loaded on admin and frontend pages
 *
 * @package   GravityView-Calendar-Ext
 * @license   GPL2+
 * @author    Katz Web Services, Inc.
 * @link      https://www.gravitykit.com
 * @copyright Copyright 2019, Katz Web Services, Inc.
 *
 * globals jQuery
 */

var gvFullCalendar = {
	init: function () {
		if ( !window.gvCalendarData ) {
			return;
		}

		var calendars = document.getElementsByClassName( 'gv-fullcalendar' );

		for ( var x = 0; x < calendars.length; x++ ) {
			var feed_id = calendars[ x ].dataset.feed_id;
			var calendar_id = calendars[ x ].dataset.calendar_id;
			var options = window.gvCalendarData[ feed_id ];

			gvFullCalendar.initSingle( calendar_id, calendars[ x ], options, true );
		}

		jQuery( window ).on( 'gravityview-datatables/event/preXhr', function ( e, options ) {
			jQuery( '.gv-fullcalendar[data-view_id="' + options.data.view_id + '"]' ).each( function ( i, el ) {
				var calendar_id = el.dataset.calendar_id;
				var calendar = gvFullCalendar.getCalendar( calendar_id );

				// Do not show loader if Calendar is configured to display all events (i.e., it's not tied to DataTables)
				if ( !calendar || ( calendar.extraOptions.ajax_params && calendar.extraOptions.ajax_params.display === 'all' ) ) {
					return;
				}

				gvFullCalendar.toggleCalendarLoader( true, calendar );
			} );
		} );

		jQuery( window ).on( 'gravityview-datatables/event/draw', function ( e, data ) {
			var widgets_data = data.settings.json.calendar_widgets_data;
			var datatables_data = data.settings.json.data;

			if ( !widgets_data ) {
				return;
			}

			jQuery.each( widgets_data, function ( feed_id ) {
				jQuery( '.gv-fullcalendar[data-feed_id="' + feed_id + '"]' ).each( function ( i, el ) {
					var calendar_id = el.dataset.calendar_id;
					var calendar = gvFullCalendar.getCalendar( calendar_id );

					if ( !calendar ) {
						return;
					}

					var calendar_events = widgets_data[ feed_id ];

					var event_sources = calendar.instance.getEventSources();

					if ( event_sources.length ) {
						event_sources[ 0 ].remove();
					}

					if ( calendar_events.length && datatables_data.length ) {
						calendar.instance.addEventSource( calendar_events );

						gvFullCalendar.navigateToEvents( calendar_id, null, calendar_events );
					} else {
						calendar.instance.addEventSource( [] );

						calendar.instance.changeView( calendar.instance.getOption( 'initialView' ), new Date() );
					}

					gvFullCalendar.toggleCalendarLoader( false, calendar );
				} );
			} );
		} );
	},

	initSingle: function ( calendar_id, calendar_element, settings, edit_event ) {
		window.gvCalendar = window.gvCalendar || [];

		// Already processed this ID
		if ( gvFullCalendar.getCalendar( calendar_id ) ) {
			return;
		} else {
			window.gvCalendar[ calendar_id ] = {};
		}

		var calendar_options = settings.calendar_options;
		var extra_options = settings.extra_options;

		if ( edit_event ) {
			calendar_options.eventDrop = function ( info ) {
				gvFullCalendar.edit_event( info );
			};
			calendar_options.eventResize = function ( info ) {
				gvFullCalendar.edit_event( info );
			};
		}

		if ( extra_options.dynamic_events_loading ) {
			calendar_options.loading = function ( isLoading ) {
				var calendar = gvFullCalendar.getCalendar( calendar_id );

				if ( !calendar || calendar.extraOptions.loader !== true ) {
					return;
				}

				gvFullCalendar.toggleCalendarLoader( isLoading, calendar );
			}

			calendar_options.events = jQuery.debounce( 700, function ( data, success, failure ) {
				let start = new Date( data.startStr );
				let end = new Date( data.endStr );

				start.setDate( start.getDate() - ( calendar_options.start_date_offset ?? 14 ) );
				end.setDate( end.getDate() + ( calendar_options.end_date_offset ?? 14 ) );

				var data = {
					_nonce: extra_options._nonce,
					start: start.toISOString(),
					end: end.toISOString(),
					calendar_id: extra_options.feed_id,
					url_query: window.location.search.substring( 1 )
				};

				jQuery.ajax( {
					dataType: 'json',
					url: extra_options.ajax_url,
					data: Object.assign( {}, data, extra_options.ajax_params ),
					success: function ( events ) {
						var calendar = gvFullCalendar.getCalendar( calendar_id );

						if ( !calendar ) {
							return;
						}

						// If navigation option is set, navigate to them on initial render
						if ( calendar.extraOptions.navigateToEvents === 'current' || calendar.extraOptions.navigationToEventsCompleted ) {
							return success( events )
						}

						calendar.extraOptions.navigationToEventsCompleted = true;

						gvFullCalendar.navigateToEvents( calendar_id, null, events );

						success( events )
					}
				} );
			} );
		}

		calendar_options.eventContent = function ( data ) {
			if ( data.event.extendedProps.hasOwnProperty( 'eventSourceHtml' ) ) {
				var $el = jQuery(document.createElement('div'));
				$el.html( data.event.extendedProps.eventSourceHtml );
				// Update the time text in the cached HTML block.
				$el.find('.fc-event-time').html( data.timeText );

				return { html: $el.html() };
			}
		}

		calendar_options.eventDidMount = function ( data ) {
			if ( !data.event.extendedProps.hasOwnProperty( 'eventSourceHtml' ) ) {
				// Decode HTML entities in event title, update DOM and save the result as event property
				var title = gvFullCalendar.processHtmlContent( data.event.title, extra_options.allow_html_content );

				jQuery( '.fc-event-title', data.el ).html( title );

				data.event.setExtendedProp( 'eventSourceHtml', jQuery( data.el ).html() );
			}

			if ( extra_options.tooltip === false || !window.gv_calendar_tippy ) {
				return
			}

			var tooltip_options = {
				content: gvFullCalendar.processHtmlContent( data.event.extendedProps.description, extra_options.allow_html_content ),
				placement: 'top',
				trigger: 'mouseenter focus',
				theme: 'gv-calendar',
				allowHTML: true,
				interactive: true,
			}

			tooltip_options = extra_options.tooltip_options ? Object.assign( {}, tooltip_options, extra_options.tooltip_options ) : tooltip_options;

			if ( tooltip_options.content.trim() === '' && !tooltip_options.allowEmptyTooltip ) {
				return;
			}

            gv_calendar_tippy( jQuery( data.el ).hasClass( 'fc-timegrid-event' ) ? jQuery( data.el ).parent()[ 0 ] : data.el, tooltip_options );
		}

		// iCal Feeds.
		if ( extra_options.ical_feeds.length > 0 ) {

			// Initialize eventSources if not already done.
			calendar_options.eventSources = calendar_options.eventSources || [];

			// Process each feed array using jQuery's each function.
			for (var i = 0; i < extra_options.ical_feeds.length; i++) {
				var feed = extra_options.ical_feeds[ i ];
				var index = i;

				// Make sure the feed has a URL.
				if ( ! feed.hasOwnProperty( 'url' ) ) {
					console.error( 'Calendar #' + extra_options.feed_id + ' contains a malformed feed.');
					continue;
				}

				var feed_url = feed.url.trim();

				/**
				 * We need to proxy the feed through our own site to avoid CORS issues.
				 * @see \GV_Extension_Calendar_Feed::proxy_icalendar_feed().
				 */
				if ( ! feed_url.includes( extra_options.site_url ) ) {
					feed_url = extra_options.calendar_rest_url + 'feeds/url/' + extra_options.feed_id + '?index=' + index;
				}

				// Add the new event source for this URL.
				calendar_options.eventSources.push( {
					'url': feed_url,
					'format': feed.format ?? 'ics',
					'color': feed.color,
				} );
			}
		}

		var calendar = window.gvCalendar[ calendar_id ] = {
			instance: new FullCalendar.Calendar( calendar_element, calendar_options ),
			extraOptions: extra_options
		};

		var finishInit = function () {
			calendar.instance.render();

			if ( calendar.extraOptions.loader === true ) {
				jQuery( calendar.instance.el ).find( '.fc-view-harness' ).prepend( jQuery( '<div class="gv-fullcalendar-loader"></div>' ) );
			}

			gvFullCalendar.navigateToEvents( calendar_id );

			jQuery( document ).trigger( 'gv_calendar_rendered', calendar );
		};

		// Load locale before rendering calendar to avoid FOC (buttons translations are located in the external locale script)
		if ( calendar_options.locale && calendar_options.locale !== 'en' ) {
			gvFullCalendar.loadLocale( calendar_options.locale, calendar_id, function () {
				calendar.instance.setOption( 'locales', [ calendar_options.locale ] );
				calendar.instance.setOption( 'locale', calendar_options.locale );
				finishInit();
			} );
		} else {
			finishInit();
		}
	},


	navigateToEvents: function ( calendar_id, new_navigate_option, events ) {
		// Navigate to past, current or future event date based on the `navigateToEvents` calendar setting
		var calendar = gvFullCalendar.getCalendar( calendar_id );

		if ( !calendar ) {
			return;
		}

		var past_events = [];
		var current_events = [];
		var future_events = [];
		var events = events || calendar.instance.getOption( 'events' );
		var default_view = calendar.instance.getOption( 'initialView' );
		var navigate_option = new_navigate_option || calendar.extraOptions.navigateToEvents;

		if ( !events ) {
			return;
		}

		if ( !navigate_option || navigate_option === 'current' ) {
			if ( new_navigate_option && new_navigate_option !== navigate_option ) {
				calendar.instance.changeView( default_view, new Date() );
			}

			return;
		}
		var calendar_view_start_date = Date.parse( calendar.instance.view.currentStart );
		var calendar_view_end_date = Date.parse( calendar.instance.view.currentEnd );

		jQuery.each( events, function ( i, event ) {
			var event_start_date = Date.parse( event.start );

			if ( event_start_date < calendar_view_start_date ) {
				past_events.push( event );
			} else if ( event_start_date >= calendar_view_start_date && event_start_date <= calendar_view_end_date ) {
				current_events.push( event );
			} else {
				future_events.push( event );
			}
		} );

		var compare_events = function ( a, b ) {
			if ( a.start < b.start ) {
				return -1;
			}
			if ( a.start > b.start ) {
				return 1;
			}
			return 0;
		}

		past_events = past_events.sort( compare_events );
		current_events = current_events.sort( compare_events );
		future_events = future_events.sort( compare_events );

		if ( !current_events.length && navigate_option === 'future' && future_events.length ) {
			calendar.instance.changeView( default_view, future_events[ 0 ].start );
		} else if ( !current_events.length && navigate_option === 'past' && past_events.length ) {
			calendar.instance.changeView( default_view, past_events[ past_events.length - 1 ].start );
		}
	},

	edit_event: function ( info ) {
		var calendar_id = info.view.calendar.el.dataset.calendar_id;
		var feed_id = info.view.calendar.el.dataset.feed_id;
		var calendar = gvFullCalendar.getCalendar( calendar_id );

		if ( !calendar ) {
			return;
		}

		var data = {
			action: 'gv_calendar_edit_event',
			calendar_id: feed_id,
			event_id: info.event.extendedProps.event_id,
			all_day: info.event.allDay,
			start: info.event.start.toString(),
			_nonce: calendar.extraOptions._nonce,
		};

		if ( info.event.end !== null ) {
			data.end = info.event.end.toString();
		}

		jQuery.ajax( {
			type: 'post',
			dataType: 'json',
			url: calendar.extraOptions.ajax_url,
			data: data,
			success: function ( response ) {
				// TODO: handle success and error messages
				if ( !response.success ) {
					info.revert();
				}
			},
		} );
	},
	changeLocale: function ( locale, calendar_id ) {
		var calendar = gvFullCalendar.getCalendar( calendar_id );

		if ( !calendar ) {
			return;
		}

		var onLocaleLoad = function () {
			calendar.instance.setOption( 'locales', [ locale ] );
			calendar.instance.setOption( 'locale', locale );
		};

		// "En" locale the default one and does not exist as a separate JS script
		if ( locale === 'en' ) {
			onLocaleLoad();


			return;
		}

		gvFullCalendar.loadLocale( locale, calendar_id, onLocaleLoad );
	},

	loadLocale: function ( locale, calendar_id, onLoad ) {
		var calendar = gvFullCalendar.getCalendar( calendar_id );

		if ( !calendar ) {
			return;
		}

		var scriptTag = document.createElement( 'script' );

		scriptTag.src = calendar.extraOptions.locales_url + locale + '.js';
		scriptTag.onload = onLoad;
		scriptTag.onreadystatechange = onLoad;

		document.body.appendChild( scriptTag );
	},

	getCalendar: function ( calendar_id ) {
		return ( window.gvCalendar && window.gvCalendar[ calendar_id ] && window.gvCalendar[ calendar_id ].hasOwnProperty( 'instance' ) ) ? window.gvCalendar[ calendar_id ] : false;
	},

	destroy: function ( calendar_id ) {
		var calendar = gvFullCalendar.getCalendar( calendar_id );

		if ( !calendar ) {
			return;
		}

		calendar.instance.destroy();
		window.gvCalendar[ calendar_id ] = {};
	},

	toggleCalendarLoader: function ( isLoading, calendar ) {
		if ( !calendar || calendar.extraOptions.loader !== true ) {
			return;
		}

		var calendarEl = jQuery( calendar.instance.el );
		var loaderEl = calendarEl.find( '.gv-fullcalendar-loader' );

		calendarEl.attr( 'aria-live', 'polite' );

		if ( isLoading ) {
			calendarEl.attr( 'aria-busy', 'true' );
			loaderEl.show()
		} else {
			calendarEl.attr( 'aria-busy', 'false' );
			loaderEl.hide()
		}
	},

	processHtmlContent: function ( content, allow_html ) {
		if ( !allow_html ) {
			// Escape HTML
			var pre = document.createElement( 'pre' );
			var text = document.createTextNode( content );

			pre.appendChild( text );

			content = pre.innerHTML;
		}

		// Remove script tags
		var sanitized_content = document.createElement( 'pre' );
		jQuery.each( jQuery.parseHTML( content ), function ( i, el ) {
			sanitized_content.appendChild( el );
		} );

		sanitized_content = sanitized_content.innerHTML.replace( /(\r\n|\n|\r)/g, '<br />' );

		return sanitized_content;
	}
};

document.addEventListener( 'DOMContentLoaded', function () {
	gvFullCalendar.init();
} );

/*
 * jQuery throttle / debounce - v1.1 - 3/7/2010
 * http://benalman.com/projects/jquery-throttle-debounce-plugin/
 *
 * Copyright (c) 2010 "Cowboy" Ben Alman
 * Dual licensed under the MIT and GPL licenses.
 * http://benalman.com/about/license/
 */
( function ( b, c ) {
	var $ = b.jQuery || b.Cowboy || ( b.Cowboy = {} ), a;
	$.throttle = a = function ( e, f, j, i ) {
		var h, d = 0;
		if ( typeof f !== "boolean" ) {
			i = j;
			j = f;
			f = c
		}

		function g() {
			var o = this, m = +new Date() - d, n = arguments;

			function l() {
				d = +new Date();
				j.apply( o, n )
			}

			function k() {
				h = c
			}

			if ( i && !h ) {
				l()
			}
			h && clearTimeout( h );
			if ( i === c && m > e ) {
				l()
			} else {
				if ( f !== true ) {
					h = setTimeout( i ? k : l, i === c ? e - m : e )
				}
			}
		}

		if ( $.guid ) {
			g.guid = j.guid = j.guid || $.guid++
		}
		return g
	};
	$.debounce = function ( d, e, f ) {
		return f === c ? a( d, e, false ) : a( d, f, e !== false )
	}
} )( this );
