// JavaScript Document


//objeto punto para dibujar
function punto(x,y){
	this.x=x;
	this.y=y;
	}
//objecto contenedor de vectores en la base
function dbvectors() {
	this.nombre= new Array();
	this.nv= new Array();
	this.m= new Array();
	this.sttype= new Array();
	this.v= new Array();
	this.n= new Array();
	
}
//methods for circlev
	//draw the circle in donde (es un div)
function drawcv(donde) {
	donde.setColor("#000000");
	donde.drawEllipse(this.topleft.x,this.topleft.y,2*this.radio,2*this.radio);
	donde.paint();
}

function getpointbynumber(n) {
	switch(n)
	{
	case 0:
	  return this.p0;
	  break;
	case 1:
	  return this.p1;
	  break;	
	case 2:
	  return this.p2;
	  break;
  	case 3:
	  return this.p3;
	  break;	
	case 4:
	  return this.p4;
	  break;	
	case 5:
	  return this.p5;
	  break;	
	 case 6:
	  return this.p6;
	  break;
	case 7:
	  return this.p7;
	  break;	
	case 8:
	  return this.p8;
	  break;	
	case 9:
	  return this.p9;
	  break;	
	case 0:
	  return this.p0;
	  break;	
	case 10:
	  return this.p10;
	  break;	
	case 11:
	  return this.p11;
	  break;		

	}
}

	//draw a line between node1 and node2 (nodes are nomber of nodes 0 to 11)
function drawcvline(node1,node2,donde) {
	donde.drawLine(this.p(node1).x,this.p(node1).y,this.p(node2).x,this.p(node2).y);
	donde.paint();
}





//circle method: draw the polygon of the nodes in the array
function drawNodePolygon(nodearray,canv,color,notfillit) {


	xpoints= new Array();
	ypoints= new Array();
	
	var a=0;


	for (a=0;a<nodearray.length;a++) 
	{
		xpoints[a]=this.p(nodearray[a]).x;
		ypoints[a]=this.p(nodearray[a]).y;
	
	}

	canv.setColor(color);

	if (notfillit==1) {
		canv.drawPolygon(xpoints, ypoints);
	} else {
		canv.fillPolygon(xpoints, ypoints);
		canv.setColor("#000000");
		canv.drawPolygon(xpoints, ypoints);
	}

	canv.paint();

	

}


function labelCircle(canv) {
	canv.setFont("arial",this.lsize+"px",Font.BOLD);
	canv.setColor("#000000");
	
	var n=0;
	for (n=0;n<12;n++) {
		canv.drawStringRect(this.fLabel[n],this.p(n).x+this.flabelXcorr[n],this.p(n).y+this.flabelYcorr[n],2*this.lsize,this.flabelalign[n]);
	}
	canv.paint();

}


// keylnum is 0=C...11=F, if sharp is 0 then is flats
function labelLetterCircle(canv) {
	var sharp=this.sharpflat[this.key];
	canv.setFont("arial",this.lsize+"px",Font.BOLD);
	canv.setColor("#000000");
	
	var n=0;
	var i=0;
	for (n=0;n<12;n++) {
		i=n+this.key;
		if (i>11) { i=i-12; }
		canv.drawStringRect(this.lLabel[i+12*sharp],this.p(n).x+this.flabelXcorr[n],this.p(n).y+this.flabelYcorr[n],2*this.lsize,this.flabelalign[n]);
	}
	canv.paint();

}



//declaring circle of Vths Object
function circlev(radio,cx,cy,lettersize) {

	this.radio=radio;
	this.c = new punto(cx,cy);
	this.topleft = new punto(cx-radio,cy-radio);
	this.coseno=0.8660254;  //sqrroot3/2 for point calculation
	
	this.p6= new punto(cx,cy+radio);
	this.p7= new punto(cx-radio/2,cy+radio*this.coseno);
	this.p8= new punto(cx-radio*this.coseno,cy+radio/2);
	this.p9= new punto(cx-radio,cy);
	this.p10= new punto(this.p8.x,2*cy-this.p8.y);
	this.p11= new punto(this.p7.x,2*cy-this.p7.y);
	this.p0= new punto(cx,cy-radio);
	this.p1= new punto(2*cx-this.p7.x,2*cy-this.p7.y);;
	this.p2= new punto(cx+radio*this.coseno,cy-radio/2);
	this.p3= new punto(cx+radio,cy);
	this.p4= new punto(2*cx-this.p8.x,this.p8.y);
	this.p5= new punto(cx+radio/2,cy+radio*this.coseno);
	
	this.lsize=lettersize;

	
	this.fLabel =      new Array("I","V","II","VI","III","VII","#IV","bII","bVI","bIII","bVII","IV");
	this.lLabel =      new Array("C","G","D","A","E","B","Gb","Db","Ab","Eb","Bb","F","C","G","D","A","E","B","F#","C#","G#","D#","A#","F");
	this.flabelalign = new Array("center","left","left","left","left","left","center","right","right","right","right","right");
	
	this.flabelXcorr = new Array(-lettersize, 0, 0 ,5, 0, 0, -lettersize, -2*lettersize,-2*lettersize,-5-2*lettersize,-2*lettersize,-2*lettersize);
	this.flabelYcorr = new Array(-lettersize-3,-lettersize-3,-lettersize-3,-lettersize/2,3,3,3,3,3,-lettersize/2,-3-lettersize,-3-lettersize);
	

	this.key=0;
	this.sharpflat = new Array(0,1,1,1,1,1,0,0,0,0,0,0);
	
	this.draw=drawcv;
	this.p=getpointbynumber;
	this.line=drawcvline;
	this.drawvpolygon=drawNodePolygon;
	this.labelFunction=labelCircle;
	this.labelLetter = labelLetterCircle;
	
	
}

function FindArrayMax(quien) {
	curmax=0;
	var j=0;
	for (j=0;j<quien.length;j++) {
		curmax=Math.max(quien[j],curmax);
	}
	
	return curmax;
	
}

function FindArrayMin(quien) {
	curmin=911111111111;
	wheremin=0;
	var j=0;
	for (j=0;j<quien.length;j++) {
		if (quien[j]<=curmin) {
			curmin=quien[j];
			wheremin=j;
		}
	}

	return wheremin;
	
}

//returns a new position for an element in an array of length l, with a new starting startnode
function RotateNodeNum(l,node,startnode) {
	
	return (node+l-startnode)%l;
	
}


function ArraytoStringinNode(arr,startnode) {
	var j=0;
	var cad="";
	var rotj=startnode-1;
	for (j=0;j<arr.length;j++) {
		rotj=rotj+1;
		if (rotj==arr.length) {rotj=0;}
		cad=cad+arr[rotj];

	}
	
	return parseInt(cad);
}

//******************

function NormalMode() {
	var l=this.largo+1;
	var q=this.mode-this.normalnode+1;
	if (q<=0) {q=q+l}
	return q;
}

//*************************************************************
//buscar si existe el normal vector en la base de datos si existe devuelve donde esta en al array de vectores de la base sino devuelve -1
function findVInArray(nvcad){

	var z=0;
	var encontre=-1;
	while (encontre==-1 && z<vectorsdb.nv.length) {

		if (nvcad===vectorsdb.nv[z]) {
			encontre=z;
		}
		z=z+1;
	}
	return encontre;
}
//******************
// El vector tiene que estar normalizado para que ande
function convertVToString() {
	cadresult="("
	for (q=0;q<=this.largo;q++) {
		if (q>0) {cadresult=cadresult+","}
		cadresult=cadresult+this.normalv[q];
	}
	
	this.normalvmstr=cadresult+")n"+this.normalMode(); //cargo la cadena en normalvmstr
	

}
//******************
function NormalizedVector(){



	var temparr= new Array();
	var j=0;
	for (j=0;j<this.largo;j++) {
		temparr[j]=this.velements[j];
		this.normalv[j]=0;  // initialazing
	}

	temparr[this.largo]=this.vlast;
	this.normalv[this.largo]=0;
	

	
	var maxinv=FindArrayMax(temparr);

	
	var normwhere = new Array(); // donde esta
	
	var e=0;
	var h=0;
	for (e=0;e<temparr.length;e++) {

		if (temparr[e]==maxinv) {
			normwhere[h]=e;
			h=h+1;
		}
	}
	


	var c=0;
	var whichnode=0;
	if (h==1) { 
		whichnode=normwhere[0]+1;
		if (whichnode>=temparr.length) {whichnode=0;}
	} else  {  // si hay dos o mas maximos hay que encontrar el minimo que le sigue
		var t=0;
		var minarr = new Array();

		for (t=0;t<normwhere.length;t++) {
			minarr[t]=ArraytoStringinNode(temparr,(normwhere[t]+1)%temparr.length); //lleno con vector para encontrar minimo despues
					
		}

		var minwhere= FindArrayMin(minarr);
		
		whichnode=normwhere[minwhere]+1;
		if (whichnode>=temparr.length) {whichnode=0;}		


	}
	
	for (p=0;p<temparr.length;p++) {   //if h=1 then theres only one maximum in the array

			c=RotateNodeNum(temparr.length,p,whichnode);

			this.normalv[c]=temparr[p];


	}
	
	this.normalnode=whichnode+1;//nodo del normal
	

	
	
	this.convertNVtoStr(); //cargo la cadena para base busqueda en this.normalvmstr
	

	this.vdbid=findVInArray(this.normalvmstr);
	
}

//declaring the vector object 
//velements = array con el vector
//relv = array with the relative position from velements[0]
function vector(cadena) {

	this.velements = new Array();
	this.relv = new Array();

	var temp = new Array();
	temp = cadena.split(",");
	
	
	this.relv[0]=0;
	var i = 0;
	for (i=0;i<temp.length;i++) {
		this.velements[i] = parseInt(temp[i]);
		this.relv[i+1]=this.relv[i]+this.velements[i];

	}

	this.vlast=12-this.relv[temp.length];

	
	//normal v is going to have the normalized vector
	//rules for normalization are: 1.Max(Ve) is the last one. 2.if not Min(Ve) is first. 3.if not Min(Ve) is second ....
	this.normalv = new Array();
	this.normalnode=-1; //not yet ready
	
	this.mode=-1;//not yet set

	this.normalMode=NormalMode;
	
	this.normalize = NormalizedVector;
	this.largo=this.velements.length;
	this.draw=drawvector;
	this.modVNode=modalVectorNode;

	this.normalvmstr=""; //por ahora esta vacio despues va a tener la cadena para busqueda (despues de converNVtoStr)
	this.convertNVtoStr=convertVToString; //convierto el vector normal a string (para buscarlo en la base)
	this.vdbid=-1;  //en que lugar del array de la base esta

	
}

//************************
function writeInMidPoint(canv,node1,node2,numero) {

		var m= new punto((node1.x+node2.x)/2,(node1.y+node2.y)/2);	
		var corrm = new punto(0,0);
		corm=vlabelcorr(node1,node2,m,10,10);
		canv.drawStringRect(numero,corm.x,corm.y,10,"center");
		canv.paint();
	
}

//corrccion del midpoint para labels en el vector
//linea node1 to node2 con midnode en el medio
// w=ancho del numero, h=alto del numero
function vlabelcorr(node1,node2,midnode,w,h) {
	var midcorr = new punto(0,0);
	if (parseInt(node1.y)<parseInt(node2.y)) { 
		midcorr.x=midnode.x-w;
	} else {
		midcorr.x=midnode.x;
	}
	

	if (parseInt(node1.x)>parseInt(node2.x)) { 
		midcorr.y=midnode.y-h;
	} else {
		midcorr.y=midnode.y;
	}
		
	return midcorr;
}

//modalize and transpose vector node keynode=0 the I is tonice keynode=1 then V is tonic. mode is n in the vector n=1 mode=1
function modalVectorNode(nodenumber,keynode,mode){
	var relmode = this.relv[(mode-1)%12];
	return (keynode+this.relv[nodenumber]-relmode+12)%12;
}	
// vector method: draw the polygon on the circle using the vector on the key of keynode and the mode 
// rel mode has de relv on the node of the mode
// if label=0 then do not label the lines
function drawvector(circle,keynode,mode,where,color,labvec) {

	where.setFont("arial","9px",Font.PLAIN);
	where.setColor("#105510");
	
	var nodes = new Array();

	var i=0;
	for (i=0;i<=this.largo;i++) {
		nodes[i]=this.modVNode(i,keynode,mode);
		if (labvec==1 && i>0) {
			writeInMidPoint(where,circle.p(nodes[i-1]),circle.p(nodes[i]),this.velements[i-1]);
		}
	}
	
	if (labvec==1) {writeInMidPoint(where,circle.p(nodes[this.largo]),circle.p(nodes[0]),12-this.relv[this.largo])}
	
	circle.drawvpolygon(nodes,where,color,labvec);

}

//*****************************
function changekey(circle,canv,key) {
	canv.clear();
	circle.draw(canv);
	circle.key=parseInt(key);
	if (key==12) {
		circle.labelFunction(canv);
	} else {
		circle.labelLetter(canv);
	}
}
	


//objeto contendor de estructutras
function structcontainer() {
	this.mainst  = new Array(); 
	this.subst = new Array();
	this.intersect = new Array();
	
	this.subcount = 0;
	this.fnames = new Array("I","V","II","VI","III","VII","#IV","bII","bVI","bIII","bVII","IV");

	this.fintersect = new Array();
	
	this.loadmain=loadmainstru;
	this.loadsub=loadsubstru;
	this.calcTotSub=totalSubNodes;
	this.iscontained=IsSubContained;
	this.loadintersect=Intersection;
	this.loadFIn = loadFIntersect;
	

	
}

function loadFIntersect() {

	for (q=0;q<=11;q++) {


		if (this.intersect[q]==1) {
				this.fintersect[q]=this.fnames[q];
		} else {
			this.fintersect[q]="";
		}

	}
}

//total de nodos en la sub
function totalSubNodes() {

		var t=0;
		
		for (i=0;i<=11;i++) {
			t=t+this.subst[i];
		}
		
		return t;
							
}

// returns true if sub is contained in main (must both be loaded and the modes and key set and subcount loaded too
function IsSubContained(){
		var cit = 0;

		for (j=0;j<=11;j++) {
			cit=cit+this.mainst[j]*this.subst[j];
		}

		if (cit==this.subcount) {
			return 1;
		} else {
			return 0;}
}
//load array of intersections for main structure for the main vector v,modemain and the subvector subv,modesub
function Intersection(v,modemain,subv,modesub) {

		for (k=0;k<=11;k++) {
			this.intersect[k]=0;
		}
		

		//cargo el vector main
		this.loadmain(v,modemain);

		// cargo todos los subvectores en las 12 keys y comparo
		for (e=0;e<=11;e++) {

			this.loadsub(subv,e,modesub);

			this.subcount=this.calcTotSub();


			if (this.iscontained()==1) { 
				this.intersect[e]=1; 
			}
		}
			
}
// metodo cargador de vector en la estructura main
function loadmainstru(vector,mode){

		for (i=0;i<=11;i++) {
			this.mainst[i]=0;
		}

		for (i=0;i<=vector.largo;i++) {
			this.mainst[vector.modVNode(i,0,mode)] = 1;
		}
}

// metodo cargador de vector en la estructura sub
function loadsubstru(vector,keynode,mode){
			for (i=0;i<=11;i++) {
			this.subst[i]=0;
		}

		for (i=0;i<=vector.largo;i++) {
			this.subst[vector.modVNode(i,keynode,mode)] = 1;
		}
}
