(function($) {			// minifier em http://refresh-sf.com/yui/
	$.fn.startQuotes = function(o) {

		var config = {
			ingles: false,						// idioma
			colorirPercentual: true,
			effect: ['#7FFFAA', '#FF7F7F'],		// highlight
			speed: 3000,						// highlight
			secondsToReload: 60,				// dados ajax
			url: 'http://webservices.infoinvest.com.br/cotacoes/cotacoes_handler.asp', // dados ajax
			// tooltip config
			tooltip: true,
			setinha_up:	"<img src='setinha_up.gif' border='0'/>",
			setinha_down:	"<img src='setinha_down.gif' border='0'/>",
			setinha_zero:	"-",
			hoverConfig : {
				sensitivity: 3,
				interval: 150,
				timeout: 10,
				over: function() {
					var offset = $('.quotes_holder td:eq(1)').offset();
					offset.top = offset.top -1; // FIXME borda
					//$(this).parent.find(".quotes_tooltip").each(function() {		//show + posicao

					var stock_id = $(this).parent().attr("id").toUpperCase();
					$("#quotes_tt_" + stock_id.replace(".","")).each(function() {		//show + posicao
						$(this).css(offset);
					}).show();
					$(this).find('.ativo_cel').toggleClass("quotes_ativo");
					},
				out: function() {
					var stock_id = $(this).parent().attr("id").toUpperCase();
					$("#quotes_tt_" + stock_id.replace(".","")).each(function() {		//show + posicao longe == hide
						$(this).css( {left:-1000, top:-1000 });
					});
					$(this).find('.ativo_cel').toggleClass("quotes_ativo");
				}
			}
		};
		$.extend(config, o);

		// descobre a partir dos ID's quem tem no HTML quais sao as cotacoes a serem carregadas.
		var cotacoes = new Array;
		this.find('.quotes_row').each(function () {
			if (this.id) cotacoes.push(this.id);
		});
		config["quotes"] = cotacoes;

		// TODO monta HTML de cada cotacao

		// seta cores par/impar para cada linha
		// menos no header, que é a tr de indice zero. É estranho que deu certo dessa forma com o gt(0) no impar
		// nth-child deve ser baseado em 1, mas gt() é baseado em zero, fica bagunçado :D
		this.find("tr:nth-child(even)").addClass("quotes_even");
		this.find("tr:nth-child(odd):gt(0)").addClass("quotes_odd");

		// só roda uma vez
		var $table = $(this);
		if ($table.data('stockLoaded')) {
			return $table;
		}

		$table.data('stockLoaded', 'loaded');

		// Cria o tooltip
		if (config.tooltip) {
			this.find('.quotes_row').each(function() {
				var tooltip = $('<div class="quotes_tooltip"></div>');
				var tooltip_content = $("#quotes_tooltip_template").html(); // pega html estatico do tooltip
				tooltip_content = $(tooltip_content);						// constroi em objetos
				tooltip_content.appendTo(tooltip);
				var tooltip_id = "quotes_tt_" + this.id.toUpperCase();
				tooltip.attr("id",tooltip_id.replace(".",""));
				tooltip.appendTo($("body"));
			});

		} // end tooltip

		reloadQuotes(this, config);
		return this;
	};

	// Pega dados do servidor, em JSONP
	var reloadQuotes = function(pai, config) {
		$.ajax({
			url: config.url,
			type: 'GET',
			data: { quotes : config.quotes },
			dataType: 'jsonp',
			error: function(XMLHttpRequest, textStatus, errorThrown) {
				if (console && console.log) { console.log("Error accessing quotes server"); }
			},
			success: function(json) {

				// REDESENHA A TELA
				pai.find('.quotes_row').each(function () {
					var cotacao = json[this.id.toUpperCase()];
					if (!cotacao) return;
					redesenha_tudo		($(this), cotacao, config);   // atualiza esse bloco com os dados novos
				})

				if (config.secondsToReload) {
					setTimeout(function () {reloadQuotes(pai, config)}, config.secondsToReload * 1000);
				}
				config.ultimos_dados_recebidos = json;

				// habilita o hover dos tooltips após a primeira carga de dados
				if (config.tooltip && !(config.tooltip_loaded)) {

					// le tamanho da tabela antes de começar a desenhar
					/*
					var width =	  pai.width() - pai.find('td:eq(0)').width();
					var height =  pai.height();
					pai.find(".quotes_tooltip").each(function() {		// seta tamanho de todos os tooltips, e os mostra
						//$(this).width(width);
						//$(this).height(height);
					});
					*/
					$('.quotes_row > td:first-child').hoverIntent(config.hoverConfig);
					config.tooltip_loaded = true;
				}

			}
		});
	};

	// Atualiza as cotacoes
	var redesenha_tudo = function(tr, cotacao, config) {
		if (!tr || !tr.size()) {
			return;
		}

		// todos os valores aqui
		// Atencao que eles não vem formatados!

		var defaults = {
			precision : 2,
			decimal:       ',',
			thousands:     '.',
			date_format:	'dd/mm/yyyy HH:MM',
			allow_negative: false
		};
		var ingles = {
			decimal:       '.',
			thousands:     ',',
			date_format:	'mm/dd/yyyy hh:MMtt'
		};

		var atribs = [
			{	name: "valor",						type: "float" },
			{	name: "_now",						type: "date" },
			{	name: "display",					type: "text" },
			{	name: "tendencia",					type: "text" },
			{	name: "variacao_pontos",			type: "float",  allow_negative: true, add_plus_if_positive: true },
			{	name: "variacao_percentual",		type: "float",  allow_negative: true, add_plus_if_positive: true, eh_percentual: true },
			{	name: "abertura",					type: "float" },
			{	name: "fechamento",					type: "float" },
			{	name: "aftermarket",				type: "float" },
			{	name: "aftermarket_datetime",		type: "date" },
			{	name: "min",						type: "float" },
			{	name: "max",						type: "float" },
			{	name: "media",						type: "float" },
			{	name: "qtd_negocios",				type: "integer" },
			{	name: "volume_financeiro",			type: "float", shorten_number: true, source: "local" },
			{	name: "qtd_acoes_negociadas",		type: "integer", shorten_number: true  },
			{	name: "min_52",						type: "float" },
			{	name: "max_52",						type: "float" },
			{	name: "media_52",					type: "float" },
			{	name: "variacao_52",				type: "float", allow_negative: true, add_plus_if_positive: true},
			{	name: "data_do_min_52",				type: "float" },
			{	name: "data_do_max_52",				type: "float" },
			{	name: "min_ytd",					type: "float" },
			{	name: "max_ytd",					type: "float" },
			{	name: "media_ytd",					type: "float" },
			{	name: "base_ytd",					type: "float" },
			{	name: "variacao_pontos_ytd",		type: "float", allow_negative: true, add_plus_if_positive: true},
			{	name: "variacao_percentual_ytd",	type: "float", allow_negative: true, add_plus_if_positive: true, eh_percentual: true },
			{	name: "termometro_valor",
				type: "termometro",
				source: "local",
				min:	 {name: "min",		type: "float"},
				max:	 {name: "max",		type: "float"},
				current: {name: "valor",	type: "float"}
			},
			{	name: "termometro_52",
				type: "termometro",
				source: "local",
				min:	 {name: "min_52",	type: "float"},
				max:	 {name: "max_52",	type: "float"},
				current: {name: "valor",	type: "float"}
			},
			{	name: "termometro_ytd",
				source: "local",
				type: "termometro",
				min:	 {name: "min_ytd",	type: "float"},
				max:	 {name: "max_ytd",	type: "float"},
				current: {name: "valor",	type: "float"}
			},
			{	name: "data_atualizacao", type: "date", source: "local" }
		];

		for (var i in atribs) {
			var atributo = atribs[i];

			var up;

			if(atributo.name == "_now") {
				defaults.date_format = 'dd/mm HH:MM';
				ingles.date_format = 'mm/dd hh:MMtt';
			} else if (atributo.name == "aftermarket_datetime") {
				defaults.date_format = '(HH:MM)';
				ingles.date_format = '(hh:MMtt)';
			} else {
				defaults.date_format = 'dd/mm/yyyy HH:MM';
				ingles.date_format = 'mm/dd/yyyy hh:MMtt';
			}

			// liga o ingles
			if (config.ingles) defaults = $.extend(defaults, ingles);

			var my_defaults = $.extend({}, defaults);
			var format_options = $.extend(my_defaults, atributo);

			if(atributo.name == "_now") {
				$('.data_servidor').html(formata_dado(atributo, cotacao, format_options));
			}

			// pego todos os elementos a processar, incluindo alem do tr, os do tooltip associado a ele
			// hack bizonho, add() nao funciona
			var stock_id = cotacao["stock_id"].replace(".","").toUpperCase();

			var elements = new Array();
			elements[0] = $("#quotes_tt_" + stock_id).find('.quotes_' + atributo.name);
			elements[1] =						   tr.find('.quotes_' + atributo.name);

			for (var i = 0; i < 2; i++) {
				elements[i].each(function () {

							// coloco o elemento onde vou gravar isso nas opcoes, necessario para o termometro se desenhar
							format_options.container = $(this);
							format_options.setinha_up = config.setinha_up;
							format_options.setinha_down = config.setinha_down;
							format_options.setinha_zero = config.setinha_zero;
							format_options.dot_width = config.dot_width;
							format_options.mostrar_moeda = $(this).hasClass("mostrar_moeda");
							format_options.setinha = $(this).hasClass("setinha");
							format_options.colorir = $(this).hasClass("colorir");
							// Pega valor formatado
							valor = formata_dado(atributo, cotacao, format_options);
							if (atributo.name == 'variacao_percentual' && valor && config.colorirPercentual == true) {
								if (valor.substr(0,1) == "-")
									$(this).addClass('quotes_negative').removeClass('quotes_positive');
								else if (valor.substr(0,1) == "+")
									$(this).removeClass('quotes_negative').addClass('quotes_positive');
							}
							if (valor) {
								// minha responsabilidade de me escrever na tela
								$(this).html(valor);

								// seta highlight
								var tem_highlight = (config.ultimos_dados_recebidos && (atributo.type == "float" || atributo.type == "integer" || atributo.type == "percent"));
								if (tem_highlight) {
									var valor_atual    = parseFloat(cotacao[atributo.name]);
									var valor_anterior = config.ultimos_dados_recebidos[cotacao["stock_id"]][atributo.name];
									if (!isNaN(valor_anterior)) {
										valor_anterior = parseFloat(valor_anterior);

										var mudanca = (valor_atual - valor_anterior);
										if (mudanca)
											$(this).effect("highlight", { color: config.effect[(mudanca < 0) ? 1 : 0]}, config.speed);
									}
									else {
										// erro quando um dado especifico vem, numa atualizacao especifica, vazio.
									}

								}

							} else {
									// ou foi termometro ou não tinha dada, nao faço nada
									if (atributo.type != "termometro" && atributo.name != "aftermarket_datetime") {
										$(this).html("&nbsp;-&nbsp;");
									}

							};
				});
			} // end for
		}
	};



	var formata_dado = function (atributo, cotacao, format_options) {
		var valor, moeda;
		var moedas = {
			SP : "R$ ",
			BR : "",
			NY : "US$ ",
			LT : "&euro;", //&#8364;",
			US : "R$ ",
			TO : "CA$ "
		};

		if (!(atributo.source) || atributo.source != "local") {
			valor = cotacao[atributo.name]; // le da fonte de dados remota
			//if (!valor || valor === "") return null;
		}

// boa parte dos hacks decorre de nao confiarmos no banco de dados para fornecer
// em qual moeda é o dado que vem, se é dinheiro, indice ou taxa cambial
// além dos outros problems de dados que bem nos campos errados, etc. Vide abaixo.

		// ===== FILTRO 1 =====
		// em regra aqui vão todas as instruções que causam o código a retornar NULL sempre

				// cotacoes fora de SP. BR. ou US. não tem os 3 volumes, pra não dar problema!
				// Pega qual a bolsa em que essa ação é negociada!
				var str = cotacao["stock_id"].toString().split(".");
				var	bolsa = str[0].toUpperCase();

				// moeda vem da bolsa!
				moeda = moedas[bolsa];

				// faz o teste
				switch (bolsa) {
					case "BR":
						// o volume financeiro dos indices da bovespa vem no campo 'qtd acoes negociadas'
						if (cotacao["stock_id"] != "BR.EUR") {			// o euro em dólar é uma exceção bizarra.
							if (atributo.name == "volume_financeiro") {
								valor = cotacao["qtd_acoes_negociadas"];
								if (!valor) return;
							} else if (atributo.name == "qtd_acoes_negociadas") {
								return null;
							} else if (atributo.name.match(/percentual/)) {  // se for variacao percentual de algum tipo, nao faço nada.

							} else {  // para todo o resto:
								format_options.precision = 0;
								format_options.type = "integer";
							}
						}
						break;
					case "SP":
					case "US":
						break;
					default:
						if (atributo.name == "volume_financeiro" || atributo.name == "qtd_acoes_negociadas" || atributo.name == "qtd_negocios") {
							return null
						}
						break;
				}


		// ===== FILTRO 2 =====
		// AQUI VÃO IF's na FONTE DE DADOS
		// trocam / ajustam / acertam dados
		// esse bloco deixa a variavel 'valor' do jeito certo para o bloco abaixo formatar
			if (atributo.name == "volume_financeiro" && !(bolsa == "BR" && (cotacao["stock_id"] != "BR.EUR"))) {
				// 1 - vou sempre calcular o volume financeiro pois o do banco não é confiável, nunca
				// 2 - quando não há qtd de acoes negociadas e média, ficamos sem o dado
				var qtd_acoes_negociadas = cotacao["qtd_acoes_negociadas"];
				var media = cotacao["media"];
				if (qtd_acoes_negociadas && media) {
					valor = 0.0;
					valor = parseFloat(qtd_acoes_negociadas) * parseFloat(media);
				} else {
					return null;
				}
			}



		// ===== FILTRO 3 =====
		// IF's pra cada cotacao, algumas tem peculiaridades
				switch (cotacao["stock_id"]) {
					case "US.DOLARC":
					case "US.SP500":
					case "US.NASDAQ":
						format_options.precision = 3;
						break;
					case "BR.EUR":
						moeda = "US$ ";
						format_options.precision = 3;
						break;

				}
		// ===== FILTRO 4 =====
		// Precisão fixa para tudo que é percentual
		if(atributo.eh_percentual) {
			format_options.precision = 2;
		}

		// === CODIGO QUE FORMATA ===
		// formatação básica para cada tipo numérico
		// ATENÇÃO: SEM IF's AQUI, só formatação pura

		if (format_options.shorten_number) { // se o atributo mandou encurtar o numero, entao ele é float! corrigimos aqui o seu type.
			atributo.type = "float";
		}
		switch (atributo.type) {

			case "float":
				if (valor) {
					valor = parseFloat(valor);

					// Algoritmo pra encurtar o numero grande botando em escala de Milhoes, Bilhoes, etc
					var scale_char = "";
					if (format_options.shorten_number == true) {
						if (valor >= 1000000000)			{ // bilhoes ou maior (1.000.000.000 ou 10^9)
							scale_char = "B";
							valor = valor / 1000000000;
						} else if (valor >= 1000000)		{ // milhoes ou maior (1.000.000 ou 10^6)
							scale_char = "M";
							valor = valor / 1000000;
						} else if (valor >= 10000)	{ // 10 mil ou maior (10.000 ou 10^4)
							scale_char = "k";
							valor = valor / 1000;
						} else						{ // mevaloror que mil
							scale_char = ""
						}
					}

					valor = valor.toFixed(format_options.precision);
					valor = $.number_format(valor, format_options);

					if (scale_char) valor = valor + " " + scale_char;
			}
				break;

			case "integer":
				if (valor) {
					valor = parseInt(valor);
					valor = $.number_format(valor, $.extend(format_options, {precision: 0}))
			}
				break;

			case "termometro":
				if (cotacao[atributo.min.name] && cotacao[atributo.max.name] && cotacao[atributo.current.name]) {
					make_termometro(atributo, cotacao, format_options);
					return null;
				}
				break;

			case "text":
				break;
			case "date":
				if (cotacao[atributo.name])
					valor = dateFormat (new Date(cotacao[atributo.name]), format_options.date_format);
				else if (atributo.name != "aftermarket_datetime")
					//valor = null;
					valor = dateFormat (new Date(), format_options.date_format);
				break;

		}

		// formatador do numero em forma de setinha positiva/negativa
		if (format_options.setinha && (atributo.type == "float" || atributo.type == "integer")) {
			if (valor) {
				var n = parseFloat(cotacao[atributo.name]);
				if (n > 0) {
					valor = format_options.setinha_up;
				}
				else {
					valor = format_options.setinha_down;
				}
				return valor;
			} else { // valor é zero ou vazio
				valor = format_options.setinha_zero;
			}

		}

		// formatacao em forma de string
		if (atributo.add_plus_if_positive) if (parseFloat(cotacao[atributo.name]) > 0) valor = "+" + valor;
		if (atributo.eh_percentual && cotacao[atributo.name]) valor = valor + "%";
		if (format_options.mostrar_moeda && cotacao[atributo.name]) valor = moeda + valor;

		// colorir numero positivo/negativo, testa se é pra colorir e se é numero válido
		if (format_options.colorir && (atributo.type == "float" || atributo.type == "integer") && valor) {
			var n = parseFloat(cotacao[atributo.name]);
			if (n) valor = "<span class='"+(n>0?"quotes_colorir_positivo":"quotes_colorir_negativo")+"'>"+valor+"</span>";
		}
		return valor;
	};

	var format_big_currency = function (n) {
		// ATENCAO: funcao logaritmo nao serve pra pegar casas decimais porque a resposta
		// aproximada é inusável!
		if (!n) return null;

	}

	// Atualiza o termometro
	var make_termometro = function(atributo, cotacao, format_options) { // FIXME: tirar a dependencia externa de barsize e dot_width

		// configuracoes
		var defaults = {
			theme: 'quotes_thermometer_',
			inline: true,
			decimal: '.',
			showCurValue: false
		}
		var settings = $.extend({}, defaults, format_options);
		min			= parseFloat(cotacao[atributo.min.name]);
		max			= parseFloat(cotacao[atributo.max.name]);
		current		= parseFloat(cotacao[atributo.current.name]);

		// dados novos
		$(this).empty();
		var termometro = $('<div class="quotes_thermometer"></div>');

		var minLabel = $('<div></div>')
							.text(formata_dado (atributo.min, cotacao, format_options))
							.addClass(settings.theme + 'Label')
							.addClass(settings.theme + 'Label_Min')
							.appendTo(termometro);

		var maxLabel = $('<div></div>')
							.text(formata_dado (atributo.max, cotacao, format_options))
							.addClass(settings.theme + 'Label')
							.addClass(settings.theme + 'Label_Max')
							.appendTo(termometro);

		var bar = $('<div></div>')
					.addClass(settings.theme + 'Bar')
					.appendTo(termometro);

		var curDot = $('<div></div>')
						.addClass(settings.theme + 'Cur_Dot')
						.attr('title', formata_dado (atributo.current, cotacao, format_options))
						.appendTo(bar);

		var curDotPosition = {};

		if (current < min) {
			current = min;
		}

		if (current > max) {
			current = max;
		}

		if (settings.container) {
			// grava o termometro no objeto container
			settings.container.empty();
			termometro.appendTo(settings.container);

			// usando percentagem para posicionar
			var left_pos = ((current - min) / (max - min))*100;
			left_pos.toFixed(0); left_pos = left_pos.toString() + "%";
			curDot.css( {left : left_pos} );
		}

		return null;

	}; // end make_termometro


})(jQuery);

