/*=========================================================
	MODULE zJsTables
	----------------------------------------------------
	Classe Table pour gestion des tables de données
	V1.0 : Création
	V2.0 : Transformation structure en XML (anciennement JSON)
=========================================================*/
import * as zLib from "./zJsLibrary.js";

export class TableXML {
	constructor(objTableStructure) {
		zLib.getXmlRoot(objTableStructure)
		.then(xmlRoot => {
			//console.log(xmlRoot);
			const parseError = xmlRoot.getElementsByTagName('parsererror');
			if(parseError.length) console.error('Error parsing XML table:\n',parseError[0].innerText);

			let parent = null;
			// Identification du conteneur parent
			if(xmlRoot.hasAttribute('containerId')) parent = document.getElementById(xmlRoot.getAttribute('containerId'));
			
			// En l'absence de parent, une div dédiée est crée
			if(!parent) parent = document.createElement('div');

			// Variable internes à la classe
			//this.viewName				= containerId;
			this.container				= parent;
			this.xmlStructure			= xmlRoot;
			// Paramètres spécifique aux table paginées
			if(xmlRoot.hasAttribute('paging')) {
				this.paging				= true;
				this.pagingIndex		= 0;
				this.pagingSize		= 1;
				this.maxRow				= 0;
			}
			// Clés de tri
			//const defaultSortingKeys = xmlRoot.getAttribute('defaultSortingKeys');
			//this.sortingKeys = (defaultSortingKeys)? defaultSortingKeys : [];
			this.sortingKeys = [];

			// construction de la structure du tableau
			this.build();

		})
	}
 
	/*================================================================
					FONCTIONS DE CONSTRUCTION
	=================================================================*/

	// Construction de la table
	async build() {
		this.buildStructure()
		.then(() => this.buildPaging())
		.then(() => { if(zLib.attr(this.xmlStructure, 'query')) this.fillTable() })
		.catch(err => console.error(err));
	}

	async buildStructure() {
		//console.log("> buildStructure");
		//console.log('... container:', this.container);
		
		// Construction de la div secondaire
		const childContainer = document.createElement('div');
		childContainer.classList = 'zTableContainer';
		// Création de la table
		const table = document.createElement('table');
		//table.classList = "zCompact";
		// Création de l'entête
		const thead = document.createElement('thead');
		// Création de la ligne d'entête
		const tr    = document.createElement('tr')

		// Génération cellules d'entête
		const columns = this.xmlStructure.querySelectorAll('column');
		for (let column of columns) {
			const colLabel = zLib.getLabel(column);
			const colField = zLib.attr(column, 'field');

			const th = document.createElement('th');

			if(colField) th.dataset['field'] = colField;
			th.classList = `unselectable`;
			if(column.hasAttribute('sort')) {
				th.classList.add("sortable");
				// Enregistrement des clés de tri actives
				if(zLib.attr(column, 'sort')!='') this.sortingKeys.push({key:colField, direction:zLib.attr(column, 'sort')});

				// Création du titre et de l'indicateur de tri
				const div = document.createElement("div");
				div.classList = "zFlexRow zHCenter zVCenter";
				const title = document.createElement("span");
				// Teste de l'entête
				title.textContent = colLabel?? '';
				
				const tag = document.createElement("span");
				tag.classList = "sort zMgL";
				const rank = document.createElement("span");
				rank.classList = "sortingRank";
				div.appendChild(title);
				div.appendChild(tag);
				div.appendChild(rank);
				th.appendChild(div);

				// Ajout du listener 
				th.addEventListener("click", () => {
					// Identification de la cellule
					const fieldKey = th.dataset["field"];
					// Recherche de la clé de tri
					const sKey = this.sortingKeys.find((obj) => obj.key==th.dataset["field"]);
					console.log(sKey);
					if(sKey) {
						if(sKey.direction=="ASC") {
							sKey.direction = "DESC";
						} else this.sortingKeys.splice(this.sortingKeys.indexOf(sKey), 1);
					} else {
						this.sortingKeys.push({key:fieldKey, direction:"ASC"});
					}
					this.setSortingTag();
					this.fillTable();
				});

			} else th.textContent = colLabel?? '';
			if(zLib.attr(column, 'width'))	th.style.width = zLib.attr(column, 'width');
			if(zLib.attr(column, 'grow'))		th.style.flexGrow = zLib.attr(column, 'grow');
			tr.appendChild(th);
		}
		// Insertion des de la ligne d'entête dans l'entête			
		thead.appendChild(tr);
		// Insertion de l'entête dans la table			
		table.appendChild(thead);
		// Création du corps de la table
		const tbody = document.createElement('tbody');
		// Insertion du corps dans la table
		table.appendChild(tbody);
		// Insertion de la table dans la div secondaire
		childContainer.appendChild(table);
		// Insertion du conteneur secondaire dans le conteneur parent
		this.container.appendChild(childContainer);
		// Affichage des indicateurs de tri
		this.setSortingTag();
		// Exécution de la requête de comptage si elle existe
		//if(this.xmlStructure.hasAttribute('countQuery')) this.maxRow = await this.countRows();
		if(this.xmlStructure.hasAttribute('countQuery')) this.countRows();
	}

	async countRows() {
		console.log("> countRows");
		zLib.dbQuery({query:this.xmlStructure.getAttribute('countQuery')})
		.then(response => {
			this.maxRow = response[0].nRow;
			//console.log("Count query response:", response);
			//return response[0].nRow;
		})
		.catch((error) => reject(error))
	}
	async setSortingTag() {
		const thx = this.container.querySelectorAll("th.sortable");
		if(thx) {
			thx.forEach((thz) => {
				const tag = thz.querySelector("span.sort");
				const rank = thz.querySelector("span.sortingRank")
				// Reset indicateur de sens et Rang
				tag.classList = "sort zMgL";
				rank.innerHTML = "";
				const sKey = this.sortingKeys.find((obj) => obj.key==thz.dataset["field"]);
				if(sKey) {
					tag.classList.add((sKey.direction.toUpperCase()=="DESC")? "up" : "down");
					rank.innerHTML = this.sortingKeys.indexOf(sKey)+1;
				}
			});
		}
	}

	async buildPaging() {
		//console.log("> buildPaging");
		//return new Promise((resolve) => {
			// Affichage table en mode pages
			if(this.paging) {
				const pagingContainter = document.createElement('div');
				pagingContainter.classList = "zPagingContainer"
				const pgLeft = document.createElement('div');
				pgLeft.classList = "zFlexRow zFGrow";
				const pgRight = document.createElement('div');
				pgRight.classList = "zFlexRow";
				// Cmd première page
				const setFirst = document.createElement('div');
				setFirst.classList = "zPagingCmd";
				setFirst.innerHTML = "&#8676;";
				setFirst.addEventListener('click', () => this.changePage('first') );
				// Cmd page précédente
				const setPrevious = document.createElement('div');
				setPrevious.classList = "zPagingCmd"
				setPrevious.innerHTML = "&#8672;";
				setPrevious.addEventListener('click', () => this.changePage('previous'));
				// Zone d'affichage/Saisie n° de page
				const setPage = document.createElement('input');
				setPage.type = 'text';
				setPage.id = "page";
				setPage.classList = "zPagingInput"
				setPage.setAttribute("autocomplete", "off");
				setPage.value = "1";
				setPage.addEventListener('input', (event) => {
					event.target.value = event.target.value.replace(/[^0-9]/g, '');
				});
				setPage.addEventListener('keydown', (event) => {
					if(event.key==="Enter") this.changePage(event.target.value)
				});
				// Cmd page suivante
				const setNext = document.createElement('div');
				setNext.classList = "zPagingCmd"
				setNext.innerHTML = "&#8674;";
				setNext.addEventListener('click', () => this.changePage('next'));
				// Cmd dernière page
				const setLast = document.createElement('div');
				setLast.classList = "zPagingCmd"
				setLast.innerHTML = "&#8677;";
				setLast.addEventListener('click', () => this.changePage('last'));

				pgRight.appendChild(setFirst);
				pgRight.appendChild(setPrevious);
				pgRight.appendChild(setPage);
				pgRight.appendChild(setNext);
				pgRight.appendChild(setLast);
				pagingContainter.appendChild(pgLeft);
				pagingContainter.appendChild(pgRight);
				this.container.insertBefore(pagingContainter, this.container.firstChild);
				// Calcul des paramètres de pages
				this.setPageSizing();

				// Mise en place d'un listener pour surveiller les modification de taille du navigateur
				let resizeTimeout;
				window.addEventListener('resize', () => {
					clearTimeout(resizeTimeout);
					resizeTimeout = setTimeout(() => {
						this.setPageSizing();
						this.changePage("update");
					}, 200); // Attendez 200ms après l'arrêt du redimensionnement
				});

				// Gestion des actions sur la roulette souris
				this.container.querySelector("div.zTableContainer").addEventListener("wheel", (event) => {
					if(event.deltaY>0) this.changePage('next');
					if(event.deltaY<0) this.changePage('previous');
				}, {passive: true});
			}
			//resolve(true);
		//})
	}

	/*=========================================================
						FONCTIONS DE REMPLISSAGE DES TABLES
	=========================================================*/
	async fillTable(fillObj={}) {
		//console.log("> fillTable");
		//console.log(fillObj);
		// Si pas d'objet ni de requête dans la structure
		if(!fillObj && !this.xmlStructure.hasAttribute('query')) return;

		const startTime = performance.now();
		if(!fillObj.dataset) {
			var fillQuery = fillObj.query ?? (this.xmlStructure.getAttribute('query'))? decodeURIComponent(this.xmlStructure.getAttribute('query')) : null;
			if(!fillQuery) return true;

			// Récupération des clés de tri
			//console.log('query:', fillQuery);
			//console.log('sortingKeys:', this.sortingKeys);
			if(this.sortingKeys.length>0) {
				fillQuery += " ORDER BY ";
				var sortCritera = [];
				this.sortingKeys.forEach((item) => sortCritera.push(item.key+" "+item.direction));
				fillQuery += sortCritera.join(", ");
			} 

			// Génération de la LIMIT et de l'OFFSET (affichage en mode page)
			if(this.paging) {
				// Limitation de la requête
				fillQuery += ` LIMIT ${this.pagingSize} OFFSET ${this.pagingIndex*this.pagingSize}`;
			}

			// Lecture du jeu de donnée via la requête
			fillObj.dataset = await zLib.dbQuery({query:fillQuery});

		}

		const loadingTime = performance.now()- startTime
		//console.log(loadingTime+" ms");

		const tbody = this.container.querySelector("tbody")
		tbody.innerHTML = '';
		fillObj.dataset.forEach(record => {
			const tr = document.createElement('tr');
			if(this.xmlStructure.hasAttribute('selectable')) {
				tr.classList.add('selectable')
				tr.setAttribute('ondblclick', `${this.xmlStructure.getAttribute('selectable')}(this)`);
			}
			if(this.xmlStructure.hasAttribute('alternateColor')) tr.classList.add('alternateColor');
			if(this.xmlStructure.hasAttribute('keys')) {
				const keys = zLib.parseArray(this.xmlStructure.getAttribute('keys'))
				keys.forEach(key => tr.setAttribute('data-'+key, record[key]));
			}
			//tr.setAttribute('ondblclick', 'tableEdit(this)');
			const columns = this.xmlStructure.querySelectorAll('column');
			for (let column of columns) {
				const colField		= zLib.attr(column, 'field');
				const colRecord	= colField? record[colField]?? null : null; 
					
				const td = document.createElement('td');
				if(zLib.attr(column, 'align'))	td.classList.add('td'+zLib.capitalizeFirstLetter(zLib.attr(column, 'align')));
				if(zLib.attr(column, 'width'))	td.style.width = zLib.attr(column, 'width');
				if(zLib.attr(column, 'grow'))		td.style.flexGrow = zLib.attr(column, 'grow');
				if(zLib.attr(column, 'format')=='checkbox') {
					const checkbox = document.createElement('input');
					checkbox.type = 'checkbox';
					checkbox.checked = colField ? record[colField] : false;
					td.appendChild(checkbox);
				} else if(colField) {
					//if(colRecord) td.insertAdjacentHTML('afterbegin', colRecord);
					if(colRecord) td.innerHTML = colRecord;
				}
				tr.appendChild(td);
			}
			tbody.appendChild(tr);
		})
		tbody.parentElement.appendChild(tbody);

		// Temps d'exécution
		const endTime = performance.now();
		const elapseTime = (endTime - startTime);
		if(messageBar) messageBar.push({msg:`${fillObj.dataset.length} enregistrements récupérés en ${elapseTime.toFixed(3)}ms`, TTL:5, priority:999});
	}

	// Ajout d'une ligne dans la table
	async addRow(data, top=false) {
		console.log(`Table add row from data`);
		console.log(data);

		const tbody = this.container("table tbody");
		const tr = document.createElement('tr');
		if(this.structure.selectable) {
			tr.classList = 'selectable';
			tr.setAttribute('ondblclick', `${this.structure.selectable}(this)`);
		}
		if(this.structure.alternateColor) tr.classList.add('alternateColor');
		if(this.structure.keys) this.structure.keys.forEach(key => tr.setAttribute('data-'+key, data[key]));
		this.structure.column.forEach(column => {
			const td = document.createElement('td');
			if(column.align=='center') td.classList.add('tdCenter');
			if(column.width) td.style.width = column.width;
			if(column.grow) td.style.flexGrow = column.grow;
			if(column.field) {
				td.setAttribute('data-field', column.field);
				td.textContent = data[column.field];
			}
			if(column.function) {
				td.setAttribute('data-function', column.function);
				const obj = window[column.function](data);
				td.appendChild(obj);
			}
			tr.appendChild(td);
		})
		if(!top) tbody.appendChild(tr);
		else tbody.insertBefore(tr, tbody.firstChild);
	}

	async removeRow(data) {
		//console.log(`Table '${viewName}' remove row with data`);
		console.log(data);

		// Lecture du fichier de paramètres de la table
		var query = "table tr";
		if(this.structure.keys) this.structure.keys.forEach(key => query += `[data-${key}='${data[key]}']`)
		console.log(query);
		const tr = document.querySelector(query)
		if(tr) tr.parentNode.removeChild(tr);
	}

	async updateRow(data) {
		//console.log(`Table '${viewName}' update row with data`);
		console.log(data);
		
		// Lecture du fichier de paramètres de la table
		var query = `table tr`;
		if(this.structure.keys) this.structure.keys.forEach(key => query += `[data-${key}='${data[key]}']`)
		console.log(query);
		const tr = this.container.querySelector(query)
		if(tr) {
			this.structure.column.forEach(column => {
				if(column.field) {
					const td = tr.querySelector(`td[data-field='${column.field}'`);
					td.textContent = data[column.field];
				}
				if(column.function) {
					const td = tr.querySelector(`td[data-function='${column.function}'`);
					td.innerHTML = '';
					const obj = window[column.function](data);
					td.appendChild(obj);
				}
			})
		}
	}

	/*=========================================================
					FONCTIONS ANNEXES
	=========================================================*/
	setPageSizing() {
		// Récupération des valeurs inscrites dans les variables CSS
		const rowHeight		= zLib.sizingToPxFloat(zLib.getCssRootVariable('--tableRow-height'));
		const fieldHeight		= zLib.sizingToPxFloat(zLib.getCssRootVariable('--genericField-height')); 
		const spacingDefault	= zLib.sizingToPxFloat(zLib.getCssRootVariable('--spacing_default')); 

		var reservedSpace = fieldHeight+spacingDefault*2;
		// Récupération des données de positionnement graphique de la table
		const table = this.container.querySelector(`table`);
		const th = this.container.querySelector(`table th`);
		const tableRect = table.getBoundingClientRect();
		const headerRect = th.getBoundingClientRect();
		const footerRect = document.getElementById("zFooter").getBoundingClientRect();

		reservedSpace += headerRect.height;

		if(tableRect.width>footerRect.width) reservedSpace += 15.34; // Ajouter la hauter de l'ascenseur horizontal
		const availableSpace = footerRect.top-tableRect.top-reservedSpace;
		this.pagingSize 	= Math.trunc(availableSpace/rowHeight);				  
	}

	changePage(change) {
		//console.log("Change page:", change);
		var newIndex = this.pagingIndex;
		const maxIndex = Math.trunc(this.maxRow/this.pagingSize);
		if(isNaN(change)) {
			switch(change) {
				case "first":
					newIndex = 0;
					break;
				case "next":
					newIndex = this.pagingIndex+1;
					break;
				case "previous":
					newIndex = this.pagingIndex-1;
					break;
				case "last":
					newIndex = maxIndex;
					break;
			}
		} else {
			newIndex = change-1;
		}
		if(newIndex<0) newIndex = 0;
		if(newIndex>maxIndex) newIndex = maxIndex;

		//console.log(index, newIndex);
		const pageHolder = document.getElementById("page");
		if(pageHolder) pageHolder.value = newIndex+1;
		if(newIndex!==this.pagingIndex || change=="update") {
			this.pagingIndex = newIndex;
			this.fillTable();
		}
	}

// FIN DE DECLARATION DE LA CLASSE
}