/* -------------------------------------------------------------------------- */
/* ------------------------------ Calendar ---------------------------------- */
/* -------------------------------------------------------------------------- */

var dayHeight = 24;
var dayWidth = 36;

var Calendar = function( target, drag, errMsg, start, end ) {
	Calendar.superClass.apply( this, arguments );

	this.Id = 'Calendar';
	this.start = start || new Date();
	this.end = end;

	this.monthsToStrList = [ 'янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек' ];
	this.monthsListGC = [ 'января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря' ];
	this.weekdaysList = [ 'воскресенье', 'понедельник', 'вторник', 'среда', 'четверг', 'пятница', 'суббота' ];
	this.length = ( end.valueOf() - start.valueOf() ) / 1000 / 60 / 60 / 24;
	this.avToSelect = 0;
	this.status = false;
	this.errMsgs = ( errMsg instanceof Array ) ? errMsg : new Array();

	this.drag = drag;

	this.sDays = new Array();

	this.element = target;
	this.inner = this.element.firstChild;

	this.monthsList = new Array();

	this.daysWrap = this.inner.lastChild;
	this.daysElement = this.daysWrap.firstChild;
	this.hovered = false;

	var curDay = this.start.getDay();
	if( curDay == 0 ) {
		curDay = 7;
	}

	var i = 0;
	while( curDay > i + 1 ) {
		i++;
	}

	this.emptyDays = i;
	this.period = false;

	if( this.length + this.emptyDays > 56 && !this.scroll ) {
		this.scroll = new Scroll( this.inner, this.daysElement );
		this.scroll.drag = this.drag;
	}

	if( 'v' == '\v' ) {
		this.addHandler( this.element, 'mousemove', this.dayHover );
	} else {
		this.addHandler( this.element, 'mouseover', this.dayHover );
	}
	this.addHandler( this.daysElement, 'click', this.daySelect );
}
Calendar.inheritsFrom( HTMLGlif );

/* ----------------------- Calendar Get Params ------------------------------ */

Calendar.prototype.getParams = function() {
	if( this.sDays.length == 0 ) return false;
	var result = new Array();
	for( var c in this.sDays ) {
		result[ c ] = this.sDays[c];
	}
	return result;
}

/* ------------------------- Calendar Check --------------------------------- */

Calendar.prototype.check = function() {
	var oS = this.status;
	this.status = false;
	if( this.avToSelect > 0 && ( this.avToSelect - this.sDays.length ) == 0 ) {
		this.status = true;
	}
	if( !( oS === this.status ) ) this.notify( 'calendarStatusChange' );
}

/* ----------------------- Calendar Change Mode ----------------------------- */

Calendar.prototype.changeMode = function( iCount ) {
	iCount = iCount || 0;
	if( typeof( iCount ) != 'number' ) return false;
	this.avToSelect = iCount;
	if( this.sDays.length > this.avToSelect ) {
		this.clear( this.sDays.length - this.avToSelect );
	}
	
	this.check();
}

/* ------------------------------ Rebuild ----------------------------------- */
/*
Calendar.prototype.rebuild = function( start, end ) {
	this.scroll.remove();
	for( var i = this.daysElement.childNodes.length; i; i-- ) {
		this.daysElement.removeChild( this.daysElement.childNodes[ i - 1 ] );
	}
}
*/
/* --------------------------- Calendar Set ---------------------------------- */

Calendar.prototype.set = function( aDays ) {
	aDays = aDays || new Array();

	if( aDays.length == 0 ) return false;
	var jsMS = this.start.valueOf();

	for( var c in aDays ) {
		var cD = aDays[c].valueOf();
		var delta =  parseInt( ( cD - jsMS ) / 1000 / 60 / 60 / 24 );
		var idx = this.emptyDays + delta;
		idx++;
		if( idx < 0 || idx > this.length + this.emptyDays ||  isNaN( idx ) ) break;
		var cell = {
			x: idx - parseInt( idx / 7 ) * 7,
			y: parseInt( idx / 7 ),
			pos: idx
		}
		this.choose( cell, parseInt( c ) );
		//this.setFrame( true, parseInt( c ), cell );
	}
	this.createPeriod();
}

/* ---------------------- Calendar Create Period ---------------------------- */

Calendar.prototype.createPeriod = function( start, end ) {
	var list = this.daysElement.childNodes;

	if( !( this.period === false ) && !( this.period.start === false ) && !( this.period.end === false ) ) {
		for( var i = this.period.start + 1; i < this.period.end; i++ ) {
			this.setClassName( 'period', false, list[i] );
		}
		
		this.period = false;	
	}

	start = ( start <= this.emptyDays ? this.emptyDays - 1 : start );
	var start = start || ( this.sDays[0] ? this.sDays[0].idx : false );
	var end =  end || ( this.sDays[ this.sDays.length - 1 ] ? this.sDays[ this.sDays.length - 1 ].idx : false );

	if( !( start === false ) && !( end === false ) && start != end ) {
		this.period = { start: start, end: end }
		
		for( var i = this.period.start + 1; i < this.period.end; i++ ) {
			this.setClassName( 'period', true, list[i] );
		}
	}

	this.check();
} 

/* ----------------------- Calendar Get Cell -------------------------------- */

Calendar.prototype.getCell = function( x, y ) {	
	this.poffset = this.offset( this.daysElement );
	var gX = x - this.poffset.left;
	var gY = y - this.poffset.top;
	
	var cell = new Object();
	
	var mX = Math.floor( gX / dayWidth );
	var mY = Math.floor( gY / dayHeight );

	if(  mX >= 0 && mY >= 0 && mX < 7 && mY < ( this.length / 7 ) + 1 ) {
		cell.x = mX;
		cell.y = mY;
		cell.pos = ( mY * 7 + mX );
	} else {
		return false;
	}
	return cell;
}

/* ----------------------------- Check Cell --------------------------------- */

Calendar.prototype.checkCell = function( cell, type ) {
	var list = this.sDays;
	var left = ( list[ type - 1 ] && list[ type - 1 ].idx ) || -1;
	var right = ( list[ type + 1 ] && list[ type + 1 ].idx ) || Infinity;

	if( ( cell.pos - this.emptyDays ) > this.length ) return false; 
	if( ( ( cell.pos < right ) && ( cell.pos > left ) ) || right == left ) {
		return true;
	}
	return false;
}

/* ----------------------------- Set Frame ---------------------------------- */

Calendar.prototype.setFrame = function( flag, type, cell ) {
	if( flag ) {
		var nFrame = new Frame();
		nFrame.append( this.daysElement );
		nFrame.type = type;
		nFrame.drag = this.drag;
		nFrame.setClassName( 't' + type, true );

		this.sDays[ type ] = new Object();
		this.sDays[ type ].frame = nFrame;

		nFrame.attachObserver( 'FrameDraged', this.dragFrame, this );
		this.moveFrame( type, cell );
	} else {
		this.sDays[ type ].frame.remove();
		this.sDays.splice( type, 1 );
	}

	this.dayHover( this.sDays.length - 1 );
}

/* ---------------------------- Move Frame ---------------------------------- */

Calendar.prototype.moveFrame = function( type, cell ) {
	var frame = this.sDays[ type ].frame;

	var date = new Date();
	date.setFullYear( this.start.getFullYear() );
	date.setMonth( this.start.getMonth() );
	date.setDate( this.start.getDate() + cell.pos - this.emptyDays );
	this.sDays[ type ].date = date;
	this.sDays[ type ].idx = cell.pos;

	frame.setPosition( cell.x * dayWidth, cell.y * dayHeight );
	frame.setContent( date.getDate() + '' );
}

/* ---------------------------- Drag Frame ---------------------------------- */

Calendar.prototype.dragFrame = function( params, obj ) {
	var cell = this.getCell( params.x, params.y );
	if( !cell || cell.pos < this.emptyDays || this.sDays[ obj.type ].idx == cell.pos ) return false;

	if( this.checkCell( cell, obj.type ) ) {
		this.choose( cell, obj.type );
	} else {
		return false;
	}
}

/* -------------------------- Calendar Clear -------------------------------- */

Calendar.prototype.clear = function( iToClearCol ) {
	var list = this.sDays;
	var iTCC = ( iToClearCol > list.length ? false : iToClearCol ) || list.length;

	for( var i = 0; i < iTCC; i++ ) {
		this.setFrame( false, list.length - 1 );
	}

	this.createPeriod();
}

/* ---------------------------- Calendar Hover ------------------------------- */

Calendar.prototype.dayHover = function( params, ev ) {
	if( this.drag.processing ) return false;
	var type = params >= 0 ? params : this.sDays.length;
	if( !( this.hovered === false ) ) {
		this.setClassName( 't' + type + '-hover', false, this.daysElement.childNodes[ this.hovered ] );
		this.hovered = false;
	}
	
	if( params >= 0 ) return false;
	if( this.avToSelect - this.sDays.length <= 0 ) return false;
	
	var cursor = this.drag.getCursorPosition( ev );
	var cell = this.getCell( cursor.x, cursor.y );

	if( cell.pos < this.emptyDays || !this.checkCell( cell, type ) ) return false;
	if( !cell ) return false;

	this.setClassName( 't' + type + '-hover', true, this.daysElement.childNodes[ cell.pos ] );
	this.hovered = cell.pos;
}

/* --------------------------- Calendar Select ------------------------------- */

Calendar.prototype.daySelect = function( params, ev ) {
	var cursor = this.drag.getCursorPosition( ev );
	var cell = this.getCell( cursor.x, cursor.y );

	var list = this.sDays;
	if( list.length == this.avToSelect ) {
		this.notify( 'dayUnSelect', list.length );
		this.clear();
	}

	if( !cell || cell.pos < this.emptyDays || cell.pos - this.emptyDays > this.length ) return false;

	if ( this.avToSelect - list.length > 0 && this.checkCell( cell, list.length ) ) {
		this.choose( cell, list.length );
	} else {
		var closerest = Infinity;
		var type = false;
		for( var c in list ) {
			var cD = list[c];
			if( Math.abs( cell.pos - cD.idx ) < Math.abs( cell.pos - closerest ) ) {
				closerest = cD.idx;
				type = c;
			}
		}

		if( !( closerest < Infinity ) ) return false;
		this.choose( cell, type );
	}
}

/* --------------------------- Calendar Select ------------------------------- */

Calendar.prototype.getError = function() {
	var errMsg = '';
	if( this.avToSelect == 1 && this.sDays.length == 0 ) errMsg += this.errMsgs[ 0 ];
	if( this.avToSelect == 2 && this.sDays.length == 1 ) errMsg += this.errMsgs[ 1 ];
	if( this.avToSelect == 2 && this.sDays.length == 0 ) errMsg += this.errMsgs[ 2 ];
	
	return errMsg;
}

/* --------------------------- Calendar Choose ------------------------------- */

Calendar.prototype.choose = function( cell, type ) {	
	if( !this.sDays[ type ] ) {
		this.setFrame( true, type, cell );
	} else {
		this.moveFrame( type, cell );
	}

	var date = new Date();
	date.setFullYear( this.start.getFullYear() );
	date.setMonth( this.start.getMonth() );
	date.setDate( this.start.getDate() + cell.pos - this.emptyDays );
	
	this.createPeriod();
	this.notify( 'daySelect', { date: date, type: type, cell: cell });
}

/* -------------------------------------------------------------------------- */
/* ------------------------------- Frame ------------------------------------ */
/* -------------------------------------------------------------------------- */

Frame = function() {
	Frame.superClass.apply( this, arguments );
	
	this.Id = 'Frame';
	this.element = this.createNode( 'div', { 'class': 'dayframe' } );
	this.addHandler( this.element, 'mouseover', this.dragSet, true );
	this.addHandler( this.element, 'mouseout', this.dragSet, false );
}
Frame.inheritsFrom( HTMLGlif );

/* -------------------------------------------------------------------------- */
/* ------------------------------ Frame Drag -------------------------------- */
/* -------------------------------------------------------------------------- */

Frame.prototype.dragSet = function( flag ) {
	if( !this.drag || this.drag.processing ) return false;
	if( flag ) {
		this.drag.set( this, this.startDrag, this.move );
	} else {
		this.drag.set( false );
	}
}

/* ---------------------------- Start ------------------------------------ */

Frame.prototype.startDrag = function( x, y ) {
	var offset = this.offset();
	this.cell = false;
}

/* ----------------------------- Move ------------------------------------ */

Frame.prototype.move = function( x, y ) {
	this.notify( 'FrameDraged', { x: x, y: y } );
}

/* -------------------------------------------------------------------------- */
/* ------------------------------- Scroll ----------------------------------- */
/* -------------------------------------------------------------------------- */

Scroll = function( target, beacon ) {	
	Scroll.superClass.apply( this, arguments );
	
	this.Id = 'Scroll';
	this.ie6 = ( '\v' == 'v' && typeof document.documentElement.style.maxHeight == "undefined" );
	this.beacon = beacon;
	this.target = target;
	this.element = this.createNode( 'div', { 'class': 'scroll' }, target, '<span class="t"><i class="png"></i></span><span class="b"><i class="png"></i></span>' );
	this.thumbler = new HTMLGlif( 'div', { id: 'thumbler' }, this.element, '<span class="t"><i class="png"></i></span><span class="b"><i class="png"></i></span>' );

	var offset = this.offset();

	this.delta = offset.top; /* Костыль */

	this.sh = offset.height;
	this.dh = beacon.offsetHeight;

	this.th =  Math.floor( this.sh * ( this.sh / this.dh ) );
	this.thumbler.setStyle( { height: this.th + 'px' } );

	this.step = Math.floor( dayHeight * this.sh / this.dh );

	this.addHandler( this.thumbler.element, 'mouseover', this.dragSet, true );
	this.addHandler( this.thumbler.element, 'mouseout', this.dragSet, false );

	this.addHandler( this.element, 'click', this.scrollTo, null );
	if ( this.ie6 ){
		this.addHandler( this.target, 'mousewheel', this.scrollTo, true );
	}
	else {
		this.addHandler( this.target, 'DOMMouseScroll', this.scrollTo, true );
	}

	return this;
}
Scroll.inheritsFrom( HTMLGlif );

Scroll.prototype.setPos = function( pos ) {	
	var tp;
	var bp;
	if( pos >= 0 && pos  <= this.sh - this.th ) {
		tp = Math.floor( pos );
		bp = Math.floor( -1 * pos * ( this.dh - this.sh ) / ( this.sh  - this.th ) );
	}

	if( pos <= 0 ) {
		tp = 0;
		bp = 0;
	}

	if( pos > this.sh - this.th  ) {
		tp = this.sh - this.th;
		bp = -1 * ( this.dh - this.sh );
	}

	this.thumbler.setPosition( false, tp );
	this.setPosition( false, bp, this.beacon );
}

Scroll.prototype.scrollTo = function( bool, ev ) {
	ev = ev || window.event;
	var pos;
	
	if( bool == null ) {
		var cP = this.drag.getCursorPosition( ev );
		this.setPos( ( cP.y - this.delta ) - this.th / 2 );
		this.stopPropagation( ev );
	} else {
		var top = parseInt( this.thumbler.offset().top - this.delta ) || 0;
		var wDelta = ev.wheelDelta / 40 || -ev.detail;
		this.setPos( top - ( this.step * wDelta ) );
		this.preventDefault( ev );
	}
}

/* ---------------------- Scroll Drag & Drop ----------------------------- */

Scroll.prototype.dragSet = function( flag ) {
  if( !this.drag || this.drag.processing ) return false;
	if( flag === true ) {
		var offset = this.offset();
		this.delta = offset.top;
		this.drag.set( this, this.startDrag, this.move );
	} else {
		this.drag.set( false );
	}
}

/* ---------------------------- Start ------------------------------------ */

Scroll.prototype.startDrag = function( x, y ) {
	var offset = this.offset();
	this.dragDelta = y - offset.top;
}

/* ----------------------------- Move ------------------------------------ */

Scroll.prototype.move = function( x, y ) {
	this.setPos( ( y - this.delta ) - this.th / 2 );
}