function UnitsSwitcher(
	aElements,
	oUfoMoveElement,
	oUfoAnimationElement,
	oAnimationElement,
	aChangeElements,
	aClientsChangeElements,
	aImagesSrc,
	aPreloaderColors
	) {

	this.aElements = aElements;
	this.oUfoMoveElement = oUfoMoveElement;
	this.oUfoAnimationElement = oUfoAnimationElement;
	this.oAnimationElement = oAnimationElement;
	this.aChangeElements = aChangeElements;
	this.aClientsChangeElements = aClientsChangeElements;
	this.iSelectedIndexPrev = -1;
	this.iSelectedIndex = -1;
	this.oAnimationController = new AnimationController(20);
	this.bBusy = false;
	this.oPreloaderContainerElement = null;
	this.aPreloaderElements = [];
	this.aPreloaderColors = aPreloaderColors;

	this.aImagesSrc = aImagesSrc;
	this.aImagesLoaded = [];

	this.init();

}

UnitsSwitcher.CLASS_NAME_SELECTED  = 'selected';
UnitsSwitcher.CLASS_NAME_INVISIBLE = 'invisible';
UnitsSwitcher.CLASS_NAME_INIT      = 'init';

UnitsSwitcher.ANIMATION_FRAME_WIDTH = 100;

UnitsSwitcher.prototype = {

	init : function() {

		var oThis = this;

		for(var i = 0; i < this.aElements.length; i++) {

			Common.Event.add(
				this.aElements[i],
				'click',
				function(iIndex) {

					return function(oEvent) {

						Common.Event.cancel(oEvent);

						oThis.select(iIndex);

					}

				}(i)
				);

			Common.Event.add(
				this.aElements[i],
				'mousedown',
				function(oEvent) {

					Common.Event.cancel(oEvent);

				}
				);

			if(Common.Class.match(this.aElements[i], UnitsSwitcher.CLASS_NAME_SELECTED)) {

				this.iSelectedIndex = this.iSelectedIndexPrev = i;
				this.aImagesLoaded[i] = true;

			}

		}

		Common.Event.add(
			window,
			'load',
			function() {

				oThis.startInitAnimation();

			}
			);

		this.oPreloaderContainerElement = document.createElement('div');

		for(var i = 0; i < 10; i++) {
			this.aPreloaderElements.push(this.oPreloaderContainerElement.appendChild(document.createElement('div')));
		}

	},

	select : function(iIndex) {

		if(iIndex == this.iSelectedIndex || this.isBusy()) {
			return;
		}

		if(this.iSelectedIndex > -1) {

			this.aElements[this.iSelectedIndex].parentNode.parentNode.style.height = 'auto';
			Common.Class.remove(
				this.aElements[this.iSelectedIndex],
				UnitsSwitcher.CLASS_NAME_SELECTED
				);

			this.arrowChangeAnimation(
				this.iSelectedIndex,
				false
				);

		}

		var
			oThis = this,
			oElement = oThis.aElements[iIndex].parentNode.parentNode
			;

		Common.Class.add(
			this.aElements[iIndex],
			UnitsSwitcher.CLASS_NAME_SELECTED
			);

		this.iSelectedIndexPrev = this.iSelectedIndex;
		this.iSelectedIndex = iIndex;

		this.bBusy = true;

		oThis.changeClients();

		this.oAnimationController.addAnimation(
			new AnimationEquation(
				oElement,
				{
					sProperty         : 'height',
					fEquation         : AnimationEquation.Cubic.easeOut,
					iFramesCount      : 10,
					dValueStart       : oElement.offsetHeight,
					dValueEnd         : 145,
					fStopCallbackFunction : function() {

						oThis.startUfoEmptyAnimation(true);

					}
				}
				)
			);

		this.oAnimationController.start();

	},

	isBusy : function() {

		return this.bBusy;

	},

	startUfoEmptyAnimation : function(bArrival) {

		var
			oThis = this,
			oUfoMoveElement = this.oUfoMoveElement,
			oUfoAnimationElement = this.oUfoAnimationElement,
			iUfoAnimationState = 0,
			iOffset = this.oAnimationElement.offsetWidth
			;

		this.oAnimationController.addAnimation(
			new AnimationEquation(
				oUfoMoveElement,
				{
					fEquation    : bArrival? AnimationEquation.Back.easeOut : AnimationEquation.Back.easeIn,
					iFramesCount : 20,
					dValueStart  : bArrival? iOffset : iOffset / 2,
					dValueEnd    : bArrival? iOffset / 2 : iOffset,
					fCallbackFunction : function(oAnimation) {

						oUfoMoveElement.style.top = oAnimation.getFrameCurrent() / 30 * 80 + 70 + 'px';

						oUfoAnimationElement.style.left = (-iUfoAnimationState * UnitsSwitcher.ANIMATION_FRAME_WIDTH) + 'px';

						iUfoAnimationState++;

						if(iUfoAnimationState == 3) {
							iUfoAnimationState = 0;
						}

					},
					fStopCallbackFunction : function() {

						if(bArrival) {
							oThis.startUfoLandingAnimation(true);
						}
						else {

							oThis.oUfoMoveElement.style.top = "70px";
							oThis.oUfoMoveElement.style.left = "100%";
							oThis.bBusy = false;

						}

					}
				}
				)
			);

	},

	startUfoLandingAnimation : function(bLanding) {

		var
			oThis = this,
			oUfoAnimationElement = this.oUfoAnimationElement,
			iUfoAnimationState = bLanding? 3 : 9
			;

		this.oAnimationController.addAnimation(
			new AnimationEquation(
				null,
				{
					fEquation         : AnimationEquation.linear,
					iFramesCount      : 10,
					dValueStart       : bLanding? 4 : 10,
					dValueEnd         : bLanding? 10 : 2,
					fCallbackFunction : function(oAnimation) {

						var iValueCurrent = Math.floor(oAnimation.getValueCurrent());

						if((bLanding && iValueCurrent <= iUfoAnimationState) || (!bLanding && iValueCurrent >= iUfoAnimationState)) {
							return;
						}

						oUfoAnimationElement.style.left = (-iUfoAnimationState * UnitsSwitcher.ANIMATION_FRAME_WIDTH) + 'px';

						iUfoAnimationState += bLanding? 1 : -1;

						if(bLanding && iUfoAnimationState == 10) {
							iUfoAnimationState = 9;
						}

						if(!bLanding && iUfoAnimationState == 2) {
							iUfoAnimationState = 3;
						}

					},
					fStopCallbackFunction : function() {

						if(bLanding) {
							oThis.startUfoFullAnimation(true);
						}
						else {
							oThis.startUfoEmptyAnimation(false);
						}

					}
				}
				)
			);

	},

	startUfoFullAnimation : function(bGetContent) {

		var
			oThis = this,
			oUfoMoveElement = this.oUfoMoveElement,
			oUfoAnimationElement = this.oUfoAnimationElement,
			oAnimationElement = this.oAnimationElement,
			iUfoAnimationState = 9,
			iOffset = this.oAnimationElement.offsetWidth
			;

		this.oAnimationController.addAnimation(
			new AnimationEquation(
				oUfoMoveElement,
				{
					fEquation         : bGetContent? AnimationEquation.Back.easeIn : AnimationEquation.Back.easeOut,
					iFramesCount      : 20,
					dValueStart       : bGetContent? iOffset / 2 : -iOffset,
					dValueEnd         : bGetContent? -iOffset : iOffset / 2,
					fCallbackFunction : function(oAnimation) {

						oUfoAnimationElement.style.left = (-iUfoAnimationState * UnitsSwitcher.ANIMATION_FRAME_WIDTH) + 'px';

						iUfoAnimationState++;

						if(iUfoAnimationState == 11) {
							iUfoAnimationState = 9;
						}

						oAnimationElement.style.left = oAnimation.getValueCurrent() - (iOffset / 2) + 'px';

					},
					fStopCallbackFunction : function() {

						if(bGetContent) {

							oThis.oUfoMoveElement.style.left = '-100%';

							oThis.changeContent();

							oThis.oUfoMoveElement.style.top = '70px';

							oThis.startUfoFullAnimation(false);

						}
						else {

							oThis.startUfoLandingAnimation(false);
							oThis.arrowChangeAnimation(
								oThis.iSelectedIndex,
								true
								);

						}

					}
				}
				)
			);

	},

	arrowChangeAnimation : function(
		iIndex,
		bShow
		) {

		var oElement = this.aElements[iIndex].parentNode.parentNode;

		this.oAnimationController.addAnimation(
			new AnimationEquation(
				null,
				{
					sPropert          : '',
					fEquation         : AnimationEquation.linear,
					iFramesCount      : 5,
					dValueStart       : bShow? 16 : 5,
					dValueEnd         : bShow? 5 : 16,
					fCallbackFunction : function(oAnimation) {

						oElement.style.backgroundPosition = oAnimation.getValueCurrent() + 'px 0.8em';

					}
				}
				)
			);

		this.oAnimationController.start();

	},

	changeContent : function() {

		Common.Class.add(
			this.aChangeElements[this.iSelectedIndexPrev],
			UnitsSwitcher.CLASS_NAME_INVISIBLE
			);

		Common.Class.remove(
			this.aChangeElements[this.iSelectedIndex],
			UnitsSwitcher.CLASS_NAME_INVISIBLE
			);

		this.loadImage();

	},

	loadImage : function() {

		if(this.aImagesLoaded[this.iSelectedIndex]) {
			return;
		}

		var
			oThis = this,
			oElement = document.createElement('img'),
			iIndex = this.iSelectedIndex
			;

		oElement.src = this.aImagesSrc[iIndex];

		if(oElement.complete) {
			return this.insertImage(
				iIndex,
				oElement.src
				);
		}

		this.enablePreloader(
			iIndex,
			oElement
			);


	},

	enablePreloader : function(
		iIndex,
		oImageElement
		) {

		var
			oThis = this,
			oPreloaderContainer = this.oPreloaderContainerElement.cloneNode(true)
			;

		Common.Dom.getElementsByClassName(this.aChangeElements[iIndex], 'image', 'div')[0].appendChild(
			oPreloaderContainer.parentNode?
				oPreloaderContainer.parentNode.removeChild(oPreloaderContainer) :
				oPreloaderContainer
				);

		var
			aPreloaderElements = oPreloaderContainer.getElementsByTagName('div')
			oAnimation = new Animation(),
			iCounter = 0,
			iColorCounter = 0
			;

		oAnimation.doProcess = function() {

			iCounter++;

			if(iCounter % 3 != 1) {
				return;
			}

			for(var i = 0, iCurrentColorIndex; i < aPreloaderElements.length; i++) {

				iCurrentColorIndex = iColorCounter + i >= aPreloaderElements.length?
					iColorCounter + i - oThis.aPreloaderColors.length:
					iColorCounter + i
					;

				aPreloaderElements[i].style.backgroundColor = oThis.aPreloaderColors[iCurrentColorIndex];

			}

			if(++iColorCounter >= oThis.aPreloaderColors.length) {
				iColorCounter = 0;
			}

		};

		oAnimation.shouldStop = function() {

			return false;

		};

		Common.Event.add(
			oImageElement,
			'load',
			function() {

				oAnimation.stop();
				oThis.insertImage(
					iIndex,
					oImageElement.src
					);
				oThis.disablePreloader(
					iIndex,
					oPreloaderContainer
					);

			}
			);

		this.oAnimationController.addAnimation(oAnimation);
		this.oAnimationController.start();

	},

	disablePreloader : function(
		iIndex,
		oPreloaderContainer
		) {

		var
			oAnimation = new Animation(),
			iCounter = 0
			;

		oAnimation.doProcess = function() {

			iCounter++;

			if(iCounter % 5 != 1) {
				return;
			}

			if(oPreloaderContainer.childNodes[0]) {
				oPreloaderContainer.removeChild(oPreloaderContainer.childNodes[0]);
			}

		};

		oAnimation.shouldStop = function() {

			return oPreloaderContainer.childNodes == 0;

		};

		oAnimation.setStopCallbackFunction(
			function() {

				oPreloaderContainer.parentNode.removeChild(oPreloaderContainer);

			}
			);

		this.oAnimationController.addAnimation(oAnimation);
		this.oAnimationController.start();

	},

	insertImage : function(
		iIndex,
		sSrc
		) {

		this.aChangeElements[iIndex].getElementsByTagName('img')[0].src = sSrc;

		this.aImagesLoaded[iIndex] = true;

	},

	changeClients : function() {

		Common.Class.add(
			this.aClientsChangeElements[this.iSelectedIndexPrev],
			UnitsSwitcher.CLASS_NAME_INVISIBLE
			);

		Common.Class.remove(
			this.aClientsChangeElements[this.iSelectedIndex],
			UnitsSwitcher.CLASS_NAME_INVISIBLE
			);

	},

	startInitAnimation : function() {

		if(this.bBusy) {
			return;
		}

		var
			oThis = this,
			oAnimation = new Animation(),
			iCounter = 0,
			iIndex = 0,
			bProcess = false;
			;

		oAnimation.shouldStop = function() {

			bProcess = iCounter++ % 2 == 0;

			return iIndex > oThis.aElements.length;

		};

		oAnimation.setCallbackFunction(
			function() {

				if(!bProcess) {
					return;
				}

				if(iIndex > 0) {
					Common.Class.remove(
						oThis.aElements[iIndex - 1],
						UnitsSwitcher.CLASS_NAME_INIT
						);
				}

				if(iIndex < oThis.aElements.length) {
					Common.Class.add(
						oThis.aElements[iIndex],
						UnitsSwitcher.CLASS_NAME_INIT
						);
				}

				iIndex++;

			}
			);

		this.oAnimationController.addAnimation(oAnimation);
		this.oAnimationController.start();

	}

};