/**Women calendar
 * @author danyastuff
 * @version 1.0
 */

var expireDate = new Date();
	expireDate.setFullYear(expireDate.getFullYear() + 2);
	
var COOKIES = {
	forecast :
	{
		cycle 	: 'women-cycle', 
		duration: 'women-duration', 
		date	: 'women-last-day', 
		month	: 'women-last-month', 
		year	: 'women-last-year'
	},
	markedDates: 'women-marked-dates', 
	options : {
		path : '/', 
		expires: expireDate
	}
}
   
var MS_IN_DAY = 1000 * 60 * 60 * 24,
	DURATION_LIMIT = 10,
	CYCLE_LIMIT    = 40,
	MONTHS_AGO	   = 5,
	MARK_MINST	   = 'minstruation',
	MARK_OVUL	   = 'ovulation',
	MARK_PMS	   = 'pms',
	STATE_FORECAST = 'forecast',
	STATE_MARK     = 'mark';

var WomenCalendar = function () {
	var self = this;
		
	this.state = STATE_FORECAST; // default state
	
	this.started = {
		name   : 'started',
		$day   : $('#last-day'),
		$month : $('#last-month'),
		$pick  : $('#start-date-pick'),
		date   : null
	}
	this.ended = {
		name   : 'ended',
		$day   : $('#last-day-end'),
		$month : $('#last-month-end'),
		$pick  : $('#end-date-pick'),
		date   : null
	}
	this.cycle = {
		$inp  : $('#day-cycle'),
		$sign : $('#cycle-sign'),
		$pick : $('#cycle-pick'),
		value : 0
	}
	this.duration = {
		$inp  : $('#day-num'),
		$sign : $('#day-sign'),
		$pick : $('#duration-pick'),
		value : 0
	}	
	
	this.calendar = {
		$forecast : $('#forecast-months'),
		$markable : $('#markable-months')
	}
	
	this.$send2friend = $('#result-links input');
	
	this.slideDate = new Date();
	this.date 	   = new Date();
	
	this.markedDates = [];
	
	/* 
	 * Workers 
	 */
	this.started.$month.parent = this.started;
	this.ended.$month.parent = this.ended;
	
	this.started.$month.init = initMonthSelect;
	this.ended.$month.init   = initMonthSelect;
	
	this.started.$month.setSelected = setSelectedMonth;
	this.ended.$month.setSelected   = setSelectedMonth;
	
	this.started.setPick = setPick;
	this.ended.setPick   = setPick;
	
	this.started.refresh = function () 
	{
		self.ended.$day.removeClass('error');
		self.ended.$month.removeClass('error');
		
		var ed = self.ended.date,
			sd = self.started.date;
			
		if (ed) {
			if (ed.getTime() > sd.getTime())
			{							
				if (self.setDuration( Math.ceil( Math.abs(ed.getTime() - sd.getTime()) / MS_IN_DAY)  + 1) ) 
				{					
					self.markMonths();
					return
				} 
			} 
			self.ended.$day.addClass('error');
			self.ended.$month.addClass('error');
		}
	}
	this.ended.refresh = function () {		
		if (self.duration.value)
		{
			var sd = self.started.date;
			this.date = new Date (
				sd.getFullYear(), 
				sd.getMonth(), 
				parseInt(sd.getDate()) + parseInt(self.duration.value) - 1
			);
			
			this.$day.val(this.date.getDate());
			this.$month.setSelected(this.date.getMonth());
			this.$day.removeClass('error');
			this.$month.removeClass('error');
		} 
	}
	
	function setPick () {
		this.$pick.text(
			this.$day.attr('value') + ' ' + this.$month.find('option:selected').text()
		)
	}
	
	function initMonthSelect () 
	{
		var now = new Date(),
			ny = now.getFullYear();
			
		now.setMonth(now.getMonth() - 3);
		this.html('');
		
		var from = 0, to = 4;
		
		if (this.parent.name == 'ended')
			to = 5;
					
		for (var i = from; i < to; i++) 
		{
			var opt = $('<option></option>'),
				m = now.getMonth(),
				y = m == 0 ? ' ' + now.getFullYear() : '';
				
			opt.attr('value', m);
			opt.text(monthName(m,1) + y);
			this.append(opt);
			
			now.nextMonth()
		}
	}
	
	function setSelectedMonth (m) 
	{
		var thisDOM = this.get(0);
		for (var i=0, l = thisDOM.options.length; i < l; i++)
			if (thisDOM.options[i].value == m) 
				thisDOM.selectedIndex = i;
	}
	
	
	this.cycle.setPick   = setPickCD;
	this.duration.setPick = setPickCD;
	
	function setPickCD () {
		this.$pick.text(
			this.$inp.attr('value') + ' ' + this.$sign.text() 
		)
	}	 
		
	/* end: Workers */
	
	/* 
	 * Handlers 
	 */
	this.cycle.$inp.keyup(function () 
	{
		var days = this.value;
		if (notPosInt(days) || days > CYCLE_LIMIT || days < 10) {
			$(this).addClass('error');
			self.cycle.$sign.addClass('ghost');
		} else {
			$(this).removeClass('error');
			self.cycle.$sign.removeClass('ghost');
			self.setCycle(parseInt(days));
			self.markMonths();
		}
	})
	
	this.duration.$inp.keyup(function() 
	{
		var days = this.value; 
		if (notPosInt(days) || days > 15) { 
			$(this).addClass('error');
			self.duration.$sign.addClass('ghost');
		} else {
			$(this).removeClass('error');
			self.duration.$sign.removeClass('ghost');			
			self.setDuration(parseInt(days));
			self.markMonths();
		} 
	})
	
	this.started.$day.keyup(function() 
	{	
		self.checkDate(self.started, true);
		self.send2cookies();
	})
	
	this.started.$month.change(function() 
	{
		self.started.date.setMonth(
			this.options[this.selectedIndex].value
		);
		self.checkDate(self.started);
		self.send2cookies();
	})
	
	this.ended.$day.keyup(function() 
	{		
		self.checkDate(self.ended, true);
		self.send2cookies();
	})
	this.ended.$month.change(function() 
	{
		self.ended.date.setMonth(
			this.options[this.selectedIndex].value
		);		
		self.checkDate(self.ended);		
		self.send2cookies();
	})
	/* end: Handlers */

	this.started.$month.init();
	this.ended.$month.init();
	
	this.calendar.$forecast.calendar(
		new Date(this.slideDate.getFullYear(), this.slideDate.getMonth(), 1),
		12,
		false
	);
	this.calendar.$forecast.legend = $('#legend-forecast');
	
	var someMonthsAgo = new Date();
		someMonthsAgo.setMonth(someMonthsAgo.getMonth() - MONTHS_AGO);
		someMonthsAgo.setDate(1);
		someMonthsAgo.setHours(0);
		
	this.calendar.$markable.calendar(someMonthsAgo, 12, onDateMark);
	this.calendar.$markable.startDate = someMonthsAgo;
	this.calendar.$markable.legend = $('#legend-markable');
	this.calendar.$markable.addClass('markable');
	
	function onDateMark ($cell, on)
	{
		var date = $($cell.get(0).parentNode.parentNode.parentNode).attr('date') + '-' + $cell.text();
		
		self.markedDates[date] = on;
		self.send2cookies(self.markedDates);
	}
	
	this.setState(this.state);
	
	this.checkCookies();
}

WomenCalendar.prototype = {
    send2friend : function ()
    {
        this.$send2friend.val(
            'http://' + BaseServer + '/services/women-calendar/' +
            this.cycle.value + '/' +
            this.started.date.getFullYear() + '.' + 
            (this.started.date.getMonth()+1) + '.' + 
            this.started.date.getDate() + '/' +
            this.duration.value
        )
        
    },
	checkCookies : function () 
	{
		var cookieOk = true,
		    urlData = window.location.pathname.replace('/services/women-calendar/', '').split('/');
		
		for (i in COOKIES.forecast) 
			if (!$.cookie(COOKIES.forecast[i]))
			{
				cookieOk = false;
				break;
			}
			
		if (urlData.length == 3)
		{
		    
		    var date = urlData[1].split('.');
		    
		    var y = date[0],
				m = date[1]-1,
				d = date[2],
				c = urlData[0],
				p = urlData[2];
			
			this.started.date = new Date(y, m, d);
			
			this.setDate(true);
			this.setCycle(c, true);
			this.setDuration(p, true);
			
			this.DATE_FROM_URL = true;
			this.checkDate(this.started);
		}
		else if (cookieOk) 
		{
			var y = $.cookie(COOKIES.forecast.year),
				m = $.cookie(COOKIES.forecast.month),
				d = $.cookie(COOKIES.forecast.date),
				c = $.cookie(COOKIES.forecast.cycle),
				p = $.cookie(COOKIES.forecast.duration);
			
			this.started.date = new Date(y, m, d);
			
			this.setDate(true);
			this.setCycle(c, true);
			this.setDuration(p, true);
			
			this.checkDate(this.started);
		} else {
		    this.started.date = new Date();
			
			this.setDate(true);
			this.setCycle(28, true);
			this.setDuration(4, true);
			this.checkDate(this.started);
		}
		
		var markedDates = $.cookie(COOKIES.markedDates);
		
		if (markedDates)
		{
			markedDates = markedDates.split(',');
			
			for (i = 0, l = markedDates.length; i < l; i++)
			{
				var date = markedDates[i].split('-');
				if (date.length == 3) 
				{
					this.calendar.$markable.mark(date[0], date[1], date[2]);
					this.markedDates[date[0] + '-' + date[1] + '-' + date[2]] = true;
				}
			}
		}
		
	},
	
	send2cookies : function (markedDates) 
	{
		if (!markedDates)
		{
			$.cookie( 
				COOKIES.forecast.year,
				this.started.date.getFullYear(),
				COOKIES.options 
			)
			
			$.cookie(
				COOKIES.forecast.month,	
				this.started.date.getMonth(), 
				COOKIES.options 
			)
			$.cookie(
				COOKIES.forecast.date,
				this.started.date.getDate(),
				COOKIES.options 
			)
		
			$.cookie(
				COOKIES.forecast.duration,
				this.duration.value,
				COOKIES.options 
			)
			$.cookie(
				COOKIES.forecast.cycle,
				this.cycle.value,
				COOKIES.options 
			)
		} 
		else 
		{
			var dateStr = '';
			
			for (date in markedDates)
				if (markedDates[date])
					dateStr += date + ',';
					
			if (dateStr)
				$.cookie(COOKIES.markedDates, dateStr, COOKIES.options);
		}
		
	},
	
	setDate : function (first) 
	{
		this.started.$day.attr(
			'value', this.started.date.getDate() 
		);
		this.started.$month.setSelected( 
			this.started.date.getMonth() 
		);
					
		if (!first) this.send2cookies();
	},
	
	setCycle : function (days, first) 
	{
		this.cycle.value = days;
		this.cycle.$inp.attr('value', days);		
		this.cycle.$sign.text( dayCase(days) );
		this.cycle.setPick();
		
		if (!first) this.send2cookies();
	},
	
	setDuration : function (days, first) 
	{	
		if (days <= DURATION_LIMIT)
		{
			this.duration.value = days;
			this.duration.$inp.attr('value', days);
			this.duration.$sign.text( dayCase(days) );
			this.duration.setPick();
			
			this.ended.refresh();		
			
			if (!first) this.send2cookies();
		} else {
			return false;
		}
		return true;
	},
	
	checkDate : function (dateType, fromDay) 
	{
		var day = dateType.$day.attr('value');
		
		if (notPosInt(day)) 
		{
			dateType.$day.addClass('error')
		} else {
			dateType.$day.removeClass('error');
			
			if (fromDay) dateType.date.setDate(day);
			
			var now =  new Date(),
				nd = now.getDate(), 
				nm = now.getMonth();
			
			if (!this.DATE_FROM_URL)
			{
			    if (dateType.date.getMonth() > nm && nm < 4)
			        dateType.date.setFullYear(now.getFullYear()-1);
			    else if (dateType.date.getMonth() > nm && nm > 3)
    				dateType.date.nextYear();
    			else
    				dateType.date.setFullYear(now.getFullYear());
			}
			this.DATE_FROM_URL = false;
			
			dateType.$month.setSelected(
				dateType.date.getMonth() 
			);
			dateType.$day.attr(
				'value', dateType.date.getDate()
			);
			
			this.started.setPick();
			this.ended.setPick();
			
			if (dateType.name == 'started') {
				this.ended.refresh();
				this.markMonths();
			} else {
				this.started.refresh();
			}
		}
	},
	
	markMonths : function () 
	{
		if (this.duration.value && this.cycle.value && this.started.date) 
		{ 
			var marks = [], 
				p = 0,
				d = new Date( this.started.date.getTime() ),
				nextYear = new Date().getFullYear() + 1,
				ovul = Math.round(this.cycle.value / 2) - 2,
				pms  = 2;
			
			this.send2friend();
			
			this.calendar.$forecast.find('TD.ovulation').removeClass('ovulation');
			this.calendar.$forecast.find('TD.pms').removeClass('pms');
			
			while (d.getFullYear() <= nextYear) // one cycle 
			{ 
				p = -1;
				while (++p < this.duration.value) // menstruation 
				{  
					marks.push({
						y : d.getFullYear(),
						m : d.getMonth(),
						d : d.getDate(),
						type : MARK_MINST
					})
					d.nextDate();
				}
				
				d.dateJump(- this.duration.value + 1);				
				d.dateJump(ovul - 1);
				
				p = -1;				
				while (++p < 4) // ovulation
				{
					marks.push({
						y : d.getFullYear(),
						m : d.getMonth(),
						d : d.getDate(),
						type : MARK_OVUL
					})
					d.nextDate();
				}
				
				d.dateJump( (this.cycle.value - ovul) - 1);
				d.dateJump(-pms -3);
				
				p = -1;				
				while (++p < pms) // PMS
				{
					marks.push({
						y : d.getFullYear(),
						m : d.getMonth(),
						d : d.getDate(),
						type : MARK_PMS,
						past : false
					})
					d.nextDate();
				}			
			}
			
			
			// Forcast in past time for markable calendar
			
			d = new Date( this.started.date.getTime() );
			d.dateJump(-this.cycle.value);
			
			var startTime = this.calendar.$markable.startDate.getTime();
			
			while (d.getTime() >= startTime)
			{				
				p = -1;
				while (++p < this.duration.value) // menstruation 
				{  
					marks.push({
						y : d.getFullYear(),
						m : d.getMonth(),
						d : d.getDate(),
						type : MARK_MINST,
						past : true
					})
					d.nextDate();
				}
				
				d.dateJump(-this.duration.value);
				d.dateJump(-this.cycle.value);				
			}
			
			this.calendar.$forecast.find('TD.day').removeClass('critical').attr('title', '');
			this.calendar.$markable.find('TD.day').removeClass('critical').attr('title', '');
			
			var mi, date, forecastDays, markableDays;
			
			for (var i = 0; i < marks.length; i++) 
			{
				mi = marks[i];
				if (i == 0 || mi.m !== marks[i-1].m) 
				{
					date = mi.y + '-' + mi.m ;
					forecastDays = this.calendar.$forecast.find('table[date="'+date+'"]').find('td.day');
					markableDays = this.calendar.$markable.find('table[date="'+date+'"]').find('td.day');
				}
				
				var forecastCell = forecastDays.filter(':eq('+ (mi.d - 1) +')'),
					markableCell = markableDays.filter(':eq('+ (mi.d - 1) +')');
				
				if (mi.type == MARK_MINST) {
					if (!mi.past)
						forecastCell.addClass('critical').attr('title', s('menstruation'));
					markableCell.addClass('critical').attr('title', s('projected') + s('menstruation'));
				} else if (mi.type == MARK_OVUL) {
					forecastCell.addClass('ovulation').attr('title', s('ovulation period'));
				} else if (mi.type == MARK_PMS) {
					forecastCell.addClass('pms').attr('title', s('probable PMS'));
				}
			}
		}//if duration and cycle
	},
	
	setState: function (newState) {
		this.state = newState;
		
		if (this.state == STATE_MARK) {
			this.calendar.$forecast.legend.hide();
			this.calendar.$markable.legend.show();
			this.calendar.$forecast.hide();			
			this.calendar.$markable.show();			
		} else if (this.state == STATE_FORECAST) {
			this.calendar.$forecast.legend.show();
			this.calendar.$markable.legend.hide();
			this.calendar.$forecast.show();
			this.calendar.$markable.hide();
		}
		
	}
}

/* 
 * Date.prototype 
 */
Date.prototype.nextMonth = function () 
{
	this.setMonth(this.getMonth() + 1)
}
Date.prototype.nextYear = function () 
{
	this.setFullYear(this.getFullYear() + 1)
}
Date.prototype.nextDate = function () 
{
	this.setDate(this.getDate() + 1)
}
Date.prototype.dateJump = function (days) 
{
	this.setDate( this.getDate() + days )
}

/*
 * jQuery plugin: calendar (Date, int, boolean)
 */
$.fn.calendar = function (firstDate, monthNum, onDateMark) 
{
	var CLASS = this.CLASS = {
		monthDiv  : 'month-container',
		monthCell : 'month',
		monthName : 'month-name',
		weekRow   : 'week',
		dayCell   : 'day',
		markedDay : 'marked'
	}
		
	var weekDay, month, week, extraYear,
		date = new Date(firstDate.getTime()),
		$this = this;
		
	date.setDate(1);
	
	this.find('td.day').unbind('click')
	this.html('');
	
	var FIRST_MONTH = true;
	
	for (var i=0; i < monthNum; i++) addCalendar();
	
	if (onDateMark) 
		this.find('td.day').bind('click', onDateClick);
	
	function addCalendar () 
	{				
		weekDay = date.getDay();
		
		if (weekDay == 0) 
			weekDay = 7;
		
		month = $('<TABLE class="'+ CLASS.monthCell +'" date="'+date.getFullYear()+'-'+date.getMonth()+'"></TABLE>');
		extraYear = date.getMonth() == 0 || FIRST_MONTH ? " "+date.getFullYear() : '';
		month.append('<TR><TD class="'+ CLASS.monthName +'" colspan="7">'+monthName(date.getMonth())+extraYear+'</TD></TR>');
		month.append('<TR class="'+ CLASS.weekRow +'"><TD>пн</TD><TD>вт</TD><TD>ср</TD><TD>чт</TD><TD>пт</TD><TD>сб</TD><TD>вс</TD></TR>');
		week = $('<TR></TR>');
		
		for (var j = 1; j < weekDay; j++)  
			week.append('<TD></TD>');
		
		dim = daysInMonth(date);
		
		for (var d = 1; d <= dim; d++) 
		{
			if (j % 7 == 1) {
				month.append(week);
				week = $('<TR></TR>');
			}
			
			week.append('<TD class="'+ CLASS.dayCell +'">'+d+'</TD>');
			
			if (d == dim) {
				month.append(week);
				week = $('<TR></TR>');
			}
			j++;
		}						
		
		var div = $('<div class="'+ CLASS.monthDiv +'"></div>');			
		
		div.append(month);
		$this.append(div);
		date.nextMonth();
		
		FIRST_MONTH = false;			
	}
	
	function onDateClick () {
		var $t = $(this); 
		if ($t.hasClass(CLASS.markedDay)) {
			$t.removeClass(CLASS.markedDay);
			onDateMark($t, false);
		} else {
			$t.addClass(CLASS.markedDay);
			onDateMark($t, true);
		}
	}
	
	this.mark = function (y, m, d)
	{
		$this.find('table[date="' + y + '-' + m + '"] .day:eq('+ (parseInt(d)-1) +')').addClass(CLASS.markedDay);
	}
}
/*
 * end: jQuery::calendar
 */

/* My functions */

function notPosInt (num) { return (/[^0-9]+/).test(num) || !num || num == 0 }

function monthName (i, low) 
{
	if (i == -1) i = 11;
	if (i == 12) i = 0;
	var m = low 
		? locale_months_genitive
		: locale_months_nominative;
	return m[i]
}

function dayCase (days) 
{
	if (window.Language == 'ru') {
		var cases = [s('day case 1'), s('day case 2'), s('day case 3')];
		days %= 100;
		if ((11 <= days) && (days <= 19))
			return cases[2];
		days %= 10;
		if (days == 1)
			return cases[0];			
		if ((2 <= days) && (days <= 4))
			return cases[1];
		return cases[2];
	} else {
		if (days == 1) 
			return s('day');
		else
			return s('days');
	}
	
}

function daysInMonth (date) 
{
	var m = date.getMonth() + 1,
		y = date.getFullYear();
	return (m!=2?((m%2)^(m>7))+30:(!(y%400)||!(y%4)&&(y%25)?29:28))
}
