/**
@author Stepan Reznikov (stepan@design.ru)
@version 2.0, 03.07.2007
*/

/*-----------*/
/*  Ellipse  */
/*-----------*/

function Ellipse(oPtr){

	this.bDrag = false;
	this.bEnabled = false;
	this.oPtr = oPtr;
	this.oSlider = oPtr.getElementsByTagName('div')[0];
	this.pad = 10;

	this._listeners = [];
	
	/*
	уравнение эллипса:
	y = b1/a1 * sqrt(a1^2 - x^2) где a1 и b1 -- радиусы эллипса
	
	уравнение прямой
	y = a2*x + b2 (в нашем случае b2 = 0, т.к. прямая проходит через центр эллипса)
	
	отсюда следует, что точка пересечения прямой и эллипса равна:
	x = b1 / sqrt(a2^2 + (b1/a1)^2)
	y = tan(alpha) * x
	*/

	this.a = (oPtr.offsetWidth - this.pad * 2) / 2;
	this.b = (oPtr.offsetHeight - this.pad * 2) / 2;
	this.k = this.b / this.a;
	
	this.step = Math.PI / 4;
	this.alpha = -Math.PI / 2;

	// на сколько смещать слайдер при установлении позиции
	this.iSliderOffsetX = this.oSlider.offsetWidth / 2 - this.pad;
	this.iSliderOffsetY = this.oSlider.offsetHeight / 2 - this.pad;

}

Ellipse.prototype.isEnabled = function(){
	return this.bEnabled;
}

Ellipse.prototype.enable = function(){
	this.bEnabled = true;
	Common.Class.add(this.oSlider, 'enabled');

	var me = this;
	Common.Event.add(this.oPtr, 'mousedown', function(evt){ me.startDrag(evt); });
	Common.Event.add(document, 'mousemove', function(evt){ me.doDrag(evt); });
	Common.Event.add(document, 'mouseup', function(evt){ me.stopDrag(evt); });
	Common.Event.add(document, 'keydown', function(evt){ me.keyFeedback(evt); });
}

Ellipse.prototype.getAlpha = function(x, y){

	var oPos = Common.Dom.getAbsoluteCoords(this.oPtr);

	// центр эллипса
	var iCenterX = oPos.iLeft + this.a + this.pad;
	var iCenterY = oPos.iTop + this.b + this.pad;

	return Math.atan2(y - iCenterY, x - iCenterX);
}

Ellipse.prototype.getDegree = function(){
	var iDegree = this.alpha.toDegree();
	if (iDegree == 360) {
		iDegree = 0;
	}
	else if (iDegree < 0) {
		iDegree = iDegree + 360;
	}
	return iDegree;
}

// выдает точку пересечения эллипса и прямой, проходящей через центр эллипса и точку (x,y)
Ellipse.prototype.getPoint = function(alpha){
	var _x = this.b / Math.sqrt(Math.pow(Math.tan(alpha), 2) + this.k * this.k);
	var _y = Math.tan(alpha) * _x;
	if (this.changeSign(alpha)) {
		_x *= -1;
		_y *= -1;
	}
	return {x: _x, y: _y};
}

Ellipse.prototype.changeSign = function(alpha){
	return Math.abs(alpha).between(Math.PI / 2, Math.PI);
}

Ellipse.prototype.moveSliderTo = function(x, y){
	this.oSlider.style.left = x + this.a - this.iSliderOffsetX;
	this.oSlider.style.top = y + this.b - this.iSliderOffsetY;
	this.broadcast();
}

Ellipse.prototype.startDrag = function(evt){
	this.bDrag = true;
	Common.Class.add(document.body, 'move');
	this.doDrag(evt);
}

Ellipse.prototype.doDrag = function(evt){
	if(this.bDrag && (evt = Common.Event.normalize(evt))){
		this.alpha = this.getAlpha(evt.clientX, evt.clientY);
		var oPoint = this.getPoint(((Math.abs(this.alpha.toDegree()) == 90) ? -1 : 1 ) * this.alpha);
		this.moveSliderTo(oPoint.x, oPoint.y);
	}
}

Ellipse.prototype.stopDrag = function(evt){
	this.bDrag = false;
	Common.Class.remove(document.body, 'move');
}

Ellipse.prototype.keyFeedback = function(evt){
	if((evt = Common.Event.normalize(evt))){
		var a = this.alpha + Math.PI;
		switch(evt.keyCode){
			case 37: //left
			case 38: //up
				a += this.step;
				break;
			case 39: //right
			case 40: //down
				a += -this.step;
				break;
		}

		this.alpha = ((a + Math.PI * 2) % (Math.PI * 2)) - Math.PI;
		var oPoint = this.getPoint(((Math.abs(this.alpha.toDegree()) == 90) ? -1 : 1 ) * this.alpha);
		this.moveSliderTo(oPoint.x, oPoint.y);
	}
}

Ellipse.prototype.nextPosition = function(sDirection){
	var a = this.alpha + Math.PI;
	switch(sDirection){
		case "left":
			a += this.step;
			break;
		case "right":
			a += -this.step;
			break;
	}

	this.alpha = ((a + Math.PI * 2) % (Math.PI * 2)) - Math.PI;
	var oPoint = this.getPoint(((Math.abs(this.alpha.toDegree()) == 90) ? -1 : 1 ) * this.alpha);
	this.moveSliderTo(oPoint.x, oPoint.y);
}

Ellipse.prototype.addListener = function(f){
	this._listeners.push(f);
}

Ellipse.prototype.broadcast = function(){
	for(var i = 0, iLength = this._listeners.length; i < iLength; i++){
		this._listeners[i](this.getDegree());
	}
}


/*----------------*/
/*  Arrow switch  */
/*----------------*/

function ArrowSwitch(oPtr){
	this.bEnabled = false;
	this.oPtr = oPtr;
	this.oArrowLeft = oPtr.getElementsByTagName('div')[0];
	this.oArrowRight = oPtr.getElementsByTagName('div')[1];
	this._listeners = [];
}

ArrowSwitch.prototype.isEnabled = function(){
	return this.bEnabled;
}

ArrowSwitch.prototype.enable = function(){
	this.bEnabled = true;
	Common.Class.add(this.oPtr, 'enabled');

	var me = this;

	Common.Event.add(this.oArrowLeft, 'click', function(){ me.broadcast('left'); });
	Common.Event.add(this.oArrowLeft, 'mouseover', function(){ Common.Class.add(me.oArrowLeft, 'hover'); });
	Common.Event.add(this.oArrowLeft, 'mouseout', function(){ Common.Class.remove(me.oArrowLeft, 'hover'); });

	Common.Event.add(this.oArrowRight, 'click', function(){ me.broadcast('right'); });
	Common.Event.add(this.oArrowRight, 'mouseover', function(){ Common.Class.add(me.oArrowRight, 'hover'); });
	Common.Event.add(this.oArrowRight, 'mouseout', function(){ Common.Class.remove(me.oArrowRight, 'hover'); });
}

ArrowSwitch.prototype.addListener = function(f){
	this._listeners.push(f);
}

ArrowSwitch.prototype.broadcast = function(sDirection){
	for(var i = 0, iLength = this._listeners.length; i < iLength; i++){
		this._listeners[i](sDirection);
	}
}


/*-------------------*/
/*  Rotation object  */
/*-------------------*/

function RotationObject(oPtr, iPosition){
	this.oPtr = oPtr;
	this.iPosition = iPosition;
	this.iPositionPrevious = 0;
}

RotationObject.prototype.rotate = function(iPosition){
	this.iPositionPrevious = this.iPosition;
	this.iPosition = iPosition;
	Common.Class.replace(this.oPtr, 'position' + this.iPositionPrevious, 'position' + this.iPosition);
}

RotationObject.prototype.setColor = function(oImage){
	this.oPtr.style.backgroundImage = 'url(' + oImage.src + ')'; 
}


/*-----------------------*/
/*  Rotation controller  */
/*-----------------------*/

function RotationController(oObject, iPositionsCount, oEllipse, oArrowSwitch){

	this.oObject = oObject;
	this.bEnabled = false;

	this.aColors = new Array();
	this.oActiveColor = null;
	this.oActiveColorPrevious = null;

	this.aBackgrounds = new Array();

	this.oEllipse = oEllipse;
	this.iDegreeStep = 360 / iPositionsCount;

	this.oArrowSwitch = oArrowSwitch;

	var me = this;

	if (oEllipse) {
		oEllipse.addListener(
			function(iDegree){
				me.rotate(iDegree);
			}
		);
	}

	if (oArrowSwitch) {
		oArrowSwitch.addListener(
			function(sDirection){
				me.nextPosition(sDirection);
			}
		);
	}
}

RotationController.prototype.isEnabled = function(){
	return this.bEnabled;
}

RotationController.prototype.addColor = function(oColor){
	this.aColors[this.aColors.length] = oColor;
}

RotationController.prototype.enableColor = function(oColor){
	if (!this.oActiveColor) {
		this.oActiveColor = oColor;
		this.oActiveColor.activate();
	}
	this.enableRotation();
}

RotationController.prototype.activateColor = function(oColor){
	if (!oColor.isActive()) {
		this.oActiveColorPrevious = this.oActiveColor;
		this.oActiveColorPrevious.deactivate();
		this.oActiveColor = oColor;
		this.oActiveColor.activate();
		this.oObject.setColor(oColor.getImage());
	}
}

RotationController.prototype.addBackground = function(oBackground){
	this.aBackgrounds[this.aBackgrounds.length] = oBackground;
}

RotationController.prototype.enableBackground = function(oBackground){
	this.enableRotation();
}

RotationController.prototype.isAllBackgroundsLoaded = function(){
	var bAllBackgroundsLoaded = true;
	for (var i = 0, iLength = this.aBackgrounds.length; i < iLength; i++) {
		if (!this.aBackgrounds[i].isEnabled()) {
			bAllBackgroundsLoaded = false;
			break;
		}
	}
	return bAllBackgroundsLoaded;
}

RotationController.prototype.enableRotation = function(){
	if (!this.isEnabled() && this.oActiveColor && this.isAllBackgroundsLoaded()) {
		this.bEnabled = true;
		this.oEllipse.enable();
		this.oArrowSwitch.enable();
		this.oObject.setColor(this.oActiveColor.getImage());
	}
}

RotationController.prototype.rotate = function(iDegree){
	var iPositionDegree = iDegree - this.iDegreeStep / 2;
	if (iPositionDegree < 0) {
		iPositionDegree = iPositionDegree + 360;
	}
	var iPosition = Math.floor(iPositionDegree / this.iDegreeStep);
	this.oObject.rotate(iPosition);
}

RotationController.prototype.nextPosition = function(sDirection){
	this.oEllipse.nextPosition(sDirection);
}


/*----------------*/
/*  Color object  */
/*----------------*/

function ColorObject(oPtr){
	this.oPtr = oPtr;
}

ColorObject.prototype.setColor = function(oImage){
	this.oPtr.style.backgroundImage = 'url(' + oImage.src + ')';
}


/*--------------------*/
/*  Color controller  */
/*--------------------*/

function ColorController(oObject){
	this.oObject = oObject;
	this.aColors = new Array();
	this.oActiveColor = null;
	this.oActiveColorPrevious = null;
}

ColorController.prototype.addColor = function(oColor){
	this.aColors[this.aColors.length] = oColor;
}

ColorController.prototype.enableColor = function(oColor){
	if (oColor == this.aColors[0]) {
		this.oActiveColor = oColor;
		this.oActiveColor.activate();
		Common.Class.add(document.body, this.oActiveColor.getId());
	}
}

ColorController.prototype.activateColor = function(oColor){
	if (!oColor.isActive()) {
		this.oActiveColorPrevious = this.oActiveColor;
		this.oActiveColorPrevious.deactivate();
		this.oActiveColor = oColor;
		this.oActiveColor.activate();
		Common.Class.replace(document.body, this.oActiveColorPrevious.getId(), this.oActiveColor.getId());
		this.oObject.setColor(oColor.getImage());
	}
}


/*---------*/
/*  Color  */
/*---------*/

function Color(oPtr, oController, sImageUrl, sFunImageUrl){

	var me = this;

	this.bEnabled = false;
	this.bActive = false;
	this.oPtr = oPtr;
	this.oController = oController;
	this.oController.addColor(this);

	this.oImage = document.createElement('img');
	this.oImage.src = sImageUrl;
	Common.Event.add(this.oImage, 'load', function(){ me.enable(); });

	this.bFunImageEnabled = false;

	if (typeof sFunImageUrl != "undefined") {
		this.oFunImage = document.createElement('img');
		this.oFunImage.src = sFunImageUrl;
		Common.Event.add(this.oFunImage, 'load', function(){ me.enableFunImage(); });
	}
}

Color.prototype.isEnabled = function(){
	return this.bEnabled;
}

Color.prototype.isActive = function(){
	return this.bActive;
}

Color.prototype.enable = function(){

	this.bEnabled = true;
	Common.Class.add(this.oPtr, 'enabled');
	this.oController.enableColor(this);

	var me = this;
	Common.Event.add(this.oPtr, 'click', function(){ me.oController.activateColor(me); });
	Common.Event.add(this.oPtr, 'mouseover', function(){ me.mouseover(); });
	Common.Event.add(this.oPtr, 'mouseout', function(){ me.mouseout(); });
}

Color.prototype.isFunImageEnabled = function(){
	return this.bFunImageEnabled;
}

Color.prototype.enableFunImage = function(){
	this.bFunImageEnabled = true;
}

Color.prototype.getImage = function(){
	if (this.isFunImageEnabled() && Math.floor(Math.random() * 2 + 1) == 1) {
		return this.oFunImage;
	}
	else {
		return this.oImage;
	}
}

Color.prototype.getId = function(){
	return this.oPtr.id;
}

Color.prototype.activate = function(){
	this.bActive = true;
	Common.Class.replace(this.oPtr, 'hover', 'active');
}

Color.prototype.deactivate = function(){
	this.bActive = false;
	Common.Class.remove(this.oPtr, 'active');
}

Color.prototype.mouseover = function(){
	if (!this.isActive()) {
		Common.Class.add(this.oPtr, 'hover');
	}
}

Color.prototype.mouseout = function(){
	Common.Class.remove(this.oPtr, 'hover');
}


/*--------------*/
/*  Background  */
/*--------------*/

function Background(oController, sImageUrl){

	var me = this;

	this.bEnabled = false;
	this.oController = oController;

	this.oImage = document.createElement('img');
	this.oImage.src = sImageUrl;
	Common.Event.add(this.oImage, 'load', function(){ me.enable(); });
}

Background.prototype.isEnabled = function(){
	return this.bEnabled;
}

Background.prototype.enable = function(){
	this.bEnabled = true;
	this.oController.enableBackground(this);
}
