/**
 * Project  : AMDA-NG
 * Name	 : EpnTapModule.js
 * @class   amdaDesktop.EpnTapModule
 * @extends amdaDesktop.AmdaModule
 * @brief   EpnTap Module controller definition
 * @author  Nathanael Jourdane
 */

// Load text with Ajax synchronously: takes path to file and optional MIME type.
function loadTextFileAjaxSync(filePath, mimeType) {
	var xmlhttp=new XMLHttpRequest();
	xmlhttp.open("GET", filePath, false);
	if (mimeType != null) {
		if (xmlhttp.overrideMimeType) {
			xmlhttp.overrideMimeType(mimeType);
		}
	}
	xmlhttp.send();
	if (xmlhttp.status == 200) {
		return xmlhttp.responseText;
	} else {
		return null;
	}
}

Ext.define('amdaDesktop.EpnTapModule', {

	extend: 'amdaDesktop.AmdaModule',
	requires: ['amdaUI.EpnTapUI'],
	contentId : 'EpnTapUI',

	/** The alias name of the module view. */
	uiType: 'panelEpnTap',

	/** The text displayed on the *help button* tooltip. */
	helpTitle: 'Help on EPN-TAP Module',

	/** The name of the documentation file related to the module. */
	helpFile : 'epnTapHelp',

	/** Window dimentions. */
	width : 1000,
	height: 550,

	/**
	Module initialisation.
	*/
	init: function() {

		// TODO: Utiliser des stores pour accéder aux fichiers JS !!

		this.metadata = JSON.parse(loadTextFileAjaxSync('../../generic_data/EpnTapData/metadata.json', 'application/json'));
		this.services = JSON.parse(loadTextFileAjaxSync('../../generic_data/EpnTapData/services.json', 'application/json'));
		this.productTypeDict = JSON.parse(loadTextFileAjaxSync('../../generic_data/EpnTapData/dataproduct_types.json', 'application/json'));
		this.mimetypeDict = JSON.parse(loadTextFileAjaxSync('../../generic_data/EpnTapData/mimetypes.json', 'application/json'));

		this.select = Array();
		this.filter = Array();

		this.selectedServiceId = null;

		this.launcher = {
			text: this.title,
			iconCls: this.icon,
			handler: this.createWindow,
			scope: this
		};
	},

	/**
	Capitalize a name and replace underscores with spaces.
	- `name`: The string to make pretty.
	*/
	prettify: function(name) {
		return name.charAt(0).toUpperCase() + name.replace(/_/g, ' ').substr(1).toLowerCase();
	},

	/**
	Capitalize a name, replace underscores with spaces, and write it in a plurial form.
	- `name`: The string to make pretty.
	*/
	allPrettify: function(name) {
		return 'All ' + (name[name.length-1] == 's' ? name : name + 's').replace(/_/g, ' ').toLowerCase();
	},

	isDate: function(date) {
		if (date === null) {
			return false;
		}
		var dateArr = date.split('/');
		if (dateArr.length != 3 || isNaN(parseInt(dateArr[0])) || isNaN(parseInt(dateArr[1])) || isNaN(parseInt(dateArr[2])) ) {
			return false;
		}
	return true;
	},

	/****************************
	*** Service filter events ***
	****************************/

	/**
	Trigerred after the render of `gridsPanel` (containing `servicesGrid` and `granulesGrid`). Among other things,
	initializes the `productType` combobox and the `servicesGrid` table.
	*/
	onWindowLoaded: function() {

		this.productTypeCB = Ext.getCmp('epnTapProductTypeCB');
		this.targetClassCB = Ext.getCmp('epnTapTargetClassCB');
		this.targetNameCB = Ext.getCmp('epnTapTargetNameCB');
		this.timeSelector = Ext.getCmp('epnTapTimeSelector');
		this.rowsPerPageNf = Ext.getCmp('epnTapRowsPerPageNf');
		this.servicesGrid = Ext.getCmp('epnTapServicesGrid');
		this.granulesGrid = Ext.getCmp('epnTapGranulesGrid');
		this.currentPageLb = Ext.getCmp('epnTapCurrentPageLb');
		this.totalPagesLb = Ext.getCmp('epnTapTotalPagesLb');
		this.firstPageBtn = Ext.getCmp('epnTapFirstPageBtn');
		this.previousPageBtn = Ext.getCmp('epnTapPreviousPageBtn');
		this.nextPageBtn = Ext.getCmp('epnTapNextPageBtn');
		this.lastPageBtn = Ext.getCmp('epnTapLastPageBtn');

		this.productTypeCB.getStore().removeAll();
		this.productTypeCB.getStore().add({'id': 'all', 'name': 'All data product types'});
		for (var productTypeId in this.metadata) {
			if (productTypeId in this.productTypeDict) {
				this.productTypeCB.getStore().add({'id': productTypeId, 'name': this.prettify(this.productTypeDict[productTypeId])});
			} else {
				console.log('Unknown data product type "' + productTypeId + '"');
			}
		}
		this.productTypeCB.select('all');

		this.targetClassCB.getStore().removeAll();
		this.targetClassCB.getStore().add({'id': 'all', 'name': 'All target names'});
		this.targetClassCB.select('all');
		this.targetClassCB.disable();

		this.targetNameCB.getStore().removeAll();
		this.targetNameCB.getStore().add({'id': 'all', 'name': 'All target classes'});
		this.targetNameCB.select('all');
		this.targetNameCB.disable();

		this.updateServices();
	},

	/**
	Trigerred when a new item is selected in `productTypeCB` (see `EpnTapUI.createProductTypeCB()`). Among other things,
	updates the `targetClassCB` combobox and the `servicesGrid` table.
	*/
	onProductTypeCBChanged: function() {
		this.targetClassCB.getStore().removeAll();
		this.targetNameCB.getStore().removeAll();
		this.targetNameCB.disable();

		if (this.productTypeCB.value == 'all') {
			this.targetClassCB.disable();
		} else {
			var targetClasses = this.metadata[this.productTypeCB.value];

			if (Object.keys(targetClasses).length == 1) {
				this.targetClassCB.getStore().add({'id': Object.keys(targetClasses)[0], 'name': this.prettify(Object.keys(targetClasses)[0])});
				this.targetClassCB.disable();
				this.targetClassCB.select(this.targetClassCB.getStore().getAt(0)['internalId']);
			} else {
				this.targetClassCB.getStore().add({'id': 'all', 'name': this.allPrettify(this.productTypeDict[this.productTypeCB.value])});
				for (var targetClassId in targetClasses) {
					this.targetClassCB.getStore().add({'id': targetClassId, 'name': this.prettify(targetClassId)});
				}
				this.targetClassCB.select('all');
				this.targetClassCB.enable();
			}
		}
		this.targetNameCB.getStore().add({'id': 'all', 'name': 'All target names'});
		this.targetNameCB.select('all');
		this.updateServices();
	},

	/**
	Trigerred when a new item is selected in `targetClassCB` (see `EpnTapUI.createTargetClassCB()`). Among other things,
	updates the `targetNameCB` combobox and the `servicesGrid` table.
	*/
	onTargetClassCBChanged: function() {
		this.targetNameCB.getStore().removeAll();

		if (this.targetClassCB.value == 'all') {
			this.targetNameCB.getStore().add({'id': 'all', 'name': 'All target names'});
			this.targetNameCB.select('all');
			this.targetNameCB.disable();
		} else {
			var targetNames = this.metadata[this.productTypeCB.value][this.targetClassCB.value];

			if (Object.keys(targetNames).length == 1) {
				this.targetNameCB.getStore().add({'id': Object.keys(targetNames)[0], 'name': this.prettify(Object.keys(targetNames)[0])});
				this.targetNameCB.select(this.targetNameCB.getStore().getAt(0)['internalId']);
				this.targetNameCB.disable();
			} else {
				this.targetNameCB.getStore().add({'id': 'all', 'name': this.allPrettify(this.targetClassCB.value)});
				for (var targetNameId in targetNames) {
					this.targetNameCB.getStore().add({'id': targetNameId, 'name': this.prettify(targetNameId)});
				}
				this.targetNameCB.select('all');
				this.targetNameCB.enable();
			}
		}
		this.updateServices();
	},

	/**
	Trigerred when a new item is selected in `targetNameCB` (see `EpnTapUI.createTargetNameCB()`). Updates the
	`servicesGrid` table.
	*/
	onTargetNameCBChanged: function() {
		this.updateServices();
	},

	/**
	Trigerred when the value of `rowsPerPageNf` is updated (see `EpnTapUI.createRowsPerPageNf()`). Do nothing yet, used
	for debug purposes only.
	*/
	onRowsPerPageChanged: function() {
		console.log("rows per page: " + this.productTypeCB);
	},

	/*********************
	*** Buttons events ***
	*********************/

	/**
	Disable or enable the navigation buttons (see `EpnTapUI.createNavigationPanel()`).
	*/
	disableNavBtns: function(firt, previous, next, last) {
		Ext.getCmp('epnTapFirstPageBtn').setDisabled(firt);
		Ext.getCmp('epnTapPreviousPageBtn').setDisabled(previous);
		Ext.getCmp('epnTapNextPageBtn').setDisabled(next);
		Ext.getCmp('epnTapLastPageBtn').setDisabled(last);
	},

	/**
	Trigerred when the `firstPageBtn` button is clicked (see `EpnTapUI.createNavigationPanel()`). Among other things,
	send a new query and fill `granulesGrid`.
	*/
	onFirstPageBtnClicked: function() {
		var newPageNumber = 1;
		var limit = Number(this.rowsPerPageNf.value);
		var offset = 0;
		var selectedServiceURL = this.services[this.selectedServiceId]['accessurl'];

		this.wait();
		this.disableNavBtns(true, true, false, false);
		this.currentPageLb.setText('' + newPageNumber);
		AmdaAction.epnTapGetGranules(this.selectedServiceId, selectedServiceURL, this.filter, this.select, limit, offset, this.fillGranules);
	},

	/**
	Trigerred when the `previousPageBtn` button is clicked (see `EpnTapUI.createNavigationPanel()`). Among other things,
	send a new query and fill `granulesGrid`.
	*/
	onPreviousPageBtnClicked: function() {
		var newPageNumber = Number(this.currentPageLb.text) - 1;
		var limit = Number(this.rowsPerPageNf.value);
		var offset = (newPageNumber-1) * limit;
		var selectedServiceURL = this.services[this.selectedServiceId]['accessurl'];

		this.wait();
		this.currentPageLb.setText('' + newPageNumber);
		var isFirstPage = this.currentPageLb.text === '1';
		this.disableNavBtns(isFirstPage, isFirstPage, false, false);
		AmdaAction.epnTapGetGranules(this.selectedServiceId, selectedServiceURL, this.filter, this.select, limit, offset, this.fillGranules);
	},

	/**
	Trigerred when the `nextPageBtn` button is clicked (see `EpnTapUI.createNavigationPanel()`). Among other things,
	send a new query and fill `granulesGrid`.
	*/
	onNextPageBtnClicked: function() {
		var newPageNumber = Number(this.currentPageLb.text) + 1;
		var limit = Number(this.rowsPerPageNf.value);
		var offset = (newPageNumber-1) * limit;
		var selectedServiceURL = this.services[this.selectedServiceId]['accessurl'];

		this.wait();
		this.currentPageLb.setText('' + newPageNumber);
		var isLastPage = this.currentPageLb.text == this.totalPagesLb.text;
		this.disableNavBtns(false, false, isLastPage, isLastPage);
		AmdaAction.epnTapGetGranules(this.selectedServiceId, selectedServiceURL, this.filter, this.select, limit, offset, this.fillGranules);
	},

	/**
	Trigerred when the `lastPageBtn` button is clicked (see `EpnTapUI.createNavigationPanel()`). Among other things,
	send a new query and fill `granulesGrid`.
	*/
	onLastPageBtnClicked: function() {
		var newPageNumber = Number(this.totalPagesLb.text);
		var limit = Number(this.rowsPerPageNf.value);
		var offset = (newPageNumber-1) * limit;
		var selectedServiceURL = this.services[this.selectedServiceId]['accessurl'];

		console.log(newPageNumber, limit, offset, selectedServiceURL);

		this.wait();
		this.currentPageLb.setText('' + newPageNumber);
		this.disableNavBtns(false, false, true, true);
		AmdaAction.epnTapGetGranules(this.selectedServiceId, selectedServiceURL, this.filter, this.select, limit, offset, this.fillGranules);
	},

	/*******************
	*** Grids events ***
	*******************/

	/**
	Trigerred when a row is clicked in `servicesGrid` table (see `EpnTapUI.createServicesGrid()`). Among other things,
	send a new query and fill `granulesGrid`.
	*/
	onServiceSelected: function(selectedServiceId) {
		this.wait();
		this.selectedServiceId = selectedServiceId;
		var selectedServiceURL = this.services[selectedServiceId]['accessurl'];

		this.select = Array();
		if (! selectedServiceId in this.services) {
			throw this.selectedServiceId + ' not found in the list of services.';
			return;
		}

		var columns = this.services[selectedServiceId]['columns'].split(',');
		for (var ic=0 ; ic<this.granulesGrid.columns.length ; ic++) {
			if (columns.indexOf(this.granulesGrid.columns[ic].dataIndex) != -1) {
				this.select.push(this.granulesGrid.columns[ic].dataIndex);
			}
		}
		this.filter = Array(
			this.productTypeCB.value !== 'all' ? this.productTypeCB.value : null, // product type
			this.targetNameCB.value !== 'all' ? this.targetNameCB.value : null, // target name
			this.timeSelector.getStartTime() !== '' ? this.timeSelector.getStartTime() : null, // start time
			this.timeSelector.getStopTime() !== '' ? this.timeSelector.getStopTime() : null // stop time
		);

		AmdaAction.epnTapGetNbRows(selectedServiceId, selectedServiceURL, this.filter, this.updateNbRows);
		AmdaAction.epnTapGetGranules(selectedServiceId, selectedServiceURL, this.filter, this.select, this.rowsPerPageNf.value, 0, this.fillGranules);
	},

	/**
	Trigerred when a row is clicked in `granulesGrid` table (see `EpnTapUI.createGranulesGrid()`). Do nothing yet, used
	for debug purposes only.
	*/
	onGranuleSelected: function(ui) {
		console.log('selected granule: ' + granule.targetName);
	},

	/**********************
	*** Other functions ***
	**********************/

	/**
	Update the services store (see `EpnTapUI.servicesStore`), according to the field values in `serviceFilterPanel`.
	*/
	updateServices: function(ui) {
		this.servicesGrid.getStore().removeAll();
		this.granulesGrid.getStore().removeAll();

		var service = null;
		var newTimeMin = null;
		var newTimeMax = null;

		var timeMin = null;
		var timeMax = null;

		var productType = this.productTypeCB.value;
		var targetClass = this.targetClassCB.value;
		var targetName = this.targetNameCB.value;

		var filterDict = new Array();
		if(productType === 'all') {
			for (var dpt in this.metadata) {
				for (var tc in this.metadata[dpt]) {
					for (tn in this.metadata[dpt][tc]) {
						for (serv in this.metadata[dpt][tc][tn]) {
							service = this.metadata[dpt][tc][tn][serv];
							filterDict[serv] = service[0] + (serv in filterDict ? filterDict[serv] : 0);

							newTimeMin = Ext.Date.parse(service[1], 'd/m/Y');
							newTimeMax = Ext.Date.parse(service[2], 'd/m/Y');
							if (newTimeMin !== null && newTimeMin < timeMin || timeMin == null) {
								timeMin = newTimeMin;
							}
							if (newTimeMax !== null && newTimeMax > timeMax || timeMax == null) {
								timeMax = newTimeMax;
							}
						}
					}
				}
			}
		} else if (this.targetClassCB.value === 'all') {
			for (var tc in this.metadata[productType]) {
				for (tn in this.metadata[productType][tc]) {
					for (serv in this.metadata[productType][tc][tn]) {
						service = this.metadata[productType][tc][tn][serv];
						filterDict[serv] = service[0] + (serv in filterDict ? filterDict[serv] : 0);

						newTimeMin = Ext.Date.parse(service[1], 'd/m/Y');
						newTimeMax = Ext.Date.parse(service[2], 'd/m/Y');
						if (newTimeMin !== null && newTimeMin < timeMin || timeMin == null) {
							timeMin = newTimeMin;
						}
						if (newTimeMax !== null && newTimeMax > timeMax || timeMax == null) {
							timeMax = newTimeMax;
						}
					}
				}
			}
		} else if (this.targetNameCB.value === 'all') {
			for (tn in this.metadata[productType][targetClass]) {
				for (serv in this.metadata[productType][targetClass][tn]) {
					service = this.metadata[productType][targetClass][tn][serv];
					filterDict[serv] = service[0] + (serv in filterDict ? filterDict[serv] : 0);

					newTimeMin = Ext.Date.parse(service[1], 'd/m/Y');
					newTimeMax = Ext.Date.parse(service[2], 'd/m/Y');
					if (newTimeMin !== null && newTimeMin < timeMin || timeMin == null) {
						timeMin = newTimeMin;
					}
					if (newTimeMax !== null && newTimeMax > timeMax || timeMax == null) {
						timeMax = newTimeMax;
					}
				}
			}
		} else {
			for (serv in this.metadata[productType][targetClass][targetName]) {
				service = this.metadata[productType][targetClass][targetName][serv];
				filterDict[serv] = service[0] + (serv in filterDict ? filterDict[serv] : 0);

				newTimeMin = Ext.Date.parse(service[1], 'd/m/Y');
				newTimeMax = Ext.Date.parse(service[2], 'd/m/Y');

				if (newTimeMin !== null && newTimeMin < timeMin || timeMin == null) {
					timeMin = newTimeMin;
				}
				if (newTimeMax !== null && newTimeMax > timeMax || timeMax == null) {
					timeMax = newTimeMax;
				}
			}
		}

		this.timeSelector.setLimits(timeMin, timeMax);
		this.timeSelector.setInterval(timeMin, timeMax);

		var filter = Object.keys(filterDict).map(function(key) {
			return [key, filterDict[key]];
		});
		filter.sort(function(first, second) {
			return second[1] - first[1];
		});
		for (var s = 0; s < filter.length; s++) {
			var service = this.services[filter[s][0]];
			this.servicesGrid.getStore().add({'id': filter[s][0], 'nbResults': filter[s][1], 'shortName': service['shortname'], 'title': service['title'], 'accessURL': service['accessurl']});
		}
	},

	/**
	Callback function, called from the PHP script when the query result is received, when a service is selected.

	Among other things, update the `epnTapCurrentPageLb` label (see `EpnTapUI.createNavigationPanel()`).
	*/
	updateNbRows: function(nb_results) {
		var totalPages = Math.ceil(Number(nb_results) / Ext.getCmp('epnTapRowsPerPageNf').value);

		Ext.getCmp('epnTapCurrentPageLb').setText(totalPages == 0 ? '-' : '1');
		Ext.getCmp('epnTapTotalPagesLb').setText(totalPages == 0 ? '-' : totalPages);

		Ext.getCmp('epnTapPreviousPageBtn').setDisabled(true);
		Ext.getCmp('epnTapFirstPageBtn').setDisabled(true);
		Ext.getCmp('epnTapNextPageBtn').setDisabled(totalPages <= 1);
		Ext.getCmp('epnTapLastPageBtn').setDisabled(totalPages <= 1);
	},

	/**
	Callback function, called from the PHP script when the query result is received, when a service is selected or a
	navigation button is clicked.

	Among other things, fill the `epnTapGranulesGrid` table (see `EpnTapUI.granulesStore`).
	*/
	fillGranules: function(granules) {
		if (granules == null) {
			console.log("There is no granules to add.");
		} else {
			try {
				console.log('Added granules:', granules);
				Ext.getCmp('epnTapGranulesGrid').getStore().removeAll();
				Ext.getCmp('epnTapGranulesGrid').getStore().add(granules);
			} catch( e ) {
				console.log('Can not add granules: ' + e);
			}
		}
		Ext.getCmp('epnTapServicesGrid').setDisabled(false);
		Ext.getCmp('epnTapServicesGrid').getEl().setStyle('cursor', 'default'); // CSS is correctly changed but without visible result.
	},

	/**
	Called before to send a query. Set the EpnTap panel in "waiting mode", informing to the user that a request is
	processing. The altered elements are resetted in `fillGranules()`.
	*/
	wait: function() {
		this.servicesGrid.getEl().setStyle('cursor', 'wait');
		this.servicesGrid.setDisabled(true); // CSS is correctly changed but without visible result.
	}

});