package {
	// Classe para gerenciar as relações entre vários objetos
	public class Relacao {
		// Constantes de tipo
		static public const QUALQUER:String = "";
		static public const VIZINHO:String = "vizinho";
		static public const DONO:String = "dono";
		static public const CARTA:String = "carta";
		static public const CONTINENTE:String = "continente";
		static public const OBJETIVO:String = "objetivo";
		
		// Armazena as relações. Cada item é uma array com duas outras arrays
		static private var relacoes:Object = {};
		
		// Estabelece uma ligação entre dois objetos
		static public function relacionar(a:*, b:*, tipo:String=""):void {
			var i:int;
			if (!tem_relacao(a, b, tipo)) {
				// Os dois objetos ainda não tem relação
				if (!relacoes[tipo]) {
					// Cria as arrays para armazenar os dados
					relacoes[tipo] = [[], []];
				}
				
				// Busca uma posição vaga
				for(i=0; i<=relacoes[tipo][0].length; i++) {
					if (relacoes[tipo][0][i] === undefined && relacoes[tipo][1][i] === undefined) {
						relacoes[tipo][0][i] = a;
						relacoes[tipo][1][i] = b;
						break;
					}
				}
			}
		}
		
		// Desfaz uma relação criada anteriormente
		static public function desrelacionar(a:*, b:*, tipo:String=""):void {
			var i:String;
			if (tipo == QUALQUER) {
				// Exclui todas as relações dos objetos
				for (i in relacoes) {
					if (i != QUALQUER) {
						desrelacionar(a, b, i);
					}
				}
			}
			
			// Busca pelo objeto a em uma array e pelo b na outra
			if (relacoes[tipo]) {
				for (i in relacoes[tipo][0]) {
					if ((relacoes[tipo][0][i] === a && relacoes[tipo][1][i] === b) || (relacoes[tipo][0][i] === b && relacoes[tipo][1][i] === a)) {
						// Encontrado, basta eliminar
						delete relacoes[tipo][0][i];
						delete relacoes[tipo][1][i];
						break;
					}
				}
			}
		}
		
		// Verifica se dois objetos tem alguma relação
		static public function tem_relacao(a:*, b:*, tipo:String=""):Boolean {
			var i:String;
			if (tipo == QUALQUER) {
				// Verifica todos os tipos de relação
				for (i in relacoes) {
					if (i != QUALQUER) {
						if (tem_relacao(a, b, i)) {
							return true;
						}
					}
				}
			}
			
			// Busca pelo objeto a em uma array e pelo b na outra
			if (relacoes[tipo]) {
				for (i in relacoes[tipo][0]) {
					if ((relacoes[tipo][0][i] === a && relacoes[tipo][1][i] === b) || (relacoes[tipo][0][i] === b && relacoes[tipo][1][i] === a)) {
						// Encontrado, basta eliminar
						return true;
					}
				}
			}
			
			// Nada foi encontrado
			return false;
		}
		
		// Retorna todas as relações registradas para um dado objeto
		static public function get_relacoes(a:*, tipo:String=""):Object {
			var i:String;
			var obj:Object = {};
			var arr:Array = [];
			if (tipo == QUALQUER) {
				// Coleta todas as relações
				for (i in relacoes) {
					if (i != QUALQUER) {
						obj[i] = get_relacoes(a, i);
					}
				}
			}
			
			// Busca pelo objeto a em uma array e pelo b na outra
			if (relacoes[tipo]) {
				for (i in relacoes[tipo][0]) {
					if (relacoes[tipo][0][i] === a) {
						arr.push(relacoes[tipo][1][i]);
					} else if (relacoes[tipo][1][i] === a) {
						arr.push(relacoes[tipo][0][i]);
					}
				}
			}
			
			// Retorna
			if (tipo == QUALQUER) {
				obj[QUALQUER] = arr;
				return obj;
			} else {
				return arr;
			}
		}
		
		// Retorna a primeira relação encontrada para o objeto
		static public function get_relacao(a:*, tipo:String):* {
			var i:String;
			var obj:Object = {};
			
			// Busca pelo objeto a em uma array e pelo b na outra
			if (relacoes[tipo]) {
				for (i in relacoes[tipo][0]) {
					if (relacoes[tipo][0][i] === a) {
						return relacoes[tipo][1][i];
					} else if (relacoes[tipo][1][i] === a) {
						return relacoes[tipo][0][i];
					}
				}
			}
			
			// Erro
			return null;
		}
		
		// Elimina todas as relações do tipo desejado
		static public function eliminar_relacoes(tipo:String=""):void {
			if (tipo == QUALQUER) {
				relacoes = {};
			} else if (relacoes[tipo]) {
				delete relacoes[tipo];
			}
		}
		
		// Retorna os objetos que estão nos dois objetos
		static public function e(a:Object, b:Object):Array {
			var i:String, j:String, r:Array = [];
			for (i in a) {
				for (j in b) {
					if (a[i] === b[j]) {
						r.push(a[i]);
						break;
					}
				}
			}
			return r;
		}
		
		// Retorna os objetos que estão em a mais não em b
		static public function menos(a:Object, b:Object):Array {
			var i:String, j:String, r:Array = [], em_b:Boolean;
			for (i in a) {
				em_b = false;
				for (j in b) {
					if (a[i] === b[j]) {
						em_b = true;
						break;
					}
				}
				if (!em_b) {
					r.push(a[i]);
				}
			}
			return r;
		}
	}
}