// Vignetteur.js
// (C) Renaud LAURETTE 2017-2020
//
//  Utilitaire de traitement de photos pour le chargement sur un site web
//  - Acceptation de photos JPEG en mode drag & drop
//	- Redressement automatique selon balise EXIF
//	- Réduction de taille jusqu'à rentrer dans un carré de dimension donnée
//  - Insertion d'un bandeau de signature
//  - Génération de vignette
//  - Extraction de données EXIF : caption, position GPS, date

function Vignetteur(dropArea, previewArea, formName, callback) {
	// parametres
	this.dropArea = dropArea;       // id de la zone de dépose des fichiers à réduire
	this.previewArea = previewArea; // id de la zone d'affichage des miniatures
	this.formName = formName;       // id du formulaire par lequel les images choisies pouront être chargées sur le serveur après réduction.
	this.callback = callback;       // callback d'exploitation des informations EXIF
	// initialisations;
	this.signature = function() {   // fonction retournant une chaine de caractères pour signer les images
                   return "";
                   };
	this.nbImages = 0;        	    // nombre d'images déja déposées
	this.maxImages = 4;       		// nombre maximal d'images acceptable - peut être modifié
	this.orientation = 1;     		// orientation EXIF par défaut
	this.dropbox = null;
	
	// quand il n'y a pas de formulaire, on limite à une image
	if(this.formName == null)
		this.maxImages = 1;
	if(this.dropArea) {
		this.dropbox = document.getElementById(this.dropArea);	
		// Lancement des gestionnaires d'événements sur la dropArea quand elle existe
		this.dropbox.addEventListener("dragenter", this.dragenter, false);
		this.dropbox.addEventListener("dragover", this.dragover, false);
		this.dropbox.addEventListener("drop", this.drop2.bind(this), false);
	}
  	// console.log('Orientation initiale=%d. ',this.orientation);
};

Vignetteur.prototype = {
  	
  	dragenter: function(e) {
  	  e.stopPropagation();
  	  e.preventDefault();
  	},

  	dragover: function(e) {
  	  e.stopPropagation();
  	  e.preventDefault();
  	}, 

  	reset: function() {
  		this.nbImages = 0;
  	},
/*
 * Gestion du drop d'un fichier image dans la zone dont l'id est dropArea
 */
  	drop2: function(e) {
  		e.stopPropagation();
  		e.preventDefault();
  		var dt = e.dataTransfer;
  		var files = dt.files;
  		this.handleFiles(files);
  	},
  	
  	handleFiles: function(files) {
  		var fileList = document.getElementById(this.previewArea);
		
  		if (!files.length ||(this.nbImages >= this.maxImages)) {
  			var perr = document.createElement("p");
  			perr.innerHTML = "Aucun fichier ou trop d'images";
  			fileList.appendChild(perr);
  		} else {
  			for (var i = 0; (i < files.length) && (this.nbImages < this.maxImages); i++) {
  				var li = document.createElement("div");
  				li.setAttribute('class','vignette');
  				fileList.appendChild(li);
			
  				var img = new Image();
  				img.reduite = document.createElement("canvas");
  				img.vignette = document.createElement("canvas");
  				img.src = window.URL.createObjectURL(files[i]);	
  				img.iname = files[i].name;
  				this.orientation = 1;
  				var that = this;
  				img.onload = function(e) {
                    EXIF.getData(this, function() {
                        // Lecture de l'orientation EXIF pour pouvoir retourner l'image si besoin
                        that.orientation = EXIF.getTag(this, "Orientation");
                        console.log('Orientation EXIF=%d. ',that.orientation);
                        that.reworkImage(this,1024,192,this.iname);
                        // On controle que la callback est bien une fonction
                        if(!!(that.callback && that.callback.constructor 
  						                    && that.callback.call && that.callback.apply)) {  
                            // Lecture des données EXIF, uniquement si
  						    // une fonction est définie pour les exploiter
  							var odate = EXIF.getTag(this, "DateTimeOriginal");
  							var latdeg = EXIF.getTag(this,"GPSLatitude");
  							var londeg = EXIF.getTag(this,"GPSLongitude");
  							var gps = false;
  							var lat = 0;
  							var lon = 0;
  							console.log('latdeg=%s londeg=%s',latdeg,londeg);
  							// On vérifie que les données GPS sont présentes avant de traiter
  							if((typeof latdeg !=='undefined') && (typeof londeg !== 'undefined')) {
  								lat = latdeg[0] + (latdeg[1]*60 + latdeg[2])/3600;
  								var latref = EXIF.getTag(this,"GPSLatitudeRef");
  								if(latref == 'S') lat = -lat;
  								lon = londeg[0] + (londeg[1]*60 + londeg[2])/3600;
  								var lonref = EXIF.getTag(this,"GPSLongitudeRef");
  								if(latref == 'W') lon = -lon;
  								// console.log("VGN: Latref: "+latref+" ; Longref: "+lonref);
  								// console.log('VGN! lat=%f lon=%f. ',lat,lon);
  								gps = true;
  							}
  							var caption = EXIF.getIptcTag(this,"caption");
  							//document.getElementById("msg").innerHTML=`${lat} ${lon}`;
  							that.callback({
  									id: that.nbImages,
  									gps: gps,
  									lat: lat,
  									lon: lon,
  									date: odate,
  									caption: caption
  							});
                          }
  						});
  						// that.reworkImage(this,1024,192,this.iname);
  					};
  				
  				li.appendChild(img.vignette);
  				// on garde les données sur la source de l'image pour un traitement ultérieur
  			    if(window.sessionStorage) {
  			    	window.sessionStorage.setItem("imageObsRapide",JSON.stringify(files[i]));
  			    }
  			}
  		}
  	},  	

  	handleCanvas: function(canvas) {
  		var fileList = document.getElementById(this.previewArea);
  		var li = document.createElement("div");
  		li.setAttribute('class','vignette');
  		fileList.appendChild(li);
  		var img = canvas;
  		img.reduite = document.createElement("canvas");
  		img.vignette = document.createElement("canvas");
  		img.iname = "photo";
  		this.orientation = 1;
  		this.reworkImage(img,1024,300,img.iname);
  		li.appendChild(img.vignette);
  	},
  	
/*
 * Rechargement d'une image qui a été stockée en sessionStorage
 */
  	loadSessionImage: function() {
  		if(window.sessionStorage && window.sessionStorage.getItem("testObsRapide")) {
  			if(this.formName != null) {
  				// Si un formulaire existe : Insertion dans le formulaire
  				// Plusieurs images peuvent ainsi être insaraes
  				var inp = document.createElement("input");
  				inp.setAttribute('name','fichobs'+this.nbImages);
  				inp.setAttribute('type','hidden');
  				inp.setAttribute('value',window.sessionStorage.getItem("imageObsRapide"));
  				document.getElementById(this.formName).appendChild(inp);
  			} 
		  	window.sessionStorage.removeItem("imageObsRapide");		
		  	
  			var vignette = new Image();
  			var fileList = document.getElementById(this.previewArea);
  			vignette.src = window.sessionStorage.getItem("vignetteObsRapide");
  			var li = document.createElement("div");
  			li.setAttribute('class','vignette');
  			fileList.appendChild(li);
  			li.appendChild(vignette);
  			window.sessionStorage.removeItem("vignetteObsRapide");
  			
  			window.sessionStorage.removeItem("testObsRapide");
  			this.nbImages++;
  		}
  		return this;
  	},

/*
 * Redimensionnement d'une image à smax pixels dans la plus grande dimension.
 * Effectue également la rotation nécessaire en fonction de l'orientation EXIF
 * précédement détectée et enregistrée dans this.orientation.
 */
	resizeImage: function(img, canvas, smax) {
		var width = img.width;
		var height = img.height;
		// Calcul des dimensions cible
		var ratio = width / height;	
		if (width > height) {
			if(width > smax) {
				height = height * smax / width;
				width = smax;
			}
		} else {
			if(height > smax) {
				width = width * smax / height;
				height = smax;
			}
		}
		// récupération et sauvegarde du contexte avant transfo
		var ctx = canvas.getContext("2d");
		ctx.save(); 
        // dimensionnement du canvas avant transfo
        // console.log('Dimensions pre-orientation %d x %d.',width,height);
        // console.log('Orientation resize %d = %d.',smax,this.orientation);
        /* -- La correction d'orientation est maintenant automatique
        if (4 < this.orientation && this.orientation < 9) {
          canvas.width = height;
          canvas.height = width;
        } else {
        */
          canvas.width = width;
          canvas.height = height;
        /* }     */
        console.log('Dimensions cible %d x %d.',canvas.width,canvas.height);
        // transformation du contexte avant de dessiner l'image
        // transform(a,b,c,d,e,f) :
        //  x1,y1 = coord dans l'image d'origine
        //	x2 = a*x1 + c*y1 + e = largeur à l'écran (gauche vers droite)
        //  y2 = b*x1 = d*y1 + f = hauteur à l'écran (haut vers bas)
        switch (this.orientation) {
        	/* -- La correction d'orientation est maintenant automatique
          case 2: ctx.transform(-1, 0, 0, 1, width, 0); break;
          case 3: ctx.transform(-1, 0, 0, -1, width, height ); break;
          case 4: ctx.transform(1, 0, 0, -1, 0, height ); break;
          case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;
          case 6: ctx.transform(0, 1, -1, 0, height , 0); break;
          case 7: ctx.transform(0, -1, -1, 0, height , width); break;
          case 8: ctx.transform(0, -1, 1, 0, 0, width); break;
          */
          default: break;
        }
        // dessin de l'image aux dimensions réduites
		ctx.drawImage(img,0,0,width,height);
		// Remise en place du contexte
		switch (this.orientation) {
			/* -- La correction d'orientation est maintenant automatique
			case 2: ctx.transform(-1, 0, 0, 1, width, 0); break;
			case 3: ctx.transform(-1, 0, 0, -1, width, height ); break;
			case 4: ctx.transform(1, 0, 0, -1, 0, height ); break;
			case 5: ctx.scale(1, -1); ctx.rotate(-0.5 * Math.PI); break;
			case 6: ctx.translate(0, height);ctx.rotate(-0.5 * Math.PI); break;
			case 7: ctx.scale(-1, 1); ctx.translate(-width, height);ctx.rotate(-0.5 * Math.PI); break;
			case 8: ctx.translate(width, 0); ctx.rotate(0.5 * Math.PI); break;
			*/
			default: break;
		}
 		// préparation des valeurs de retour
		width = canvas.width;
		height = canvas.height;
		// console.log('Dimensions post resize WxH = %d x %d.',width,height);              
		return {
			contexte: ctx,
			width: width,
			height: height
		};
	},
/*
 * Assignation d'une fonction qui retournera une chaine de caractères
 * pour signer la photo.
 */
	setSignature: function(signature) {
		this.signature = signature;
		return this;
	},
/*
 * Traitement global d'une image déposée
 * - redimensionnement et rotation de l'image à charger
 * - attachement de cette image à un formulaire de téléchargement (formName)
 * - génération d'une vignette pour la page courante
 * - attachement de la vignette dans la page (previewArea)
 */
	reworkImage: function(img,smax,vmax,fname) {
		// Redimensionnement de l'image à télécharger 
		console.log('Dimensions image WxH = %d x %d.',img.width,img.height);
		var sres = this.resizeImage(img,img.reduite,smax);
		console.log('Dimensions image réduite WxH = %d x %d.',img.reduite.width,img.reduite.height);
		var sctx = sres.contexte; 
		// Insertion du bandeau de signature 
		var sig = this.signature();
		console.log('Signature retournee ',sig);
		if(sig.signature.length > 0) {
			console.log('Dimensions signature WxH = %d x %d.',sres.width,sres.height);
			sctx.globalAlpha = 0.6;
			sctx.fillStyle = "white";
			sctx.fillRect(0,sres.height-20,sres.width,20);
			sctx.font="bold 11px Arial";
			sctx.fillStyle = "black";
			sctx.fillText(sig.signature,10,sres.height-7);	
		}
		// Fabrication de la vignette	
		var vres = this.resizeImage(img,img.vignette,vmax);	
		console.log('Dimensions vignette WxH = %d x %d.',img.vignette.width,img.vignette.height);
		// Enregistrement du résultat
		if(this.formName != null) {
			// Si un formulaire existe : Insertion dans le formulaire
			// Plusieurs images peuvent ainsi être insérées
			var inp = document.createElement("input");
			inp.setAttribute('name','fichobs'+this.nbImages);
			inp.setAttribute('type','hidden');
			inp.setAttribute('value',img.reduite.toDataURL("image/jpeg"));
			document.getElementById(this.formName).appendChild(inp);
			// insertion du nom de l'auteur
			inp = document.createElement("input");
			inp.setAttribute('name','sigobs'+this.nbImages);
			inp.setAttribute('type','hidden');
			inp.setAttribute('value',sig.name);
			document.getElementById(this.formName).appendChild(inp);
		}  else {
		   // En l'absence de formulaire, une seule image autorisée
		   // On enregistre l'image et sa miniature en stockage local, pour pouvoir la réutiliser ultérieurement.
			if (window.sessionStorage) {
				window.sessionStorage.setItem("imageObsRapide",img.reduite.toDataURL("image/jpeg"));     
				window.sessionStorage.setItem("vignetteObsRapide",img.vignette.toDataURL("image/jpeg"));
				window.sessionStorage.setItem("testObsRapide",JSON.stringify(true));
			}
		}
		this.nbImages++;
	}
};	

