

//--------CLASSE COLLECTION ----------
// Cria uma coleção de dados
// Estrutura genérica para uso das pilhas, filas
// listas e listas circulares

sandecom.Collection = Class.create();
sandecom.Collection.prototype = {
	
	// CONSTRUTOR
	initialize: function(){
	
		this.idCont = 0;
		this.ini = null;
		this.end = null;
		this.len = 0;
		this.current = null;
		this.xmlDoc = null;
	
	},
	
	/*****************************************/
	/* ASSINATURAS                           */
	/* A classe que estende esta classe deve */
	/* implementar estes métodos             */
	/*****************************************/
	// ASSINATURA - adiciona um nó na coleção
	addNodo : function(node){
		
		// SOMENTE DE ASSINATURA
		// ESTE MÉTODO DEVE SER IMPLEMENTADO NA COLEÇÃO ESPECÍFICA
	},
	

	/****************************************/
	/* MÉDOTOS                              */
	/****************************************/

	// MÉTODO - cria um novo nó da classe node
	newNodo : function(obj){
		// cria novo nodo 
		var node = new sandecom.Collection.Nodo();
		node.register = obj;
		node.id = this.idCont;
		this.idCont ++;
		this.len++;
		return node;	
	},

	// MÉTODO - Define aonde o nó deve ser inserido
	add : function(obj){
		
		var node = this.newNodo(obj);
		
		if(this.ini==null){
			this.addStart(node);
		}else{
			this.addNodo(node);		
		}

	},
	
	// MÉTODO - é chamado por add, e adiciona de fato um nó
	// no inicio da lista
	addStart: function(node){
		
		this.ini = node; // O INICIO DA FILA RECEBE O NODO
		this.end = node; // 1° nodo O FIM DA FILA RECEBE NODO
	},
	
	
	/***********************************/
	/* MÉTODOS DE BUSCA NA COLEÇÃO     */
	/***********************************/

	// Retorna o primeiro OBJETO de acordo com o campo e chave do objeto
	seek : function(field,key){

		var aux = this.ini
		
		while(aux!= this.end ){
			if(aux.register[field]!=key){
				aux = aux.next;	
				this.current = aux;
			}else{
				break;
			}
			
		}
		
		if(aux==null){ 
			// NÃO ACHOU NADA
			return null;
		}else{		
	
			//achou alguma coisa verifica se a chave está certa
			if(aux.register[field]==key){ 
				this.current = aux;
				return aux.register;	
			}else{
				return null;
			}	
		}
	},
	
	// Retorns TODOS os OBJETOS  da lista 
	// de acordo com o campo de chave do objeto
	seekAll : function(field,key){
		
		// cria um vetor de resultado
		var result = new Array();
		
		// pega o primeiro nodo da coleção
		var aux = this.ini
		
		var i=0

		do{
			// se encontrou um objeto coloca no array;
			if(aux.register[field]==key){
				result[i] = aux.register			
			}

			// proximo nodo
			aux = aux.next;			
			i++;
			
		}while(aux!= null);
		
		
		
		return result;		
	},
	
	// MÉTODO each itera os itens da lista (do primeiro ao último)
	each: function(iterator) {
		
	    var aux = this.ini

		// faz iteração entre todos os nodos		
		
		if(aux!=null){
		
			do{
				iterator(aux.register);
				aux = aux.next;
			}while(aux!= this.end);
		
			// retornando último register;
			iterator(aux.register);
		}
		
	},
	

	/*********************************/
	/* SETS AND GETS                 */
	/*********************************/
	
	// MÉTODO - retorna o próprio objeto
	clone:function(obj){
		obj = this;
		return obj;		
	},
	
	// MÉTODO - altera o nó atual
	setCurrent:function(nodo){
		this.current = nodo;		
	},
	
	// MÉTODO - retorna o primeiro nó da lista
	first:function(){
		this.current = this.ini
		return this.ini.register;	
		
	},
	
	// MÉTODO - retorna o último nó da lista
	last:function(){
		this.current = this.end
		return this.end.register;		
	},
	
	// MÉTODO - retorna o tamanho da lista
	size:function(){	 
		return this.len;
	},
		
	
	/****************************************************/
	// REMOVE O PRIMEIRO DA COLEÇÃO (PADRÃO)
	// este metodo pode ser reimplementado conforme 
	// as regras da coleção especifica
	/******************************************************/	
	remove : function(){

		
		var aux = this.ini;

		if(aux.next!=null){
			this.ini = aux.next;
			aux.next.prev = null;
			if(this.current = aux){
				this.current = aux.next;
			}			
		}else{
			// unico nodo coleção vazia
			this.ini = null;
			this.end = null;
			this.current = null;
		}		
		
		this.len--;
		var obj = aux.register;
		
		aux = null
		return obj;
	},
	
	
	/************************************************/
	/* MÉTODOS DE SETAGEM POR XML                   */
	/************************************************/
	
	// MÉTODO - Carrega um xml na lista
	// pode ser passado a string de conexão ou 
	// o próprio objeto do xml
	loadXML: function(obj){
		
		var typeObj = typeof obj
		var classe = this;
		
		if( typeObj == "string"){
			
			// carrega XML XMLHTTPREQUEST
			new Ajax.Request(obj,{
					 method: 'get', 
					 onComplete: this.addXML.bind(this),
					 onFailure: function(){ alert('Não foi possível carregar o arquivo:' + ojb) }});		
			
		}else if(typeObj=="object"){
			// XML já está carregado
			// Criar lista com base no xml
			this.xmlDoc = obj;
			this.createListXML();
		
		}
		
	},
	
	// MÉDOTO -  vincula o xml ao objeto this
	addXML:function (RequestXml){
		// dados
		this.xmlDoc = RequestXml.responseXML;
		this.createListXML();

	},
	
	// MÉTODO - Adiciona um nó baseado no XML na lista
	createListXML : function(){
		
		//debugger;
		// Carregando nodos com base nos registros do xml
		var xml = this.xmlDoc;
		this.xmlDoc = null;
		
		var regs = xml.getElementsByTagName("record");
		for (var i=0; i< regs.length;i++){
			
			var node = regs[i].childNodes;
			var obj = new Object();
			var obj = this.createObject(node);
			this.add(obj);			
			
		}	
		
		
	},
	
	// MÉTODO - Cria um objeto baseado no XML 
	createObject : function(node){	
		
		//cria um novo objeto
		//debugger;
		var obj = new Object();
		
		for( var i= 0; i < node.length; i++){
			
			
			var itemAtual = node[i];
							
			if(window.ActiveXObject){
				if(itemAtual!=null){
					obj[itemAtual.nodeName] = itemAtual.text;
				}else{
					obj[itemAtual.nodeName] = "";	
				}
			}else{
				
				if(itemAtual.firstChild!=null){				
					obj[itemAtual.nodeName] = itemAtual.firstChild.data
				}else{
					obj[itemAtual.nodeName] = "";
				}
				
			}
						
		}//endfor
		
		return obj;
	}
						   
}

// END CLASS



//---------- CLASSE DE LISTA -------------
// Extende Collection
// Insere nó no atual(antes ou depois) ou no inicio da lista
// Remove no inicio da lista ou a partir de uma chave do objeto
//
// Modo de Instânciamento
// var lista = new sandecom.Collection.List();
// var lista = new sandecom.Collection.List(xml);

sandecom.Collection.List = Class.create();
sandecom.Collection.List.prototype =Object.extend(new sandecom.Collection(),{
	
	// CONSTRUTOR
	initialize: function(xml){
		if(xml!=null){
			this.loadXML(xml);
			this.xmlDoc = xml;
		}
	},
		
	//************************************************************************/
	// ESTE MÉTODO INCLUI O NODO BASEADO NAS REGRAS DE INSEÇÃO DESTA COLEÇÃO  /
	/*************************************************************************/
	addNodo : function(nodo){

		var aux = this.end;
		aux.next = nodo;		
		nodo.prev = aux;
		this.end = nodo;
		this.current = nodo;
	},
		
	// MÉTODO - adiciona um nó após o nó corrente
	addNext : function(field,key,obj){
		
		this.seek(field,key);
		var nodo = this.newNodo(obj);
		
		// evitando erro de inseção no fim da lista
		if(this.current.next!=null){
			this.current.next.prev = nodo;	
		}else{
			this.end = nodo;	
		}
		
		nodo.next = this.current.next;
		nodo.prev = this.current;
		
		this.current.next = nodo
	
		this.current = nodo;
		this.len++;
		
	},
	
	// MÉTODO - anda pra o proximo nó e retorna true ou false se deu certo
	next :function(){
		
		if(this.current.next!=null){
			this.current = this.current.next;	
			return true;
		}else{
			return false	
		}
		
	},
	
	// MÉTODO - anda pra o nó anterior e retorna true ou false se deu certo
	prev : function(){

		if(this.current.prev!=null){
			this.current = this.current.prev;	
			return true;
		}else{
			return false	
		}
		
	},
	
	/******************************************************/
	/* MÉTODO -  REMOVE UM OU VÁRIOS NODOS BASEADO 
	/* NO CRITÉRIO DE CAMPO E CHAVE PASSADOS.
	/* SE ACHADO UM NÓ RETORNA O OBJETO INTERNO DO NÓ
	/* SENÃO RETORNA UM ARRAY DE NÓS ENCONTRADOS 
	/*****************************************************/
	removeNodo : function(field,key){
			
		var nodos = new Array();
		nodos = this.seekAll(field,key);
		
		for(var i=0 ; i < nodos.length ; i++){
		
			var aux = nodos[i];
			if(aux.prev==null){  
				// remove no inicio
				this.remove(); 
			}else if(aux.next==null){
				// remove no fim
				this.end = aux.prev;
				this.current = aux.prev;
				this.current.next = null;
				this.len--;
			}else{
				// remove no meio
				aux.next.prev = aux.prev
				aux.prev.next = aux.next;
				this.current = aux.next;
				this.len--;
			}
			
		}
		
		// FORMA DE RETORNO DO METODO
		if(nodos.length!=0){
			if(nodos.length ==1){
				//retorna um objeto
				return nodos[0].register;
			}else{
				//retorna um array
				return nodos;
			}			
		}else{
			//retorna falso
			return false
		}
		
	},
	
	removeNodeObj:function(node){
		
		if(node==this.ini){
			this.remove();
		}else if(node==this.end){
			node.prev.next = null;
			this.end = node.prev;
		}else{
			node.prev.next = node.next;
			node.next.prev = node.prev;
		}
		
		node.next = null;
		node.prev = null;
		node.register = null;
		node=null;
	},
	
	
	// MÉTODO - Faz a fusão entre duas lista a atual e a passada
	fusionList : function(newList){
		
		//debugger;
		
		// fusão da lista 
		// nó final da lista atual
		// é ligado ao 
		// nó inicial da nova lista
		if(this.ini==null && this.end==null ){// LISTA ATUAL ESTÁ VAZIA
		
			this.ini = newList.ini;
			this.current = newList.ini;
			this.end = newList.end;
							
		}else{
			
			newList.ini.prev = this.end;		
			this.end.next = newList.ini;
			
			// posiciona o current da lista atual no inicio da nova lista
			this.current = newList.ini;
			
			// end da lista atual recebe end da nova lista
			this.end = newList.end;
		
		}
		
		this.idCont = newList.idCont;
		this.len = this.len + newList.len;
		

		// descarta a nova lista
		// todos os nós já pertencem a lista atual
		newList.ini = null;
		newList.end = null;
		newList.current = null;
		
		newList = null;
	
	}
});

// END CLASS





//-------- CLASSE DE PILHA ----------
// Extende a clase Collection
// insere e remove somente no Topo 
// da estrutura de dados

// Modo de instânciamento
// var pilha = new sandecom.Collection.Stack();

sandecom.Collection.Stack = Class.create();
sandecom.Collection.Stack.prototype =Object.extend(new sandecom.Collection(),{
	
	// CONSTRUTOR
	initialize: function(){
		
	},
	
	//************************************************************************/
	// ESTE MÉTODO INCLUI O NODO BASEADO NAS REGRAS DE INSEÇÃO DESTA COLEÇÃO  /
	/*************************************************************************/
	addNodo : function(nodo){

		var aux = this.ini;
		aux.prev = nodo;		
		nodo.next = aux;
		this.ini = nodo;

		this.Node++;
			
	}
});

// END CLASS



//------- CLASSE DE FILA ----------
// Extende Collection
// Insere no fim da fila 
// Remove no inicio da fila 

// Modo de instânciamento
// var fila = new sandecom.Collection.Queue()

sandecom.Collection.Queue = Class.create();
sandecom.Collection.Queue.prototype =Object.extend(new sandecom.Collection(),{
	
	// CONSTRUTOR
	initialize: function(){
		
	},
	
	
	//************************************************************************/
	// ESTE MÉTODO INCLUI O NODO BASEADO NAS REGRAS DE INSEÇÃO DESTA COLEÇÃO  /
	/*************************************************************************/
	addNodo : function(nodo){

		var aux = this.end;
		aux.next = nodo;		
		nodo.prev = aux;
		
		this.Node++;

		this.end = nodo;
	
	}	

});

// END CLASS



//---- CLASSE DE LISTA CIRCULAR -----
// Extende Lista
// No deslocamento a inicio e o fim da lista
// estão ligados

sandecom.Collection.CircleList = Class.create();
sandecom.Collection.CircleList.prototype = Object.extend(new sandecom.Collection.List(),{
	
	// CONSTRUTOR
	initialize: function(){
		 // extende List
	},

	// anda pra o proximo se for o fim vai para o inicio
	next :function(){
		
		if(this.current.next!=null){
			this.current = this.current.next;		
		}else{
			this.current = this.ini;
		}
		
	},
	
	// anda pra o nodo anterior se for o inicio vai para o fim
	prev : function(){
		
		if(this.current.prev!=null){
			this.current = this.current.prev;	
		}else{
			this.current = this.end			
		}
	}
	
});
	
// END CLASS



///////////////  ESTRUTURA DOS NODES ////////////////////

// NÒS DE PILHAS,FILAS E LISTAS E CIRCULAR
sandecom.Collection.Nodo = Class.create()
sandecom.Collection.Nodo.prototype = {
	
	// CONSTRUTOR
	initialize: function(){
		this.id = 0;
		this.prev = null;
		this.next = null;
		this.register = null;

	}

}

