/**
 * JavaScript code related to displaying course schedules.
 *
 * Additional scripts exist for different views.
 *
 * @author Francois Suter <support@cobweb.ch>
 */

var ScheduleGrid = {
	// Get the main tag and related data
	mainWrapper: $('#tx_courseschedule'),
	currentView: $('#tx_courseschedule').data('view'),
	userSearchField: $('#tx_courseschedule_user_search_field'),
	userSearchSuggestions: $('#user_search_suggestions'),
	rangeStart: parseInt($('#tx_courseschedule').data('startDate')),
	rangeEnd: parseInt($('#tx_courseschedule').data('endDate')),

	baseUrl: document.location.protocol + '//' + document.location.hostname + '/',

	scheduleContainer: $('#tx_courseschedule_schedule'),
	dateHeader: $('#tx_courseschedule_header'),
	dateRangeDisplay: $('#tx_courseschedule_date_range'),
	dateRangeSelector: $('#tx_courseschedule_date_selector'),

	searchField: $('#tx_courseschedule_search_field'),
	cancelSearchButton: $('#cancel_search'),

	// Full data received for processing
	rawData: [],
	// Earliest date in the raw data
	rawStart: null,
	// Latest date in the raw data
	rawEnd: null,
	// Dates in the current selection
	dates: [],
	// Reformatted data from the raw data
	schedule: [],
	// Current date range start
	start: null,
	// Current date range end
	end: null,
	// Max date in the future for the datepicker
	maxDate: null,
	// Currently selected date
	current: null,
	// DataTables instance
	dataTable: null,
	// Element holding the header
	header: null,
	// Possible display options
	options: {
	},
	// Current time (as an integer)
	currentTime: null,
	// Count of elements that were already made visible
	visibleElementsCount: 0,
	// Current page displayed
	currentPage: 0,
	// Total number of pages
	totalPages: 0,
	// Current window height
	windowHeight: 0,

	/**
	 * Sets the data for the grid. Additional properties are computed,
	 * in particular to have full date objects to work with.
	 *
	 * @param data
	 */
	setRawData: function(data) {
		this.rawData = data;
		// Key is a date in dd.mm.yyyy format
		var counter = 0;
		for (var key in this.rawData) {
			if (this.rawData.hasOwnProperty(key)) {
				var keyDay = moment(key + ' 00:00:00').valueOf();
				for (var i = 0; i < this.rawData[key].length; i++) {
					var entry = this.rawData[key][i];
					// For all entries, calculate timestamp for start and end date
					var startDate = moment(key + ' ' + entry['heuredebut']);
					var endDate = moment(key + ' ' + entry['heurefin']);
					// Add entries to schedule array, with some additional values
					entry['day'] = keyDay;
					entry['start'] = startDate;
					entry['end'] = endDate;
					this.schedule.push(entry);
					// Store the earliest and latest date (we receive them already sorted)
					if (counter === 0) {
						this.rawStart = keyDay;
					} else {
						this.rawEnd = keyDay;
					}
					counter++;
				}
			}
		}
	},
	/**
	 * Filters the grid data based on the start and end dates.
	 */
	filterPaginationData: function() {
		var startLimit = this.start.valueOf();
		var endLimit = this.end.valueOf();
		// Reset filtered data
		this.dates = [];
		// Key is a date in dd.mm.yyyy format
		for (var key in this.rawData) {
			if (this.rawData.hasOwnProperty(key)) {
				// Add date to list of available dates, if within date range
				var keyDay = moment(key + ' 00:00:00').valueOf();
				if (keyDay >= startLimit && keyDay <= endLimit) {
					this.dates.push(key);
				}
			}
		}
		// If the list of dates is empty, display special error message
		var courseDisplay = $('#tx_courseschedule_display');
		var courseDisplayError = $('#tx_courseschedule_display_error');
		if (this.dates.length === 0) {
			courseDisplay.addClass('d-none');
			courseDisplayError.removeClass('d-none');
		} else {
			if (courseDisplay.hasClass('d-none')) {
				courseDisplay.removeClass('d-none');
				courseDisplayError.addClass('d-none');
			}
		}
	},
	/**
	 * Returns the current grid data.
	 *
	 * @returns {Array}
	 */
	getData: function() {
		return this.schedule;
	},
	/**
	 * Checks if the given range is inside the existing range.
	 * If not, the page is reloaded, in order to query the server for new data.
	 *
	 * @param newStart
	 * @param newEnd
	 */
	checkRange: function(newStart, newEnd) {
		var self = this;
		// If the new range is outside the available range, we need to call the server
		if (newStart < this.rawStart || newEnd > this.rawStart) {
			// Display the loader again
			$('#tx_courseschedule_loader').removeClass('d-none');
			$('#tx_courseschedule').addClass('d-none');

			var start = (newStart.valueOf() / 1000);
			var end = Math.floor(newEnd.valueOf() / 1000);

			var serverUrl = self.getCurrentPageUrl();
			serverUrl += 'tx_courseschedule_schedule[start]=' + start;
			serverUrl += '&tx_courseschedule_schedule[end]=' + end;
			if (self.mainWrapper.data('user')) {
				serverUrl += '&tx_courseschedule_schedule[userId]=' + self.mainWrapper.data('user');
			}
			document.location = serverUrl;
		}
	},
	getCurrentPageUrl: function() {
		var serverUrl = document.location.href;
		// Assemble the URL to pass the new range to the server
		if (serverUrl.indexOf('?id=') !== -1) {
			var parts = serverUrl.split('&');
			serverUrl = parts[0] + '&';
		}
		else {
			serverUrl = document.location.pathname + '?';
		}
		return serverUrl;
	},
	/**
	 * Sets a new range of date and redraws the table.
	 *
	 * @param newStart
	 * @param newEnd
	 */
	setRange: function(newStart, newEnd) {
		// Set a new date range and redraw the table
		this.start = newStart;
		this.end = newEnd;
		// The current date is set to the start of the new range
		this.current = this.start;
		this.filterPaginationData();
		if (this.dataTable) {
			this.dataTable.columns(5).search(this.current.valueOf()).draw();
			this.buildPagination();
			this.buildHeader();
		}
	},
	/**
	 * Sets a new current date and filters data accordingly.
	 *
	 * @param dateButton
	 */
	setDay: function(dateButton) {
		// Get the new date from the data attribute, set it and filter on it
		var startDate = dateButton.data('startDate');
		this.current = moment(startDate);
		this.buildHeader();
		//this.dataTable.columns(5).search(this.current.valueOf()).draw();
		// Change the highlighted date button
		$('.date-filter').removeClass('btn-primary');
		dateButton.addClass('btn-primary');
	},
	/**
	 * Sets the element that will contain the pagination.
	 *
	 * @param element
	 */
	setPagination: function(element) {
		this.pagination = element;
	},
	/**
	 * Builds the date pagination based on the current date range.
	 */
	buildPagination: function() {
		var self = this;
		// Assemble the date pagination
		if (self.pagination) {
			moment.locale('fr');
			var paginationCode = '';
			for (var i = 0; i < self.dates.length; i++) {
				var keyStartDate = moment(self.dates[i] + ' 00:00:00');
				var keyEndDate = moment(self.dates[i] + ' 23:59:59');
				var extraClass = '';
				if (self.current && keyStartDate.valueOf() === self.current.valueOf()) {
					extraClass = ' btn-primary';
				}
				paginationCode += '<span class="btn btn-default btn-sm date-filter' + extraClass + '" data-start-date="' + keyStartDate.valueOf() + '" data-end-date="' + keyEndDate.valueOf() + '">' + keyStartDate.format('dd DD.MM.YYYY') + '</span>';
			}
			self.pagination.empty().append(paginationCode);
		}
	},
	/**
	 * Sets the element that will contain the header.
	 *
	 * @param element
	 */
	setHeader: function(element) {
		this.header = element;
	},
	/**
	 * Renders the header, based on the current date.
	 */
	buildHeader: function() {
		if (this.header) {
			this.header.empty().append(
				this.current.format('DD MMMM YYYY')
			);
		}
	},

	getCoursesForPeriod: function(start, end, callback) {
		var self = this;
		// Prepare extra data, if any
		var extraData = {
			eID: 'tx_courseschedule_connector',
			'tx_courseschedule[view]': self.mainWrapper.data('view')
		};
		if (self.mainWrapper.data('user')) {
			extraData['tx_courseschedule[user]'] = self.mainWrapper.data('user');
		}
		if (self.mainWrapper.data('track')) {
			extraData['tx_courseschedule[track]'] = self.mainWrapper.data('track');
		}
		if (start && start > 0) {
			extraData['tx_courseschedule[from]'] = start;
		}
		if (end && end > 0) {
			extraData['tx_courseschedule[to]'] = end;
		}

		$.ajax({
			url: self.baseUrl,
			data: extraData,
			success: callback,
			cache: false,
			error: function(data, status, xhr) {
				if (!Array.isArray(self.getData()) && $.isEmptyObject(self.getData())) {
					// Hide the loading mask and show the error message
					$('#tx_courseschedule_loader').addClass('d-none');
					$('#tx_courseschedule_error').removeClass('d-none');
				}
			}
		});
	},

	initDataTables: function(data) {
		var self = this;
		// Format data as needed for DataTables
		self.setRawData(data);
		self.filterPaginationData();
		// Set language file depending on current view
		var languageFile = self.baseUrl + 'typo3conf/ext/course_schedule/Resources/Public/JavaScript/Libraries/DataTables/i18n/French.json';

		// early return prevent double DataTables initialization
		// @see https://datatables.net/manual/tech-notes/3
		if (self.dataTable !== null) {
			return
		}

		// Initialize DataTables
		self.dataTable = self.scheduleContainer.DataTable({
			data: self.getData(),
			language: {
				url: languageFile
			},
			responsive: {
				details: {
					type: 'column',
					target: 'tr'
				}
			},
			autoWidth: false,
			dom: 't',
			ordering: false,
			paging: false,
			// Map JSON data to table columns
			columns: [
				{
					targets: 'schedule-date',
					data: 'heuredebut',
					className: 'col-schedule-date',
					render: function(data, type, row, meta) {
						return  row.start.format('HH:mm') + '<span class="d-none d-sm-inline">&nbsp;-&nbsp;</span><br class="d-sm-none">' + row.end.format('HH:mm');
					}
				},
				{
					targets: 'schedule-title',
					data: 'titre',
					className: 'col-schedule-title',
					render: function(data, type, row, meta) {
						var content = '';
						if (row.matiere !== '') {
							content += row.matiere;
							if (row.codematiere !== '') {
								content += ' (' + row.codematiere + ')';
							}
							content += '<br>';
						}
						if (row.titre !== '') {
							content += row.titre + '<br>';
						}
                        if (row.day !== '') {
                            content += '<span class="d-none day">' + row.day + '</span>';
                        }
						return content;
					}
				},
				{
					targets: 'schedule-groups',
					data: 'groupes',
					className: 'col-schedule-groups',
					render: function(data, type, row, meta) {
						return data.join('<br>');
					}
				},
				{
					targets: 'schedule-rooms',
					data: 'salles',
					className: 'col-schedule-rooms',
					render: function(data, type, row, meta) {
						return data.join('<br>');
					}
				},
				{
					targets: 'schedule-speakers',
					data: 'intervenants',
					className: 'col-schedule-speakers',
					render: function(data, type, row, meta) {
						var fullSpeakerNames = [];
						for (var i = 0; i < data.length; i++) {
							fullSpeakerNames.push(data[i]['prenom'] + ' ' + data[i]['nom'])
						}
						return fullSpeakerNames.join('<br>');
					}
				},
				{
					data: 'start',
					visible: false,
					className: 'd-none',
					render: function(data, type, row, meta) {
						return data.valueOf();
					}
				}
			],
			// Act when the loading is complete
			initComplete: function() {
				// Activate the date range picker
				self.activateDateRangePicker();

				// Activate the search field
				self.activateSearchField();

				// Hide the loading mask and show the table
				$('#tx_courseschedule_loader').addClass('d-none');
				self.mainWrapper.removeClass('d-none');

				// Add day above each row
				window.setTimeout("ScheduleGrid.initRowHeaders()", 100);
			}
		});
	},
	initRowHeaders: function() {
		var self = this;
		self.scheduleContainer.find('tr:visible td > span.day').each(function() {
			var date = parseInt($(this).text().trim(), 10);
			var headerRow = $('<tr id="day-' + date + '" class="date-header" data-day="' + date +'"><th colspan="7">' + moment(date).format('Do MMMM YYYY') + '</th>');

			if ($('#day-' + date).length == 0) {
				$(this).closest('tr').before(headerRow);
			}
		});
	},
	activateSearchField: function() {
		var self = this;
		if (self.searchField.length > 0) {

			// Perform search on any activity in the search field
			self.searchField.on('keyup', function() {
				var searchValue = $(this).val();
				// Toggle visibility of cancel search button
				if (searchValue == '') {
					self.cancelSearchButton.addClass('d-none');
				} else {
					self.cancelSearchButton.removeClass('d-none');
				}
				self.dataTable.search(searchValue).draw();

				self.initRowHeaders();
			});

			// Cancel search
			self.cancelSearchButton.on('click', function() {
				// Empty the search field value
				self.searchField.val('');
				// Redraw the table without search
				self.dataTable.search('').draw();
				// Hide the cancel search button
				self.cancelSearchButton.addClass('d-none');
				self.initRowHeaders();
			});
		}
	},
	activateDateRangePicker: function() {
		var self = this;
		if (self.dateRangeSelector.length > 0) {
			var currentRange = self.start.format('DD.MM.YYYY') + ' - ' + self.end.format('DD.MM.YYYY');
			// Get max available date for the date picker
			var maxDate = self.mainWrapper.data('max-date')  * 1000;
			var maxDateMoment = moment(maxDate);
			self.dateRangeDisplay.empty().append(currentRange);

			// Datepicker Configuration
			var dateRangePickerConfiguration = {
                                startDate: self.start,
                                endDate: self.end,
                                locale: {
                                        format: 'DD.MM.YYYY'
                                }
			};

			// Checks if maxDateMoment is v valid before setting a max date in the datepicker
			if (maxDateMoment.isValid()) {
				dateRangePickerConfiguration.maxDate = moment(maxDate);
			}

			self.dateRangeSelector.daterangepicker(dateRangePickerConfiguration);

			// On selection, reload the schedule with the new date range
			self.dateRangeSelector.on('apply.daterangepicker', function(event, picker) {
				self.checkRange(picker.startDate, picker.endDate);
				self.setRange(picker.startDate, picker.endDate);
				var currentRange = self.start.format('DD.MM.YYYY') + ' - ' + self.end.format('DD.MM.YYYY');
				self.dateRangeDisplay.empty().append(currentRange);
			});

			$('#tx_courseschedule_date_choice').find('.date-range').each(function() {
				var startDate = moment($(this).data('startDate'));
				var endDate = moment($(this).data('endDate')).hours(23).minutes(59).seconds(59).milliseconds(0);
				$(this).data('start-timestamp', startDate.format('x'));
				$(this).data('end-timestamp', endDate.format('x'));

				if (startDate.format('X') == self.mainWrapper.data('start-date') && endDate.format('X') == self.mainWrapper.data('end-date')) {
					$(this).addClass('btn-primary');
				}
			});

			// Act on predefined date ranges
			$('#tx_courseschedule_date_choice').find('.date-range').on('click', function() {
				// Get the predefined dates and apply them to the date picker
				var startDate = $(this).data('start-timestamp');
				var endDate = $(this).data('end-timestamp');
				self.dateRangeSelector.data('daterangepicker').setStartDate(startDate);
				self.dateRangeSelector.data('daterangepicker').setEndDate(endDate);
				// Refresh the schedule with the new date range
				self.checkRange(startDate, endDate);
				self.setRange(startDate, endDate);
				var currentRange = self.start.format('DD.MM.YYYY') + ' - ' + self.end.format('DD.MM.YYYY');
				self.dateRangeDisplay.empty().append(currentRange);
			});
		}
	},
	/**
	 * Bind events to the user search field
	 */
	activeUserSearchField: function() {
		var self = this;
		// Prefill the search field with the current user if one is already selected
		if (self.mainWrapper.data('user-name')) {
			self.userSearchField.val(self.mainWrapper.data('user-name'));
		}

		if (self.userSearchField.length > 0) {
			self.userSearchField.on('keyup', function(e) {
				var search = $(this).val();
				var $suggestionLinks = self.userSearchSuggestions.find('a');
				var currentIndex = self.userSearchSuggestions.data('index') ? self.userSearchSuggestions.data('index') : self.userSearchSuggestions.find('a.selected').index();
				switch (e.keyCode) {
					// Arrow down
					case 40:
						currentIndex++;
						currentIndex = currentIndex >= $suggestionLinks.length ? 0 : currentIndex;
						$suggestionLinks.removeClass('selected');
						$suggestionLinks.eq(currentIndex).addClass('selected');
						break;
					// Arrow up
					case 38:
						currentIndex--;
						currentIndex = currentIndex < 0 ? $suggestionLinks.length-1 : currentIndex;
						$suggestionLinks.removeClass('selected');
						$suggestionLinks.eq(currentIndex).addClass('selected');
						break;
					// Enter
					case 13:
						// Assemble the URL to pass the new range to the server
						var serverUrl = self.getCurrentPageUrl();
						serverUrl += 'tx_courseschedule_schedule[start]=' + self.rangeStart;
						serverUrl += '&tx_courseschedule_schedule[end]=' + self.rangeEnd;
						var selectedUser = self.userSearchSuggestions.find('a.selected').length > 0 ? self.userSearchSuggestions.find('a.selected').data('user') : self.userSearchSuggestions.find('a').first().data('user');
						serverUrl += '&tx_courseschedule_schedule[userId]=' + selectedUser;
						document.location = serverUrl;
						break;
					default:
						if (search.length >= 2) {
							var extraData = {
								eID: 'tx_courseschedule_usersuggestion',
								'tx_courseschedule_schedule[q]': search
							};
							$.ajax({
								url: self.baseUrl,
								data: extraData,
								success: function (data) {
									if (data.length) {
										self.updateUserSuggestions(data);
										self.userSearchSuggestions.data('index', '');
									}
									else {
										self.userSearchSuggestions.html('').hide();
									}
								}
							});
						}
						else {
							self.userSearchSuggestions.html('').hide();
						}
						break;
				}
				$(this).focus().val($(this).val());
				self.userSearchSuggestions.data('index', currentIndex);
			});
			// Close the select when clicking anywhere else in the page
			$('body').on('click', function(e) {
				if (!$(e.target).parents('#tx_courseschedule_user_search_field_container').length) {
					self.userSearchSuggestions.html('').hide();
				}
			});

			$('body').on('click', '#user_search_suggestions a', function() {
				// Assemble the URL to pass the new range to the server
				var serverUrl = self.getCurrentPageUrl();
				serverUrl += 'tx_courseschedule_schedule[start]=' + self.rangeStart;
				serverUrl += '&tx_courseschedule_schedule[end]=' + self.rangeEnd;
				serverUrl += '&tx_courseschedule_schedule[userId]=' + $(this).data('user');
				document.location = serverUrl;
			});
		}
	},
	/*
	 * Update the interface with the given suggestions for user search
	 */
	updateUserSuggestions: function(data) {
		var self = this;
		self.userSearchSuggestions.html('').hide();
		for (var i = 0 ; i < data.length ; i++) {
			var user = data[i];
			self.userSearchSuggestions.append('<li><a data-user="' + user['isa_code'] + '">' + user['last_name'] + ', ' + user['first_name'] + ' <small>(' + user['username'] + ')</small></a></li>');
		}
		self.userSearchSuggestions.show();

	}
};

$(document).ready(function() {
	// Set the initial range based on input values or set default values
	var start;
	var end;
	var rangeStart = ScheduleGrid.rangeStart;
	var rangeEnd = ScheduleGrid.rangeEnd;
	var mainWrapper = ScheduleGrid.mainWrapper;

	if (mainWrapper.length > 0) {
		if (rangeStart && rangeStart > 0) {
			start = new moment(rangeStart * 1000);
			end = new moment(rangeEnd * 1000);
		} else {
			start = new moment().hours(0).minutes(0).seconds(0).milliseconds(0);
			end = new moment().add(6, 'days').hours(23).minutes(59).seconds(59).milliseconds(0);
		}
		ScheduleGrid.current = start;
		ScheduleGrid.start = start;
		ScheduleGrid.end = end;
		ScheduleGrid.currentTime = moment().valueOf();

		// Active the user search if needed
		ScheduleGrid.activeUserSearchField();

		if (mainWrapper.data('view') !== 'personal' || (mainWrapper.length > 0 && mainWrapper.data('user'))) {
			ScheduleGrid.getCoursesForPeriod(rangeStart, rangeEnd, function (data) {
				ScheduleGrid.initDataTables(data);
			});
		}
		else {
			$('#tx_courseschedule_loader').addClass('d-none');
		}
	}
});
