package Model;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;

import View.DrawingFrame;

public class Pretraitement {

	///////////////////////////////////////////////////////
	// POUR CREATION DU MODELE
	static Model model = new Model();
	// Nombre de signatures max par personne
	static int nbSignatures = 5;
	///////////////////////////////////////////////////////

	// Taille des images PNG
	static int size = 1300;
	// Nombre de points souhaités en fin de traitement
	static int nbPoints = 1000;

	// Récupération de la liste des points pour chaque traitement d'acquisition
	static List<List<Point>> originePointList = new ArrayList<List<Point>>();
	static List<List<Point>> interpolatedPointList = new ArrayList<List<Point>>();
	static List<List<Point>> alignedPointList = new ArrayList<List<Point>>();
	static List<List<Point>> rotatedPointList = new ArrayList<List<Point>>();
	static List<List<Point>> normalizedPointList = new ArrayList<List<Point>>();
	static List<List<Point>> resampledPointList = new ArrayList<List<Point>>();
	static List<Point> originePointListIdentification;
	static List<Point> interpolatedPointListIdentification;
	static List<Point> alignedPointListIdentification;
	static List<Point> rotatedPointListIdentification;
	static List<Point> normalizedPointListIdentification;
	static List<Point> resampledPointListIdentification;

	
	public static void init() {
		originePointList.clear();
		interpolatedPointList.clear();
		alignedPointList.clear();
		rotatedPointList.clear();
		normalizedPointList.clear();
		resampledPointList.clear();
	}
	
	/**
	 * Crée une image graphique
	 * @param pointList, La liste de points
	 * @param size, La taille de l'image
	 * @return une image graphique
	 */
	public static RenderedImage myCreateImage(List<Point> pointList, int size) {
		int height = size;
		int width = height;

		// Create a buffered image in which to draw
		BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

		// Create a graphics contents on the buffered image
		Graphics2D g2d = bufferedImage.createGraphics();

		// Draw graphics
		g2d.setColor(Color.white);
		g2d.fillRect(0, 0, size, size);
		for(int i=0; i<pointList.size(); i++) {
			g2d.setColor(Color.black);
			g2d.fillRect((int) Math.round(pointList.get(i).getPoint().x), (int) Math.round(pointList.get(i).getPoint().y), 1, 1);
		}

		// Graphics context no longer needed so dispose it
		g2d.dispose();

		return bufferedImage;
	}

	/**
	 * Transforme une liste de points en une image PNG
	 * @param filenameOutput
	 * @param pointList, La liste de points
	 * @param size, La taille de l'image
	 */
	static void listToPNG (String filenameOutput, List<Point> pointList, int size) {
		// Create an image to save
		RenderedImage rendImage = myCreateImage(pointList, size);

		// Write generated image to a file
		try {
			// Save as PNG
			File file = new File(filenameOutput+".png");
			ImageIO.write(rendImage, "png", file);
		}
		catch (IOException e) {

		}
	}

	/**
	 * Supprime les points acquis sur un même temps
	 * @param pointList, La liste d'interface points
	 * @return la liste des points (1 par moment d'acquisition)
	 */
	static List<Point> IdeleteSameTime (List<IPoint> pointList) {
		float previousTime = 0;
		List<Point> newPointList = new ArrayList<Point>();
		for (int i=0; i<pointList.size(); i++) {
			if (pointList.get(i).getTime() >= previousTime+16) {
				newPointList.add((Point) pointList.get(i));
				previousTime = pointList.get(i).getTime();
			}
		}
		return newPointList;
	}

	/**
	 * Supprime les points acquis sur le même temps
	 * @param pointList, La liste de points
	 * @return la liste des points (1 par moment d'acquisition)
	 */
	static List<Point> deleteSameTime (List<IPoint> pointList) {
		float previousTime = 0;
		List<Point> newPointList = new ArrayList<Point>();
		for (int i=0; i<pointList.size(); i++) {
			if (pointList.get(i).getTime() >= previousTime+16) {
				newPointList.add((Point) pointList.get(i));
				previousTime = pointList.get(i).getTime();
			}
		}
		return newPointList;
	}

	/**
	 * Indique la présence d'un lever de stylo entre deux points
	 * @param point1, le premier point
	 * @param point2, le second point
	 * @return oui ou non
	 */
	public static boolean penUp (Point point1, Point point2) {
		return point1.getTime() <= point2.getTime()-DrawingFrame.delayBetweenLines;
	}

	/**
	 * Calcule le nombre de combinaisons possibles de comparaison pour une même personne
	 * @return
	 */
	public static int nbCombParPers () {
		int nbCombParPers = 0;
		for (int i=nbSignatures-1; i>0; i--) {
			nbCombParPers += i;
		}
		return nbCombParPers;
	}
	/**
	 * Interpolation cubique d'une liste de points
	 * @param pointsList, La liste des points d'acquisition
	 * @param n Le nombre de points à générer entre 2 points
	 * @return La liste des points après interpolation
	 */
	static List<Point> interpolate (List<Point> pointList, int n) {
		CubicInterpolator cubicInterpolator = new CubicInterpolator();
		List<Point> interpolatedPointList = new ArrayList<Point>();
		for(int i=0; i<pointList.size()-1; i++) {
			// Si le point courant n'est pas suivi d'un lever de stylo
			if (!penUp(pointList.get(i),pointList.get(i+1))) {
				double y0,x1,y1,x2,y2,y3;
				int p0,p1,p2,p3;
				float t0,t1,t2,t3;

				// Si on est en début de liste
				if (i-1 < 0) {
					y0 = 2*pointList.get(i).getPoint().y - pointList.get(i+1).getPoint().y;
					p0 = 2*pointList.get(i).getPressure() - pointList.get(i+1).getPressure();
					t0 = 2*pointList.get(i).getTime() - pointList.get(i+1).getTime();
					// ou si le point précédent est suivi d'un lever de stylo
				} else if (penUp(pointList.get(i-1),pointList.get(i))){
					y0 = 2*pointList.get(i).getPoint().y - pointList.get(i+1).getPoint().y;
					p0 = 2*pointList.get(i).getPressure() - pointList.get(i+1).getPressure();
					t0 = 2*pointList.get(i).getTime() - pointList.get(i+1).getTime();
					// Sinon
				} else {
					y0 = pointList.get(i-1).getPoint().y;
					p0 = pointList.get(i-1).getPressure();
					t0 = pointList.get(i-1).getTime();
				}
				x1 = pointList.get(i).getPoint().x;
				y1 = pointList.get(i).getPoint().y;
				p1 = pointList.get(i).getPressure();
				t1 = pointList.get(i).getTime();
				x2 = pointList.get(i+1).getPoint().x;
				y2 = pointList.get(i+1).getPoint().y;
				p2 = pointList.get(i+1).getPressure();
				t2 = pointList.get(i+1).getTime();
				// Si on est en fin de liste
				if (i+2 > pointList.size()-1) {
					y3 = 2*pointList.get(i+1).getPoint().y - pointList.get(i).getPoint().y;
					p3 = 2*pointList.get(i+1).getPressure() - pointList.get(i).getPressure();
					t3 = 2*pointList.get(i+1).getTime() - pointList.get(i).getTime();
					// ou si le point suivant est suivi d'un lever de stylo
				} else if (penUp(pointList.get(i+1),pointList.get(i+2))) {
					y3 = 2*pointList.get(i+1).getPoint().y - pointList.get(i).getPoint().y;
					p3 = 2*pointList.get(i+1).getPressure() - pointList.get(i).getPressure();
					t3 = 2*pointList.get(i+1).getTime() - pointList.get(i).getTime();
					// Sinon
				} else {
					y3 = pointList.get(i+2).getPoint().y;
					p3 = pointList.get(i+2).getPressure();
					t3 = pointList.get(i+2).getTime();
				}

				// Valeurs des 4 points
				double[] points = {y0,y1,y2,y3}, pressures = {p0,p1,p2,p3}, times = {t0,t1,t2,t3};

				// Entre deux points on génère n points
				for (int j = 1; j <= n; j++) {
					double newX = x1 + j*(x2-x1)/(n+1);
					double newY = cubicInterpolator.getValue(points, 1.*j/(n+1));
					int newPressure = (int) Math.round(cubicInterpolator.getValue(pressures, 1.*j/(n+1)));
					float newTime = (float) cubicInterpolator.getValue(times, 1.*j/(n+1));
					Point point = new Point(new Point2D.Double(newX, newY), newPressure, newTime);
					interpolatedPointList.add(point);
				}
			}
		}
		return interpolatedPointList;
	}

	/**
	 * Analyse en Composantes Principales
	 * @param pointList, La liste de points
	 * @param size, La taille de l'image
	 * @return double[] { xCenter, yCenter, vector1.x, vector1.y, vector2.x, vector2.y }
	 */
	static double[] applyACP (List<Point> pointList, int size) {

		// Compute sum of x,x2,y,y2,x*y of marked pixels
		double sum_x=0, sum_x2=0, sum_y=0, sum_y2=0, sum_xy=0, count=0;
		for(int i=0;i<pointList.size();i++) {
			Point2D.Double point = pointList.get(i).getPoint();
			sum_x += point.x; sum_x2 += point.x*point.x;
			sum_y += point.y; sum_y2 += point.y*point.y;
			sum_xy += point.x*point.y;
			count++;
		}

		// cov(X,Y) = E(X*Y) - E(X)*E(Y)
		double xCenter = sum_x/count, Ex2 =  sum_x2/count;
		double yCenter = sum_y/count, Ey2 =  sum_y2/count;
		double Exy = sum_xy/count;
		double COVxx = Ex2-xCenter*xCenter; 
		double COVyy = Ey2-yCenter*yCenter; 
		double COVxy = Exy-xCenter*yCenter; 

		// covariance matrix is 
		// | a b |   | COVxx COVxy |
		// | c d | = | COVxy COVyy |
		double a=COVxx, b=COVxy, c=COVxy, d=COVyy;

		// characteristic polynomial : P(L) = L^2 - Trace.L + Determinant
		double T = a+d;
		double Det = a*d-b*c;

		// eingenvalues L1,L1 = roots of characteristic polynomial
		double sqrt =  Math.sqrt(T*T - 4*Det);
		double L1 =  0.5 * ( T + sqrt ); 
		double L2 =  0.5 * ( T - sqrt );

		// eigenvectors: A.v = Li.v
		// | a.vx + b.vy = Li.vx
		// | c.vx + d.vy = Li.vy
		double V1x,V1y,V1n,V2x,V2y,V2n, EPSILON=1E-8;
		V1x=-(b+d-L1); V1y=(a+c-L1);
		V2x=-(b+d-L2); V2y=(a+c-L2);

		// normalize eigenvectors
		V1n=Math.sqrt(V1x*V1x+V1y*V1y);
		if (V1n>EPSILON) {V1x/=V1n;V1y/=V1n;}
		V2n=Math.sqrt(V2x*V2x+V2y*V2y);
		if (V2n>EPSILON) {V2x/=V2n;V2y/=V2n;}

		// Retourne données utiles
		return new double[] {xCenter, yCenter, V1x, V1y, V2x, V2y};
	}

	/**
	 * Centre la signature
	 * @param pointList, La liste de points
	 * @param xCenter, abscisse du centre de la signature
	 * @param yCenter, ordonnee du centre de la signature
	 * @param size, La taille de l'image
	 * @return la liste des points alignés sur le centre de la signature
	 */
	static List<Point> alignToCenter (List<Point> pointList, double xCenter, double yCenter, int size) {
		double dx = size/2 - xCenter;
		double dy = size/2 - yCenter;
		List<Point> alignedPointList = new ArrayList<Point>();
		for(int i=0; i<pointList.size(); i++) {
			Point point = pointList.get(i);
			// Si le nouveau point est toujours inclus dans l'écran
			if (point.getPoint().x+dx<size && point.getPoint().x+dx>0 && point.getPoint().y+dy<size && point.getPoint().y+dy>0) {
				alignedPointList.add(new Point(new Point2D.Double(point.getPoint().x+dx, point.getPoint().y+dy), point.getPressure(), point.getTime()));
			}		
		}
		return alignedPointList;
	}

	/**
	 * Effectue une rotation d'une liste de points
	 * @param pointList, La liste de points
	 * @param dx, vecteur x de l'axe principal de la signature
	 * @param dy, vecteur y de l'axe principal de la signature
	 * @param size, La taille de l'image
	 * @return la liste des points après rotation
	 */
	static List<Point> rotate (List<Point> pointList, double dx, double dy, int size) {
		int height = size;
		int width = height;
		// Calcul de l'angle de rotation à effectuer
		double angleRadian = Math.acos(Math.abs(dx)/Math.sqrt(dx*dx+dy*dy));
		if (dy<0) {
			angleRadian *= -1;
		}

		List<Point> rotatedPointList = new ArrayList<Point>();

		for(int i=0; i<pointList.size(); i++) {
			double x2 = Math.cos(angleRadian)*(pointList.get(i).getPoint().x - width/2) + Math.sin(angleRadian)*(pointList.get(i).getPoint().y - width/2) + width/2;
			double y2 = -Math.sin(angleRadian)*(pointList.get(i).getPoint().x - height/2) + Math.cos(angleRadian)*(pointList.get(i).getPoint().y - height/2) + height/2;
			// Si le nouveau point est toujours inclus dans l'écran
			if (x2<width && x2>0 && y2<height && y2>0) {
				Point point = new Point(new Point2D.Double(x2, y2), pointList.get(i).getPressure(), pointList.get(i).getTime());
				rotatedPointList.add(point);
			}
		}
		return rotatedPointList;
	} 

	/**
	 * Effectue une normalisation, ou zoom, sur une liste de points
	 * @param pointList, La liste de points
	 * @param size, La taille de l'image
	 * @return la liste des points après normalisation
	 */
	static List<Point> normalize (List<Point> pointList, int size) {
		double meanX = size/2;
		double meanY = size/2;
		double minX = size;
		double minY = size;
		double maxX = 0;
		double maxY = 0;
		for (int i=0; i<pointList.size(); i++) {
			minX = Math.min(minX,pointList.get(i).getPoint().x);
			minY = Math.min(minY,pointList.get(i).getPoint().y);
			maxX = Math.max(maxX,pointList.get(i).getPoint().x);
			maxY = Math.max(maxY,pointList.get(i).getPoint().y);
		}

		// Calcul le coefficient d'agrandissement
		double coef = Math.max(Math.max(1.*(meanX-minX)/meanX, 1.*(meanY-minY)/meanY), Math.max(1.*(maxX-meanX)/meanX, 1.*(maxY-meanY)/meanY));
		coef = 1/coef;

		List<Point> normalizedPointList = new ArrayList<Point>();

		for (int i=0; i<pointList.size(); i++) {
			double newX = meanX + (pointList.get(i).getPoint().x - meanX) * coef;
			double newY = meanY + (pointList.get(i).getPoint().y - meanY) * coef;
			if (newX < size && newY < size) {
				Point point = new Point(new Point2D.Double(newX, newY),pointList.get(i).getPressure(),pointList.get(i).getTime());
				normalizedPointList.add(point);
			}
		}
		return normalizedPointList;
	}

	/**
	 * Rechantillonage pour obtenir des courbes à nbPoints points
	 * @param pointList, la liste de points
	 * @param nbPoints, le nombre de points souhaités
	 * @return la liste contenant exactement nbPoints
	 */
	static List<Point> resample (List<Point> pointList,int nbPoints) {

		List<Point> resampledPointList = new ArrayList<Point>();
		// Différence de nombre de points
		double diff = pointList.size() - nbPoints;
		// Si la liste a trop de points
		if (diff > 0) {
			double j = 1;
			// On supprime des points
			for (int i=0; i<pointList.size(); i++) {
				if (i < j*pointList.size()/diff - 1) {
					resampledPointList.add(pointList.get(i));
				} else {
					j++;
				}
			}
			// Si la liste n'a pas assez de points
		} else if (diff < 0) {
			double j = 1;
			// On interpole des points
			for (int i=0; i<pointList.size(); i++) {
				if (i < j*pointList.size()/Math.abs(diff) - 1) {
					resampledPointList.add(pointList.get(i));
				} else {
					resampledPointList.add(pointList.get(i));
					Point2D.Double point2D = new Point2D.Double((pointList.get(i).getPoint().x+pointList.get(i-1).getPoint().x)/2., (pointList.get(i).getPoint().y+pointList.get(i-1).getPoint().y)/2.);
					int pressure = (int) ((pointList.get(i).getPressure()+pointList.get(i-1).getPressure())/2.);
					float time = (pointList.get(i).getTime()+pointList.get(i-1).getTime())/2;
					Point point = new Point(point2D, pressure, time);
					resampledPointList.add(point);
					j++;
				}
			}
			// Si la liste est déjà correctement échantillonée
		} else {
			return pointList;
		}
		return resampledPointList;
	}

	/**
	 * Réalise le pretraitement d'acquisitions en vue de construire un modele d'apprentissage
	 * et/ou le pretraitement des acquistions de la personne que l'on prétend être pour l'identification
	 * @param signature, la signature a traiter
	 * @param nbAcquisitions, nombre d'acquisitions à traiter
	 * @param genererPNG, Si l'on souhaite générer des PNG
	 */
	public static void pretraitementBD (ISignature signature, int nbAcquisitions, boolean genererPNG) {

		// Récupération d'un utilisateur
		System.out.println("Récupération de "+signature.getUser().getUserFirstName()
				+" "+signature.getUser().getUserLastName());
		// Récupération de la liste des ses acquisitions
		List<IAcquisition> acquisitionList = signature.getAcquisitions();

		// Taille de la liste avant pretraitement (Pour génération PNG)
		int start = originePointList.size();
		
		// Nb total d'acq
		int nbTotalAcquisitions = acquisitionList.size();

		// Pour chacune de ses acquisitions
		for (int i = 0; i < nbTotalAcquisitions; i++) {

			IAcquisition acqCourante = acquisitionList.get(i);
			if (!acqCourante.isLog()) {
				// Récupération de la liste des points
				List<IPoint> pointList = acqCourante.getAcquisitionPoints();

				// Prétraitement de l'acquisition
				List<Point> newPointList = IdeleteSameTime(pointList);
				originePointList.add(newPointList);

				// Interpolation
				newPointList = interpolate(newPointList, nbPoints
						/ newPointList.size() + 1);
				interpolatedPointList.add(newPointList);

				// ACP
				double[] dataOfACP = applyACP(newPointList, size);

				// Centrage
				newPointList = alignToCenter(newPointList, (int) Math
						.round(dataOfACP[0]), (int) Math.round(dataOfACP[1]),
						size);
				alignedPointList.add(newPointList);

				// Rotation
				newPointList = rotate(newPointList, dataOfACP[2], dataOfACP[3],
						size);
				rotatedPointList.add(newPointList);

				// Normalisation
				newPointList = normalize(newPointList, size);
				normalizedPointList.add(newPointList);

				// Ré-échantillonnage
				newPointList = resample(newPointList, nbPoints);
				resampledPointList.add(newPointList);

				System.out.println("Pretraitement effectue " + (i + 1) + "/"
						+ nbAcquisitions);
			}
		}

		// Génération des PNG (Etape la plus longue d'un pretraitement et pas utile)
		if (genererPNG) {
			for (int i=start; i<start+nbAcquisitions; i++) {
				System.out.println("Début de la génération des PNG "+(i+1)+"/"+originePointList.size());
				listToPNG("0_origine_"+signature.getUser().getUserFirstName()+"_"+i, originePointList.get(i), size); 
				listToPNG("1_interpolee_"+signature.getUser().getUserFirstName()+"_"+i, interpolatedPointList.get(i), size);
				listToPNG("2_centree_"+signature.getUser().getUserFirstName()+"_"+i, alignedPointList.get(i), size);
				listToPNG("3_droite_"+signature.getUser().getUserFirstName()+"_"+i, rotatedPointList.get(i), size);
				listToPNG("4_normalisee_"+signature.getUser().getUserFirstName()+"_"+i, normalizedPointList.get(i), size);
				listToPNG("5_reechantillonne_"+signature.getUser().getUserFirstName()+"_"+i, resampledPointList.get(i), size);
				System.out.println("Fin de la génération des PNG "+(i+1)+"/"+originePointList.size());
			}
		}
		System.out.println();

	}

	/**
	 * Réalise le pretraitement de l'acquisition à identifier
	 * @param pointList, la liste de points
	 * @param genererPNG, Si l'on souhaite générer des PNG
	 */
	public static void pretraitementNouvelleAcquisition (List<IPoint> pointList, boolean genererPNG) {

		// Prétraitement de l'acquisition
		originePointListIdentification = deleteSameTime(pointList);

		// Interpolation
		interpolatedPointListIdentification = interpolate(originePointListIdentification, nbPoints/originePointListIdentification.size() + 1);
		System.out.println(interpolatedPointListIdentification.size()+" >> "+originePointListIdentification.size()+" "+(nbPoints/originePointListIdentification.size() + 1));

		// ACP
		double[] dataOfACP = applyACP(interpolatedPointListIdentification, size);

		// Centrage
		alignedPointListIdentification = alignToCenter(interpolatedPointListIdentification, (int) Math.round(dataOfACP[0]), (int) Math.round(dataOfACP[1]), size);

		// Rotation
		rotatedPointListIdentification = rotate(alignedPointListIdentification,dataOfACP[2], dataOfACP[3], size);

		// Normalisation
		normalizedPointListIdentification = normalize(rotatedPointListIdentification, size);

		// Rééchantillonnage
		resampledPointListIdentification = resample(normalizedPointListIdentification, nbPoints);

		System.out.println("Pretraitement nouvelle acquisition effectue\n");

		// Génération des PNG (Etape la plus longue d'un pretraitement et pas utile)
		if (genererPNG) {
			listToPNG("0_origine_identification", originePointListIdentification, size); 
			listToPNG("1_interpolee_identification", interpolatedPointListIdentification, size);
			listToPNG("2_centree_identification", alignedPointListIdentification, size);
			listToPNG("3_droite_identification", rotatedPointListIdentification, size);
			listToPNG("4_normalisee_identification", normalizedPointListIdentification, size);
			listToPNG("5_reechantillonee_identification", resampledPointListIdentification, size);
			System.out.println("Fin de la génération des PNG");
		}

	}

	public static List<String> getAttributes (boolean[] attributes) {
		List<String> attributesList = new ArrayList<String>();
		// Si vitesse sélectionnée
		if (attributes[0]) {
			//attributesList.add("averagedSpeedX");
			//attributesList.add("averagedSpeedY");
			//attributesList.add("averagedSpeed");
			attributesList.add("totalTime");
		}
		// Nombre de lever de stylo toujours pris en compte
		attributesList.add("numberPenUp");
		// Si pression sélectionnée
		if (attributes[1]) {
			attributesList.add("averagedPressure");
			attributesList.add("CORTPressure");
			attributesList.add("DTWPressure");
		}
		// Si positionX sélectionnée
		if (attributes[2]) {
			attributesList.add("CORTX");
			attributesList.add("DTWX");
		}
		// Si positionY sélectionnée
		if (attributes[3]) {
			attributesList.add("CORTY");
			attributesList.add("DTWY");
		}
		// SI positionX et positionY sélectionnées
		if (attributes[2] && attributes[3]) {
			attributesList.add("pourcentageRadiance");
			attributesList.add("sumRadiance");
		}
		return attributesList;
	}

	/**
	 * Ecriture manuelle des fichiers ARFF pour l'apprentissage (construction du modèle)
	 * @param attributes, la liste des attributs de comparaison
	 * @param nbUser, le nombre d'utilisateurs a traiter
	 * @throws IOException
	 */
	public static void createApprentissageARFF (boolean[] attributes, int nbUser, String filename) throws IOException {

		// Ouverture du fichier ARFF d'apprentissage
		PrintWriter pw =  new PrintWriter(new BufferedWriter(new FileWriter(filename)));

		// Nom de la relation
		pw.println("@relation signature");
		// Attributs de comparaison
		List<String> attributesList = getAttributes(attributes);

		for (int i=0; i<attributesList.size(); i++) {
			pw.println("@attribute "+attributesList.get(i)+" numeric");
		}
		// Classe d'identification (accepté ou non)
		pw.println("@attribute accepte {no,yes}");

		// Ecriture des données obtenues après les différentes comparaisons
		pw.println("@data");
		// Comparaison intra-user (signatures équivalentes)
		for (int n=0; n<nbUser; n++) {
			for (int i=n*5; i<(n+1)*nbSignatures-1; i++) {
				for (int j=i+1; j<(n+1)*nbSignatures; j++) {
					double[] data = Comparaison.comparaison(resampledPointList.get(i), resampledPointList.get(j), attributesList);
					for (int k=0; k<data.length; k++) {
						pw.print(data[k]+",");
					}
					pw.println("yes");
				}
			}
		}
		// Comparaison inter-user de manière aléatoire (signatures différentes)
		boolean[][] combinaisonsEffectuees = new boolean[nbSignatures*nbUser][nbSignatures*nbUser];
		int nbCombinaisonsEffectuees = 0;		
		while (nbCombinaisonsEffectuees != 2*nbCombParPers()*nbUser) {
			int i = (int) (Math.random()*nbSignatures*nbUser);
			int decalage = i + (nbSignatures - (i % nbSignatures));
			int j = (int) ((Math.random()*nbSignatures) + decalage) % (nbSignatures*nbUser);
			if (combinaisonsEffectuees[i][j]!=true) {
				double[] data = Comparaison.comparaison(resampledPointList.get(i), resampledPointList.get(j), attributesList);
				for (int k=0; k<data.length; k++) {
					pw.print(data[k]+",");
				}
				pw.println("no");
				combinaisonsEffectuees[i][j]=true;
				nbCombinaisonsEffectuees++;
			}
		}
		// Fermeture du fichier
		pw.close();
	}

	/**
	 * Ecriture manuelle des fichiers ARFF pour l'identification (decision du modèle)
	 * @param attributes, la liste des attributs de comparaison
	 * @param nbUser, le nombre d'utilisateurs a traiter
	 * @throws IOException
	 */
	public static void createIdentificationARFF(boolean[] attributes)  throws IOException {

		// Ouverture du fichier ARFF d'identification
		PrintWriter pw =  new PrintWriter(new BufferedWriter(new FileWriter("identification.arff")));
		// Nom de la relation
		pw.println("@relation signature");
		// Attributs de comparaison
		List<String> attributesList = getAttributes(attributes);

		for (int i=0; i<attributesList.size(); i++) {
			pw.println("@attribute "+attributesList.get(i)+" numeric");
		}
		// Classe d'identification (accepté ou non)
		pw.println("@attribute accept {no,yes}");

		// Ecriture des données obtenues après les différentes comparaisons
		pw.println("@data");
		// Comparaison entre la nouvelle acquisition et les acquisitions de la personne qu'on prétend être
		for (int j=0; j<nbSignatures; j++) {
			double[] data = Comparaison.comparaison(resampledPointListIdentification, resampledPointList.get(j), attributesList);
			for (int k=0; k<data.length; k++) {
				pw.print(data[k]+",");
			}
			pw.println("?");
		}
		// Fermeture du fichier
		pw.close();
	}


	/**
	 * Construit le modèle
	 * @param args
	 * @throws IOException
	 */
	public static void main (String args[]) throws IOException {

		// Récupération de la liste des points pour chaque acquisition
		originePointList = new ArrayList<List<Point>>();
		interpolatedPointList = new ArrayList<List<Point>>();
		alignedPointList = new ArrayList<List<Point>>();
		rotatedPointList = new ArrayList<List<Point>>();
		normalizedPointList = new ArrayList<List<Point>>();
		resampledPointList = new ArrayList<List<Point>>();

		// On récupère la liste des utilisateurs de la base à utiliser pour construire le modèle
		List<IUser> userList = model.getUserList();

		init();
		
		// Pour chaque utilisateur
		for (int i=0; i<userList.size(); i++) {
			// Récupération de cinq signatures équivalentes (d'une même personne)
			pretraitementBD(model.getUserList().get(i).getUserSignature(), 5, false);
		}

		/*// Attributs tests pour la comparaisonCORT

		boolean[][] values = new boolean[16][4];
		for (int i=0; i<16; i++){
			values[i][0] = i<8;
			values[i][1] = i%8<4;
			values[i][2] = i%4<2;
			values[i][3] = i%2==0;
			createApprentissageARFF(values[i], userIDSelected.length, "apprentissage"+values[i][0]+values[i][1]+values[i][2]+values[i][3]+".arff");
			Classification.constructModel("apprentissage"+values[i][0]+values[i][1]+values[i][2]+values[i][3]+".arff");
		}*/
		boolean[] value = {true,true,true,true};
		createApprentissageARFF(value, userList.size(), "apprentissagebest.arff");
		Classification.constructModel("apprentissagebest.arff");
	}
}
