var compos = [];
var timeIntervalsByDay = {};
var daysToDisable = [];
var datesToDisable = [];
var masks = {};

var joursFeries = [];
var horairesReady = false;
var todoAfterRetourHoraires = [];
var documentReady = false;
var deferredLogs = [];

// config
var lnkDebug = false;
var lnkLocalizedExtDateFormat = 'd/m/Y';
var lnkLocalizedExtRepeatableDateFormat = 'd/m';
var lnkToLocalizedHour; // voir ci-dessous les valeurs possibles

var lnkToLocalizedHourNeutral = function (hour) {
	return toPaddedString(hour, 2);
}
var lnkToLocalizedHourFrench = function (hour) {
	return toPaddedString(hour, 2) + "h";
}
var lnkToLocalizedHourUS = function (hour) {
	var h = Number(hour);
	return String(h <= 0 ? 12 : ((h - 1) % 12) + 1) + ' ' + (Math.floor(h / 12) ? 'p.m.' : 'a.m.');
}
if (!lnkToLocalizedHour) { // réglage par défaut, on n'écrase pas une éventuelle config précédente
	lnkToLocalizedHour = lnkToLocalizedHourFrench;
	//lnkToLocalizedHour = lnkToLocalizedHourUS;
	//lnkToLocalizedHour = lnkToLocalizedHourNeutral;
}

function log(message, exc) {
	if (!lnkDebug) {
		return;
	}
	if (!documentReady) {
		deferredLogs[deferredLogs.length] = function () { log(message, exc); };
		return;
	}
	var logElem = document.getElementById('lnkLog');
	if (!logElem) {
		logElem = document.createElement("DIV");
		logElem.id = 'lnkLog';
		document.body.appendChild(logElem);
	}
	var line = document.createElement("DIV");
	var text = message;
	if (exc) {
		text += ' ' + exc + '(';
		for (prop in exc) {
			if (exc.hasOwnProperty(prop) && prop != 'stack') {
				text += prop + ": '" + exc[prop] + "', ";
			}
		}
		text += ')';
	}
	line.appendChild(document.createTextNode(text));
	logElem.appendChild(line);
	if (exc && exc.hasOwnProperty('stack') && typeof(exc.stack) == 'string') {
		var stack = exc.stack.split(/\r?\n/);
		var stackTrace = document.createElement("OL");
		var i, stackElem;
		for (var i = 0; i < stack.length; ++i) {
			if (stack[i]) {
				stackElem = document.createElement("LI");
				stackElem.appendChild(document.createTextNode(stack[i]));
				stackTrace.appendChild(stackElem);
			}
		}
		logElem.appendChild(stackTrace);
	}
}

function flushDeferredLogs() {
	try {
		var i, logCall;
		while (deferredLogs.length > 0) {
			logCall = deferredLogs.shift();
			if (logCall) {
				logCall();
			}
		}
	}
	catch (exc) {
		log('flushDeferredLogs', exc);
	}
}

try {
	Ext.onReady(function() {
		documentReady = true;
		flushDeferredLogs();
	});
}
catch (exc) {
	log('Ext.onReady flushDeferredLogs registration', exc);
}

if (!Array.prototype.map) {
	Array.prototype.map = function(fun /* , thisp */) {
		var len = this.length >>> 0;
		if (typeof fun != "function") {
			throw new TypeError();
		}
		var res = new Array(len);
		var thisp = arguments[1];
		for (var i = 0; i < len; i++) {
			if (i in this) {
				res[i] = fun.call(thisp, this[i], i, this);
			}
		}
		return res;
	};
}

function retourHoraires(resp) {
	try {
		//log('debut retourHoraires');
		var prop;
		for (prop in timeIntervalsByDay) {
			timeIntervalsByDay[prop] = undefined;
		}
		var i;
		var plagesHoraires = resp.horairesOuverture;
		var plage, debut, fin, dayOfWeek1, dayOfWeek2, time1, time2, dayShift;
		var day;
		for (i = 0; i < plagesHoraires.length; ++i) {
			plage = plagesHoraires[i];
			debut = plage.debut;
			fin = plage.fin;
			dayOfWeek1 = Number(debut.charAt(3));
			dayOfWeek2 = Number(fin.charAt(3));
			time1 = Number(debut.substring(5, 7)) * 60 + Number(debut.substring(8));
			time2 = Number(fin.substring(5, 7)) * 60 + Number(fin.substring(8));
			dayShift = Number(plage.debutHeureCanal.charAt(3)) - dayOfWeek1;
			if (dayOfWeek1 == dayOfWeek2) {
				if (!timeIntervalsByDay[dayOfWeek1]) {
					timeIntervalsByDay[dayOfWeek1] = [];
				}
				timeIntervalsByDay[dayOfWeek1].push([ time1, time2, dayShift ]);
			}
			else {
				for (day = dayOfWeek1; day <= dayOfWeek2; ++day) {
					if (!timeIntervalsByDay[day]) {
						timeIntervalsByDay[day] = [];
					}
					if (day == dayOfWeek1) {
						timeIntervalsByDay[day].push([ time1, 24*60, dayShift ]);
					}
					else if (day == dayOfWeek2) {
						timeIntervalsByDay[day].push([ 0, time2, dayShift ]);
					}
					else { // should never happen for the moment
						timeIntervalsByDay[day].push([ 0, 24*60, dayShift ]);
					}
					--dayShift;
				}
			}
		}
		/*if (Array.prototype.map) {
			for (var prop in timeIntervalsByDay) {
				//log('prop: '+prop);
				if (!timeIntervalsByDay.hasOwnProperty || timeIntervalsByDay.hasOwnProperty(prop)) {
					log(prop +' - timeIntervals: ['+timeIntervalsByDay[prop].map(function(it) {
						return '['+Math.floor(it[0]/60)+':'+toPaddedString(it[0]%60,2)+','+Math.floor(it[1]/60)+':'+toPaddedString(it[1]%60,2)+','+it[2]+']';
					}).join(',')+']');
				}
			}
		}*/

		while (daysToDisable.length > 0) {
			daysToDisable.pop();
		}
		for (i = 0; i < 7; ++i) {
			if (!timeIntervalsByDay[i]) {
				daysToDisable.push(i);
			}
		}
		//log('daysToDisable: ['+daysToDisable.join(',')+']');
		
		while (datesToDisable.length > 0) {
			datesToDisable.pop();
		}
		joursFeries = resp.joursFeries;
		var now = new Date();
		var partialDate, year, month, day, localDate, intervals, j;
		for (i = 0; i < joursFeries.length; ++i) {
			partialDate = joursFeries[i].date;
			localDate = new Date();
			localDate.setHours(0, 0, 0, 0);
			if (/^-/.test(partialDate)) {
				year = null;
				month = Number(partialDate.substring(2, 4));
				day = Number(partialDate.substring(5, 7));
				localDate.setMonth(month - 1, day);
				if (localDate.getTime() < now.getTime()) {
					localDate.setFullYear(localDate.getFullYear() + 1);
				}
			}
			else {
				year = Number(partialDate.substring(0, 4));
				month = Number(partialDate.substring(5, 7));
				day = Number(partialDate.substring(8, 10));
				localDate.setFullYear(year, month - 1, day);
			}
			//log ('year: '+year+', month: '+month+', day: '+day+', localDate: '+localDate+'.');
			intervals = timeIntervalsByDay[localDate.getDay()];
			if (intervals) {
				j = 0;
				while (j < intervals.length && intervals[j][2] === 0) {
					++j;
				}
				if (j >= intervals.length) {
					datesToDisable.push(localDate.format(year ? lnkLocalizedExtDateFormat : lnkLocalizedExtRepeatableDateFormat));
				}
			}
		}
		var time = (now.getHours() * 60) + now.getMinutes();
		var horairesAjdh = timeIntervalsByDay[now.getDay()];
		if (!horairesAjdh || !findNextTimeInterval(horairesAjdh, time, now)) {
			datesToDisable.push(now.format(lnkLocalizedExtDateFormat));
			//log('datesToDisable.push(' + now.format(lnkLocalizedExtDateFormat) + ')');
		}
		//log('fin retourHoraires');
		horairesReady = true;
		var todoCall;
		while (todoAfterRetourHoraires.length > 0) {
			todoCall = todoAfterRetourHoraires.shift();
			if (todoCall) {
				todoCall();
			}
		}
	}
	catch (exc) {
		log('retourHoraires', exc);
	}
}

function showMask(compId, msg) {
	try {
		var comp = !compId ? Ext.getBody() : Ext.getCmp(compId);
		var mask;
		if (!masks[comp.id]) {
			mask = new Ext.LoadMask(comp, {
				msg : "Traitement en cours..."
			});
			masks[comp.id] = mask;
		}
		mask = masks[comp.id];
		mask.msg = msg;
		mask.show();
	}
	catch (exc) {
		log('showMask', exc);
	}
}

function hideMask(compId) {
	try {
		var comp = !compId ? Ext.getBody() : Ext.getCmp(compId);
		if (masks[comp.id]) {
			masks[comp.id].hide();
		}
	}
	catch (exc) {
		log('hideMask', exc);
	}
}

function getLnkDate(dateContainerElemId) {
	try {
		var comp = Ext.get("datefield_" + dateContainerElemId);
		if (!comp) {
			return null;
		}
		var date = comp.getValue();
		var timeContainerElemId = Ext.getCmp(comp.id).lnkTimeContainerElemId;
		if (!timeContainerElemId) {
			return date;
		}
		var hComp = document.getElementById(timeContainerElemId + "_hours");
		if (!hComp) {
			return null;
		}
		var mComp = document.getElementById(timeContainerElemId + "_minutes");
		if (!mComp) {
			return null;
		}
		return date + " " + hComp.options[hComp.selectedIndex].value + ":" + mComp.options[mComp.selectedIndex].value;
	}
	catch (exc) {
		log('getLnkDate', exc);
	}
	return null;
}

function isToday(date) {
	try {
		var now = new Date();
		return (date.getDate() == now.getDate()
				&& date.getMonth() == now.getMonth()
				&& date.getYear() == now.getYear());
	}
	catch (exc) {
		log('isToday', exc);
	}
	return false;
}

function removeAllOptions(selectElem) {
	try {
		while (selectElem.options.length > 0) {
			selectElem.remove(0);
		}
	}
	catch (exc) {
		log('removeAllOptions', exc);
	}
}

function addVoidOption(selectElem, voidChar) {
	try {
		voidChar = voidChar || " ";
		var optionElem = document.createElement("OPTION");
		optionElem.text = voidChar;
		optionElem.value = voidChar;
		selectElem.options.add(optionElem);
	}
	catch (exc) {
		log('addVoidOption', exc);
	}
}

function isWorkedDay(dateObj) {
	try {
		if (!dateObj) {
			return true;
		}
		var testDateMD, testDateYMD, i;
		testDateYMD = testDateMD = "-" + toPaddedString(dateObj.getMonth() + 1, 2) + "-" + toPaddedString(dateObj.getDate(), 2);
		testDateMD = "-" + testDateMD;
		testDateYMD = toPaddedString(dateObj.getFullYear(), 4) + testDateYMD;
		i = 0;
		while (i < joursFeries.length && !(testDateYMD == joursFeries[i].date || testDateMD == joursFeries[i].date)) {
			++i;
		}
		return i >= joursFeries.length;
	}
	catch (exc) {
		log('isWorkedDay', exc);
	}
}

function findMatchingTimeInterval(timeIntervals, time, dateObj) {
	try {
		var i, interval, dateFuseauCanal;
		for (i = 0; i < timeIntervals.length; i++) {
			interval = timeIntervals[i];
			if (interval && time >= interval[0] && time < interval[1]) {
				dateFuseauCanal = new Date(dateObj.getTime() + 24*60*60*1000*interval[2]);
				if (isWorkedDay(dateFuseauCanal)) {
					return interval;
				}
			}
		}
	}
	catch (exc) {
		log('findMatchingTimeInterval', exc);
	}
	return null;
}

function findNextTimeInterval(timeIntervals, time, dateObj) {
	try {
		var i, interval;
		for (i = 0; i < timeIntervals.length; i++) {
			interval = timeIntervals[i];
			if (interval && time < interval[1]) {
				dateFuseauCanal = new Date(dateObj.getTime() + 24*60*60*1000*interval[2]);
				if (isWorkedDay(dateFuseauCanal)) {
					return interval;
				}
			}
		}
	}
	catch (exc) {
		log('findNextTimeInterval', exc);
	}
	return null;
}

function fillHoursAndMinutes(field, dateObj) {
	try {
		var timeContainerElemId = field.lnkTimeContainerElemId;
		if (!timeContainerElemId) {
			return;
		}
		var now = new Date();
		var timeNow = now.getHours() * 60 + now.getMinutes();

		var timeIntervals = timeIntervalsByDay[dateObj.getDay()];
		/*if (Array.prototype.map) {
			log('timeIntervals: ['+timeIntervals.map(function(it) { return '['+Math.floor(it[0]/60)+':'+toPaddedString(it[0]%60,2)+','+Math.floor(it[1]/60)+':'+toPaddedString(it[1]%60,2)+','+it[2]+']'; }).join(',')+']');
		}*/
		var hoursSelectElem = document.getElementById(timeContainerElemId + "_hours");
		var minutesSelectElem = document.getElementById(timeContainerElemId + "_minutes");

		hoursSelectElem.lnkTimeIntervals = timeIntervals;
		removeAllOptions(hoursSelectElem);
		var start = Math.floor(timeIntervals[0][0] / 60);

		var interval;
		if (isToday(dateObj)) {
			interval = findNextTimeInterval(timeIntervals, timeNow, dateObj);

			if (interval && timeNow >= interval[0] && timeNow < interval[1]) {
				start = Math.floor(timeNow / 60);
				//log('today fixed start: ['+start+']')
			}
			else if (!interval || timeNow >= interval[1]) {
				datesToDisable.push(now.format(lnkLocalizedExtDateFormat));
				removeAllOptions(hoursSelectElem);
				addVoidOption(hoursSelectElem, "--");
				removeAllOptions(minutesSelectElem);
				addVoidOption(minutesSelectElem, "--");
				return;
			}
		}
		var end = timeIntervals[timeIntervals.length - 1][1] / 60;
		var heure, minute;
		for (heure = start; heure <= end; heure++) {
			for (minute = 0; minute < 60; minute += minutesSelectElem.lnkMinutesStep) {
				if (findMatchingTimeInterval(timeIntervals, heure * 60 + minute, dateObj)) {
					var h = "" + heure;
					if (h.length == 1) {
						h = "0" + h;
					}
					var optionElem = document.createElement("OPTION");
					optionElem.text = lnkToLocalizedHour ? lnkToLocalizedHour(heure) : lnkToLocalizedHourFrench(heure);
					optionElem.value = h;
					hoursSelectElem.options.add(optionElem);
					break;
				}
			}
		}

		minutesSelectElem.lnkDateObj = dateObj;
		minutesSelectElem.lnkTimeIntervals = timeIntervals;
		
		hoursSelectElem.onchange = function() {
			fillMinutes(hoursSelectElem);
		};
		hoursSelectElem.onchange();
	}
	catch (exc) {
		log('fillHoursAndMinutes', exc);
	}
}

function destroyComponent(containerElemId) {
	try {
		var i = 0, comp, df, h, m;
		while (i < compos.length && !(compos[i] && compos[i].id == containerElemId)) {
			++i;
		}
		if (i < compos.length) {
			comp = compos[i];
			if (comp.xtype == "datefield") {
				df = Ext.ComponentMgr.get('datefield_' + containerElemId);
				if (df) {
					df.destroy();
				}
			}
			else if (comp.xtype == "lnkTime") {
				h = Ext.get(containerElemId + "_hours");
				if (h) {
					h.remove();
				}
				m = Ext.get(containerElemId + "_minutes");
				if (m) {
					m.remove();
				}
			}
		}
	}
	catch (exc) {
		log('destroyComponent', exc);
	}
}

function showComponents() {
	try {
		if (!horairesReady) {
			//log('registering showComponents');
			todoAfterRetourHoraires[todoAfterRetourHoraires.length] = function () { showComponents(); };
			return;
		}
		//log('showComponents');
		var i, comp;
		for (i = 0; i < compos.length; i++) {
			//log('showComponents compos[' + i + ']');
			comp = compos[i];
			if (!comp) {
				continue;
			}
			else if (comp.xtype == "datefield") {
				//log('showComponents compos[' + i + '] datefield');
				var dateFieldCompId = 'datefield_' + comp.id;
				var todayMidnight = new Date();
				todayMidnight.setHours(0, 0, 0, 0);
				var dateField = Ext.ComponentMgr.get(dateFieldCompId);
				if (dateField) { // already created
					dateField.setMinValue(todayMidnight);
					dateField.setDisabledDays(daysToDisable);
					dateField.setDisabledDates((!datesToDisable || !datesToDisable.length) ? null : datesToDisable);
					dateField.reset();
					continue;
				}
				//log('showComponents compos[' + i + '] datefield - creating new DateField - daysToDisable: ['+daysToDisable.join(',')+'], disabledDates: ['+datesToDisable.join(',')+']');
				dateField = new Ext.form.DateField({
					value : '',
					format : lnkLocalizedExtDateFormat,
					width : 100,
					minValue: todayMidnight,
					disabledDays : daysToDisable,
					disabledDates : (!datesToDisable || !datesToDisable.length) ? null : datesToDisable, // Ext 3.0.0 up to 3.2.0 misinterprets empty array here
					id : dateFieldCompId
				});
				dateField.lnkTimeContainerElemId = comp.timeCompId;
				dateField.on("select", fillHoursAndMinutes, dateField);
				dateField.render(comp.id);
				//log('showComponents compos[' + i + '] datefield - rendered');
			}
			else if (comp.xtype == "lnkTime") {
				//log('showComponents compos[' + i + '] lnkTime');
				var timeContainerElemId = comp.id;
				var timeContainerElem = document.getElementById(timeContainerElemId);
				var hId = timeContainerElemId + "_hours";
				var mId = timeContainerElemId + "_minutes";
				var hoursSelectElem = document.getElementById(hId);
				var minutesSelectElem = document.getElementById(mId);
				if (hoursSelectElem) {
					removeAllOptions(hoursSelectElem);
					addVoidOption(hoursSelectElem, "--");
				}
				else {
					hoursSelectElem = document.createElement("SELECT");
					hoursSelectElem.id = hId;
					hoursSelectElem.lnkMinutesSelectElemId = mId;
					hoursSelectElem.lnkMinutesStep = comp.minutesStep;
					addVoidOption(hoursSelectElem, "--");
					timeContainerElem.appendChild(hoursSelectElem);
				}
				if (minutesSelectElem) {
					removeAllOptions(minutesSelectElem);
					addVoidOption(minutesSelectElem, "--");
				}
				else {
					minutesSelectElem = document.createElement("SELECT");
					minutesSelectElem.id = mId;
					minutesSelectElem.lnkMinutesStep = comp.minutesStep;
					addVoidOption(minutesSelectElem, "--");
					timeContainerElem.appendChild(minutesSelectElem);
				}
			}
		}
	}
	catch (exc) {
		log('showComponents', exc);
	}
}

var disableAutoShowComponents;

try {
    if (!disableAutoShowComponents) {
	Ext.onReady(function() {
		try {
			//log('calling show components from Ext.onReady()');
			showComponents();
		}
		catch (exc) {
			log('showComponents call', exc);
		}
	});
    }
}
catch (exc) {
	log('Ext.onReady showComponents registration ' + exc);
}

/**
 * Creates a date picker whose container {@link Element#id} is
 * {@code dateContainerElemId} and links it to the time component whose container
 * {@link Element#id} is {@code timeContainerElemId}.
 * 
 * @param dateContainerElemId
 * @param timeContainerElemId
 * @return
 */
function showLnkDate(dateContainerElemId, timeContainerElemId) {
	//log('showLnkDate');
	compos.push({
		xtype : 'datefield',
		id : dateContainerElemId,
		timeCompId : timeContainerElemId
	});
}

function fillMinutes(hoursSelectElem) {
	try {
		var hour = hoursSelectElem.options[hoursSelectElem.selectedIndex].value;

		var minutesSelectElem = document.getElementById(hoursSelectElem.lnkMinutesSelectElemId);

		var dateObj = minutesSelectElem.lnkDateObj;
		var now = new Date();
		var timeNow = now.getHours() * 60 + now.getMinutes();
		removeAllOptions(minutesSelectElem);
		var start = hour * 60;
		var end = (hour + 1) * 60;
		var mustStartFromNow = (isToday(dateObj) && timeNow >= start && timeNow < end);
		var offset = 0;
		var timeIntervals = minutesSelectElem.lnkTimeIntervals;

		var minutes, plage;
		for (minutes = 0; minutes < 60; minutes += minutesSelectElem.lnkMinutesStep) {
			offset = start + minutes;
			plage = findMatchingTimeInterval(timeIntervals, offset, dateObj);

			if (plage && offset < plage[1] && (!mustStartFromNow || offset > timeNow)) {
				var m = "" + minutes;
				if (m.length == 1) {
					m = "0" + m;
				}
				var optionElem = document.createElement("OPTION");
				optionElem.text = m;
				optionElem.value = m;

				minutesSelectElem.options.add(optionElem);
			}
		}
		if (minutesSelectElem.options.length <= 0) {
			hoursSelectElem.remove(hoursSelectElem.selectedIndex);
			if (hoursSelectElem.selectedIndex < hoursSelectElem.options.length) {
				hoursSelectElem.onchange();
			}
			else {
				removeAllOptions(hoursSelectElem);
				addVoidOption(hoursSelectElem, "--");
			}
		}
	}
	catch (exc) {
		log('fillMinutes', exc);
	}
}

/**
 * Creates a time picker whose container is {@code timeContainerElemId}.
 * 
 * @param timeContainerElemId
 * @param minutesStep
 * @return
 */
function showLnkTime(timeContainerElemId, minutesStep) {
	//log('showLnkTime');
	compos.push({
		xtype : 'lnkTime',
		id : timeContainerElemId,
		minutesStep : minutesStep
	});
}

