/* inicjalizuje wszystkie widgety na stronie */
function init_widgets() {
	var widgets = $$('div.widget')
	for (var i = 0; i < widgets.length; i++) {
		widgets[i] = Widget.getWidget(widgets[i])
		if (widgets[i] && widgets[i]) {
			widgets[i].init()
		}
	}
	WIDGETS = widgets
}

/**
 * Returns diff in years and months between two dates
 * date1 must be greater or equal date2
 */
function years_months_diff(date1, date2) {
	var y_diff = date1.getFullYear() - date2.getFullYear()
	var m_diff = y_diff * 12 + (date1.getMonth() - date2.getMonth())
	var d1_cp = new Date(date1), d2_cp = new Date(date2)
	d1_cp.setFullYear(2004)
	d2_cp.setFullYear(2004)
	if (d1_cp < d2_cp) {
		y_diff--
	}
	d1_cp.setMonth(0)
	d2_cp.setMonth(0)
	if (d1_cp < d2_cp) {
		m_diff--
	}
	return {'years': y_diff, 'months': m_diff}
}

/* 'klasa' reprezentujaca cyfry w cyfrowych wyswietlaczach widgetow-licznikow */
function Digit() {
	this.digit_elem = new Element('div', {'class': 'digit'})
	this.digit = null
	this.set_digit = function(digit) {
		if (this.digit !== digit) {
			this.digit = digit
			this.digit_elem.className = digit===null? 'digit' : 'digit'+digit
		}
	}
}

/** 
 * 'Klasa' reprezentujaca grupe cyfr licznika (lata, dni, godziny, minuty, sekundy) 
 * parametry:
 *     - digit_nums - liczba cyfr wchodzacych w sklad grupy
 *     - class_name - nazwa klasy CSS elementu-kontenera (diva) grupy
 * Obiekty CntPart posiadaja metode update, na wzror obiektow `Element` Prototype.
 * Dzieki temu mozliwe jest dzialanie jednej funkcji uaktualniajacej stan licznika bez
 * wzgledu czy jest to licznik tekstowy czy cyfrowy.
 */
function CntPart(digits_num, class_name) {
	//this.digits_num = digits_num
	this.digits = new Array(digits_num)
	this.container = new Element('div', class_name ? {'class': class_name} : {}) 
	this.number = null
	this.isNull = false
	this.prev = null
	for (var i = 0, digit; i < digits_num; i++) {
		digit = new Digit()
		this.container.appendChild(digit.digit_elem)
		this.digits[i] = digit
	}
	this.update = function(number) {
		if (this.number !== number) {
			this.number = number
			if ((this.prev === null || this.prev.isNull) && number == 0) {
				this.isNull = true
			}
			for (var i = 0; i < digits_num; i++) {
				if (this.isNull || ((this.prev === null || this.prev.isNull) 
					&& (number / Math.pow(10, digits_num-1-i)) < 1)) {
					this.digits[i].set_digit(null)
				} else {
					this.digits[i].set_digit(~~(number / Math.pow(10, digits_num-1-i) % 10))
				}
			}
		}
	}
}


// Widget 'abstact class'
function Widget(WidgetClass) {
	WidgetClass.prototype.close = function() {
		this.element.remove()
		// + ajax request to change widget visibility settings
		
	}
	// this method should be called after calling init
	WidgetClass.prototype._post_init = function() {
		var self = this
		var closeBtn = new Element('div')
		var widget = self.element.down('div.widget_id'), widget_id = null
		if (widget) {
			widget_id = widget.innerHTML
		}
		
		closeBtn.addClassName('close_button')
		closeBtn.hide()
		this.element.observe('mouseover', function() {
			closeBtn.show()
		})
		this.element.observe('mouseout', function() {
			closeBtn.hide()
		})
		closeBtn.observe('click', function() {
			if (widget_id) {
				new Ajax.Request('/widgets/hide/', {
					method: 'post',
					parameters: {'widget': widget_id},
					onFailure: function(resp){
						try {
							document.documentElement.innerHTML = resp.responseText;
						} catch(e) {
							document.body.innerHTML = resp.responseText;
						}
					}
				})
			}
			return self.close()
		})
		this.element.insert(closeBtn)
	}
	// metoda inicjalizujaca
	WidgetClass.prototype.init = function() {
		if (this.run) {
			this.run()
		}
		this._post_init()
	}
	
	return WidgetClass
}
// factory static method
Widget.getWidget = function(element) {
	this.element = element
	if (element.id == 'id_age_counter_widget') {
		return new AgeCounterWidget(element)
	} else if (/id_event_\d+_counter_widget/.test(element.id)) {
		return new EventCounterWidget(element)
	} else if (element.ud = 'id_user_bmi_widget') {
		return new BMI_Widget(element)
	}
}

/**
 * Age Counter Widget
 */
function AgeCounterWidget(element) {
	//var self = this
	this.element = element
	
	this.digital_cnt = element.down('div.digital_cnt')
	
	if (this.digital_cnt) {  // definicja poszczegolnych skladowych licznika cyfrowego
		this.years = new CntPart(3)
		this.digital_cnt.appendChild(this.years.container)
		this.digital_cnt.appendChild(new Element('div', {'class':'space_sep'}))
		
		this.days = new CntPart(3); this.days.prev = this.years
		this.digital_cnt.appendChild(this.days.container)
		this.digital_cnt.appendChild(new Element('div', {'class':'space_sep'}))
		
		this.hours = new CntPart(2); this.hours.prev = this.days
		this.digital_cnt.appendChild(this.hours.container)
		this.digital_cnt.appendChild(new Element('div', {'class':'dot_sep'}))
		
		this.minutes = new CntPart(2); this.minutes.prev = this.hours
		this.digital_cnt.appendChild(this.minutes.container)
		this.digital_cnt.appendChild(new Element('div', {'class':'dot_sep'}))

		this.seconds = new CntPart(2); this.seconds.prev = this.minutes
		this.digital_cnt.appendChild(this.seconds.container)
		
		this.seconds_total = element.down('div.cnt_seconds_total')
		
	} else { // definicja poszczegolnych skladowych licznika tekstowego
		this.years = element.down('td.cnt_years')
		this.days = element.down('td.cnt_days')
		this.hours = element.down('td.cnt_hours')
		this.minutes = element.down('td.cnt_minutes')
		this.seconds = element.down('td.cnt_seconds')
		
		this.months_total = element.down('td.cnt_months_total')
		this.weeks_total = element.down('td.cnt_weeks_total')
		this.days_total = element.down('td.cnt_days_total')
		this.hours_total = element.down('td.cnt_hours_total')
		this.minutes_total = element.down('td.cnt_minutes_total')
		this.seconds_total = element.down('td.cnt_seconds_total')
	}
	// data urodzin uzytkownika
	this.birth_date = new Date()
	this.birth_date.setSeconds(-this.seconds_total.firstChild.nodeValue)
	
	this.run = function() {
		var self = this
		new PeriodicalExecuter(function(pe) {
			var now = new Date()
			var timedelta = (now - self.birth_date) / 1000

			var diff = years_months_diff(now, self.birth_date)
			var last_birthday = new Date(self.birth_date)
			last_birthday.setFullYear(self.birth_date.getFullYear() + diff['years'])
			var last_birthday_delta = (now - last_birthday) / 1000
			
			self.years.update(diff['years'])
			self.days.update(parseInt(last_birthday_delta/(3600*24)))
			self.hours.update(parseInt((last_birthday_delta%(3600*24)/3600)))
			self.minutes.update(parseInt((last_birthday_delta%3600)/60))
			self.seconds.update(parseInt(last_birthday_delta%60))
			
			if (!self.digital_cnt) {
				self.months_total.update(diff['months'])
				self.weeks_total.update(Math.floor(timedelta/(3600*24*7)))
				self.days_total.update(Math.floor(timedelta/(3600*24)))
				self.hours_total.update(Math.floor(timedelta/3600))
				self.minutes_total.update(Math.floor(timedelta/60))
				self.seconds_total.update(Math.floor(timedelta))
			}

		}, 1)
	}
}
// 'dziedziczenie' po 'klasie' Widget
Widget(AgeCounterWidget)


/**
 * Event Counter Widget
 */
function EventCounterWidget(element) {
	//var self = this
	var nr = element.id.replace(/id_event_(\d+)_counter_widget/, '$1')
	this.element = $(element)
	
	var prefix = 'event_' + nr + '_'
	
	this.digital_cnt = element.down('div.digital_cnt')
	
	if (this.digital_cnt) { // definicja poszczegolnych skladowych licznika cyfrowego
		this.years = new CntPart(3)
		this.digital_cnt.appendChild(this.years.container)
		this.digital_cnt.appendChild(new Element('div', {'class':'space_sep'}))
		
		this.days = new CntPart(3); this.days.prev = this.years
		this.digital_cnt.appendChild(this.days.container)
		this.digital_cnt.appendChild(new Element('div', {'class':'space_sep'}))
		
		this.hours = new CntPart(2); this.hours.prev = this.days
		this.digital_cnt.appendChild(this.hours.container)
		this.digital_cnt.appendChild(new Element('div', {'class':'dot_sep'}))
		
		this.minutes = new CntPart(2); this.minutes.prev = this.hours
		this.digital_cnt.appendChild(this.minutes.container)
		this.digital_cnt.appendChild(new Element('div', {'class':'dot_sep'}))

		this.seconds = new CntPart(2); this.seconds.prev = this.minutes
		this.digital_cnt.appendChild(this.seconds.container)
		
	} else {  // definicja poszczegolnych skladowych licznika tekstowego
		this.years = element.down('td.cnt_years')
		this.days = element.down('td.cnt_days')
		this.hours = element.down('td.cnt_hours')
		this.minutes = element.down('td.cnt_minutes')
		this.seconds = element.down('td.cnt_seconds')
		
		this.months_total = element.down('td.cnt_months_total')
		this.weeks_total = element.down('td.cnt_weeks_total')
		this.days_total = element.down('td.cnt_days_total')
		this.hours_total = element.down('td.cnt_hours_total')
		this.minutes_total = element.down('td.cnt_minutes_total')
		this.seconds_total = element.down('td.cnt_seconds_total')
	}
	// funkcja parsujaca date rozpoczecia imprezy
	function parseDatetimeFormat(str) {
		/(\d+)[\D+](\d+)[\D+](\d+)\s+(\d+)[\D+](\d+)/.test(str)
		return new Date(RegExp.$1, RegExp.$2-1, RegExp.$3, RegExp.$4, RegExp.$5)
	}
	
	this.event_start_date = $('event_' + nr + '_start_date')
	this.start_date = parseDatetimeFormat(this.event_start_date.firstChild.nodeValue)
	
	this.run = function() {
		var self = this
		new PeriodicalExecuter(function(pe) {
			var now = new Date()
			var timedelta = new Date(self.start_date - now) / 1000
						
			var diff = years_months_diff(self.start_date, now)
			var help_date = new Date(now)
			help_date.setFullYear(help_date.getFullYear() + diff['years'])
			var help_delta = (self.start_date - help_date) / 1000
			
			
			self.years.update(diff['years'])
			self.days.update(parseInt(help_delta/(3600*24)))	
			self.hours.update(parseInt((help_delta%(3600*24)/3600)))
			self.minutes.update(parseInt((help_delta%3600)/60))
			self.seconds.update(parseInt(help_delta%60))

			if (!self.digital_cnt) {
				self.months_total.update(diff['months'])
				self.weeks_total.update(Math.floor(timedelta/(3600*24*7)))
				self.days_total.update(Math.floor(timedelta/(3600*24)))
				self.hours_total.update(Math.floor(timedelta/3600))
				self.minutes_total.update(Math.floor(timedelta/60))
				self.seconds_total.update(Math.floor(timedelta))
			}

			if (timedelta<0){				
				
				self.days.update(0);	
				self.hours.update(0);
				self.minutes.update(0);
				self.seconds.update(0);
	
				if (!self.digital_cnt) {
					self.months_total.update(0);
					self.weeks_total.update(0);
					self.days_total.update(0);
					self.hours_total.update(0);
					self.minutes_total.update(0);
					self.seconds_total.update(0);
				};
				
				
				
				pe.stop();
				
				// na koncu bo blad ?! - ale nie pokazuje bledu !? a nie ustawiaja sie zera!
				self.years.update(0);						
			};


		}, 1)
	}
}
// 'dziedziczenie' po 'klasie' Widget
Widget(EventCounterWidget)


/**
 * Body Mass Index Widget
 */
function BMI_Widget(element) {
	this.element = $(element)
	
	// funkcja ("metoda prywatna") uaktualniajaca pola tekstowe formularza widgetu indeksu masy
	function _update_values() {
		var weight = parseFloat($('id_weight').value.replace(',', '.'))
		var height = parseFloat($('id_height').value.replace(',', '.'))
		var age = parseInt($('id_age').innerHTML)
		var bmi = (weight / Math.pow(height/100, 2)).toFixed(2)
		var bmi_elem = $('id_bmi')
		bmi_elem.writeAttribute('value', bmi) // uaktualnienie wartosci parametru BMI
		if (age >= 18 && weight && height) {  // uaktualnienie "komentarza"
			if (bmi < 19) {
				bmi_elem.className = 'underweight'
			} else if ((age <= 34 && bmi < 25) || (age > 34 && bmi < 27)) {
				bmi_elem.className = 'normal'
			} else if (bmi < 30) {
				bmi_elem.className = 'overweight'
			} else {
				bmi_elem.className = 'adipositas'
			}
		}
		var chart = $('id_weight_chart')
		if (chart) {  // uaktualnienie wykresu
			var qs = get_query_string(chart.src)
			qs['date'] = Math.random()
			var baseurl = chart.src.replace(/\?.*/, '')
			chart.src = baseurl + '?' + Hash.toQueryString(qs)
		}
	}
	/* glowna metoda wywolywana przez metode inicjujaca widget
	 * obsluguje akcje wysylania formularza, wysyla zapytanie do serwera,
	 * uaktualnia pola formularza i wykres
	 */
	this.run = function() {
		frm = $('id_bmi_form')
		frm.observe('submit', function(event) {
			var self = this
			new Ajax.Request(self.readAttribute('action'), {
				'method': 'POST',
				'parameters': self.serialize(),
				'onSuccess': function(transport) {
					var resp = transport.responseJSON
					if (resp && resp.ERROR) {
						alert(resp.ERROR)
					}
					else if (resp && resp.OK) {
						_update_values()
					}
				},
				'onFailure': function(transport) {
					try {
						document.documentElement.innerHTML = resp.responseText;
					} catch(e) {
						document.body.innerHTML = resp.responseText;
					}
				}
			})
			Event.stop(event);
		})
	}

}
// 'dziedziczenie' po 'klasie' Widget
Widget(BMI_Widget)

// funkcja pomocnicza, pobiera zmienne (w postaci slownika) z QueryStringa
function get_query_string(url) {
	var qs = url.replace(/^[^?]*\?/, '').split(/&amp;|&/)
	var ret = {}, tmp
	for (var i = 0; i < qs.length; i++) {
		tmp = qs[i].split('=')
		if (tmp.length == 2) {
			ret[unescape(tmp[0])] = unescape(tmp[1])
		}
	}
	return ret
}

// Observes all widgets at main window
Event.observe(window, 'load', init_widgets)