// omg!!11
Function.prototype.bind = function (scope) {
    var fn = this;
    return function () {
        return fn.apply(scope, arguments);
    };
};

/**
 * |||
 * asyncFunc(function () {
    this.callMethod();
   }.bind(this));
 */



function Item(obj,k)
{
    this.object = obj;                  // элемент доммодели
    this.jqobject = $(obj);
    this.weight = k;

    this.jqobject.css('position','absolute'); // set position type for dom element
};

Item.prototype.calcActualSize = function()
{
    this.actualHeight = this.engine.referenceHeight*this.weight;
    this.actualWidth = this.engine.referenceWidth*this.weight;
    return this;
};

Item.prototype.forceHide = function()
{
    this.jqobject.css('display','none');
    return this;
};


Item.prototype.setEngine = function(e)
{
    this.engine = e;
    return this;
};

Item.prototype.updateDimensions = function()
{
    this.jqobject.width(this.actualWidth);
    this.jqobject.height(this.actualHeight);
    
    this.jqobject.find('img').width(this.actualWidth);
    this.jqobject.find('img').height(this.actualHeight);
	
    this.jqobject.css('top',(this.engine.height-this.jqobject.height())/2 - 60);

    return this;
}

/*
 * main object, constructor
 */
function SliderEngine(settings/*object,height,width,weights,padding*/)
{
    this.container = settings.element;  // элемент дома, в котором лежат картинки
    this.jqcontainer = $(settings.element);
    this.height = this.jqcontainer.height();
    this.width = this.jqcontainer.width();  // собсно ширина объекта с картинками
    this.referenceHeight = settings.refHeight;  // эталонная высота (х1)
    this.referenceWidth = settings.refWidth;  // эталонная ширина
    this.items = [];  // dom elements array for container children
    this.visibleItems = [];  // array of indexes of visible elements
    this.count = this.container.childElementCount; // children items count for container shortcut
    this.visibleCount = 0;  // num of visible items
    this.weights = settings.weights; // weights array for elements
    this.padding = settings.padding; // spacing between images
    this.arrowsSize = 20; // arrows block size (must be from some class for dom element)
    
    this.arrows = new Object;
    this.arrows.left = null;  // left arrow dom element link (jquery object)
    this.arrows.right = null;  // and for right

    this.quenue = new Object;
    this.quenue.items = [];
    this.quenue.start = -1;
    this.quenue.stop = -1;

    this.animating = false; // flag that controls animation

    this.init();
};


/*
 *
 */

SliderEngine.prototype.getArrowsSize = function()
{
    
};

/*
 * Smooth animation for removing elements from left
 */
SliderEngine.prototype.removeFromLeft = function(count)
{
    if(this.visibleCount<=0) return false;
    for(var i=0;i<count;i++)
    {
        this.items[this.visibleItems[i]].jqobject.animate({
            'opacity':'0',				
            'left':('-='+this.items[this.visibleItems[i]].jqobject.width())
        },200,function(){
            this.forceHide()
        }.bind(this.items[this.visibleItems[i]]));
    }
    /*for(i=0;i<count;i++)
    {
        this.visibleItems.shift();
        this.visibleCount--;
    }*/
    return this;
};

/*
 * Smooth animation for removing elements from right
 */
SliderEngine.prototype.removeFromRight = function(count)
{
    if(this.visibleCount<=0) return false;
    for(var i=0;i<count;i++)
    {
        this.items[this.visibleItems[this.visibleCount-i-1]].jqobject.animate({
            'opacity':'0',
            'left':('+='+this.items[this.visibleItems[this.visibleCount-i-1]].jqobject.width())
        },200,function(){
            this.forceHide()
        }.bind(this.items[this.visibleItems[this.visibleCount-i-1]]));
    }
    /*for(i=0;i<count;i++)
    {
        this.visibleItems.pop();
        this.visibleCount--;
    }*/
    return this;
};

/*
 * Updates container width in engine
 */
SliderEngine.prototype.updateWidth = function()
{
    this.width = this.jqcontainer.width();
    return this;
};


/*
 * engine initialization
 */
SliderEngine.prototype.init = function ()
{
    for(var i=0;i<this.count;i++)
    {
        this.items.push(
            new Item(this.container.children[i],this.weights[i]).       // create object
            forceHide().                                                // hide it
            setEngine(this).                                            // set link to engine
            calcActualSize()                                            // set actual size reffering to element weight
            );
    }
    this.jqcontainer.css('position','relative');
    this.jqcontainer.parent().css('position','relative');
    
    
    this.fillVisible();
    this.positionVisible();
    this.fadeInVisibleGroup();
    
    if(this.items.length>this.visibleItems.length) this.addArrows();
      
};

/*
 * Floating point num equ check
 */
SliderEngine.prototype.weightEqu = function(x,y)
{
    return (Math.abs(x-y)<0.00001);
};

/*
 * remove navigation arrows
 */
SliderEngine.prototype.removeArrows = function()
{
    if(this.arrows.left!=null)
    {
        this.arrows.left.remove();
        this.arrows.left = null;
    }
    if(this.arrows.right!=null)
    {
        this.arrows.right.remove();
        this.arrows.right = null;
    }
};

/*
 * add navigation arrows
 */
SliderEngine.prototype.addArrows = function()
{
    this.arrows.left = $('<a class="left_arrow" href="javascript:void(0);">');
    this.arrows.right = $('<a class="right_arrow" href="javascript:void(0);">');
    var that = this;

    var css = {
        'position':'absolute',
        'height':'100%',
        'width':that.arrowsSize,
        'top':0
    };

    this.arrows.left.css(css);
    this.arrows.left.css('left',0); // ?

    this.arrows.right.css(css);
    this.arrows.right.css('right',0); // ?
    
    this.arrows.leftInterval = null;
    this.arrows.rightInterval = null;
    
    this.arrows.leftTimeout = null;
    this.arrows.rightTimeout = null;

    this.jqcontainer.parent().append(this.arrows.left);
    this.jqcontainer.parent().append(this.arrows.right);
    
    this.arrows.right.bind('mousedown',function(){
        this.stepRight();
				$(this.arrows.right).addClass('clicked');
				this.arrows.rightTimeout = setTimeout(function(){
            this.arrows.rightInterval = setInterval(function(){
                this.stepRight();
            }.bind(this),300);
        }.bind(this),300);
    }.bind(this));
    this.arrows.right.bind('mouseup',function(){
				$(this.arrows.right).removeClass('clicked');
        clearTimeout(this.arrows.rightTimeout);
        clearInterval(this.arrows.rightInterval);
    }.bind(this));
    
    this.arrows.left.bind('mousedown',function(){
        this.stepLeft();
				$(this.arrows.left).addClass('clicked');
        this.arrows.leftTimeout = setTimeout(function(){
            this.arrows.leftInterval = setInterval(function(){
                this.stepLeft();
            }.bind(this),300);
        }.bind(this),300);
    }.bind(this));
    this.arrows.left.bind('mouseup',function(){
				$(this.arrows.left).removeClass('clicked');
        clearTimeout(this.arrows.leftTimeout);
        clearInterval(this.arrows.leftInterval);
    }.bind(this));
};

/*
 * find INVISIBLE element with max weight and returnes it's index
 */
SliderEngine.prototype.getMaxWeight = function()
{
    var max = -1;
    var maxIndex = -1;
    for(var i=0;i<this.count;i++)
    {
        if( (this.items[i].weight>max) &&  (this.visibleItems.indexOf(i)==-1) )
        {
            max = this.items[i].weight;
            maxIndex = i;
        }
    }
    return maxIndex;
};

/*
 * returns width of visible group of elements
 * if arr and count presented it will be used instead of visibleItems inside engine
 */
SliderEngine.prototype.getVisibleWidthSum = function(arr,count)
{
    var sum = 0;
    var arrayToSum = (arr!=undefined&&count!=undefined)?arr:this.visibleItems;
    var arrayCount = (arr!=undefined&&count!=undefined)?count:this.visibleCount;

    for(var i=0;i<arrayCount;i++)
        sum+=this.items[arrayToSum[i]].actualWidth;
    if(arrayCount!=0) sum+=(arrayCount+1)*this.padding;
    return sum;
};


/*
 * check that we can add selected element to visible group (according to container width)
 * if arr and count presented it will be used instead of visibleItems inside engine
 */
SliderEngine.prototype.checkToAdd = function(index,arr,count)
{
    if(index==-1) return false;

    if(arr!=undefined&&count!=undefined)
        return (this.getVisibleWidthSum(arr,count)+this.items[index].actualWidth+((count>0?1:2)*this.padding))<(this.width-this.padding*2-this.arrowsSize*2);
    
    return (this.getVisibleWidthSum()+this.items[index].actualWidth+((this.visibleCount>0?1:2)*this.padding))<(this.width-this.padding*2-this.arrowsSize*2);
};

/*
 * add element to visible list from start or in end of list
 */
SliderEngine.prototype.addVisible = function(index,position,ignore)
{
    if(this.checkToAdd(index)||ignore!=undefined)
    {
        if(position!=undefined)
        {
            if(position)
                this.visibleItems.push(index);
            else
                this.visibleItems.unshift(index);
        }
        else
            this.visibleItems.push(index);
        this.visibleCount++;
        return this;
    }
    else
    {
        console.error('Cannot add visible element!',this);
        return false;
    }
};

SliderEngine.prototype.fillQuenue = function()
{
    var backupItems = this.visibleItems;
    var countBackup = this.visibleCount;
    this.visibleItems = [];
    this.visibleCount = 0;

    var findIndex = this.getMaxWeight();
    var pos = true;

    while(findIndex!=-1)
    {
        this.addVisible(findIndex,pos,false);
        findIndex = this.getMaxWeight();
        pos = !pos;
    }


    this.quenue.items = this.visibleItems;
    this.visibleItems = backupItems;
    this.visibleCount = countBackup;
}

/*
 * fill empty visible group with elements
 */
SliderEngine.prototype.fillVisible = function()
{
    var findIndex = this.getMaxWeight();
    var checkFlag = this.checkToAdd(findIndex);
    var pos = true;
    
    this.quenue.start = findIndex;
    this.quenue.stop = findIndex;

    while(findIndex!=-1 && checkFlag)
    {
        this.addVisible(findIndex,pos);
        findIndex = this.getMaxWeight();
        checkFlag = this.checkToAdd(findIndex);
        if (pos) this.quenue.start = findIndex;
        else if(!pos&&checkFlag) this.quenue.stop = findIndex;
        pos = !pos;
    }
    this.fillQuenue();

};

/*
 * defines visible elements positions
 */
SliderEngine.prototype.positionVisible = function()
{
    this.updateWidth(); // update current container width
    var groupWidth = this.getVisibleWidthSum(); // group width with paddings
    
    var start = (this.width-2*this.padding-2*this.arrowsSize-groupWidth)/2+(this.padding+this.arrowsSize)+this.padding;

    for(var i=0;i<this.visibleCount;i++)
    {
        if(i!=0) start+=this.padding;
        this.items[this.visibleItems[i]].jqobject.css('left',start);
        this.items[this.visibleItems[i]].updateDimensions();
        start+=this.items[this.visibleItems[i]].actualWidth;
    }
};

/*
 * smooth fadein for all visible group
 */
SliderEngine.prototype.fadeInVisibleGroup = function()
{
    this.animating = true;
    this.items[this.visibleItems[0]].jqobject.fadeIn(500,function(){
        this.animating=false;
    }.bind(this));
    for(var i=1;i<this.visibleCount;i++)
    {
        this.items[this.visibleItems[i]].jqobject.fadeIn(500);
    }
};

SliderEngine.prototype.forceShowVisibleGroup = function()
{
    this.animating = true;
    this.items[this.visibleItems[0]].jqobject.show(0,function(){
        this.animating=false;
    }.bind(this));
    for(var i=1;i<this.visibleCount;i++)
    {
        this.items[this.visibleItems[i]].jqobject.show();
    }
};

SliderEngine.prototype.rebuildVisibleGroup = function()
{
    this.animating = true;
    this.items[this.visibleItems[0]].jqobject.hide(0,function(){
        this.animating=false;
    }.bind(this));
    for(var i=1;i<this.visibleCount;i++)
    {
        this.items[this.visibleItems[i]].jqobject.fadeIn(200);
    }
};
SliderEngine.prototype.debug = function()
{
	console.log(this.quenue.start,this.quenue.stop);
	console.log(this.quenue.items,this.visibleItems);
}
/*
 * step right
 */
SliderEngine.prototype.stepRight = function()
{
    if(this.animating) return false;
	var oldVisibleItems = this.visibleItems.slice(0);
    var oldVisibleCount = this.visibleCount;
    var nextElement = (this.quenue.items.indexOf(this.quenue.stop)==this.quenue.items.length-1)?this.quenue.items[0]:this.quenue.items[this.quenue.items.indexOf(this.quenue.stop)+1];
    var shiftCount=0;

    while(!this.checkToAdd(nextElement,oldVisibleItems,oldVisibleCount))
    {
        oldVisibleItems.shift();
        oldVisibleCount--;
        shiftCount++;
    }

    oldVisibleItems.push(nextElement); // add our new element
    oldVisibleCount++;

    this.items[oldVisibleItems[oldVisibleCount-1]].updateDimensions();
    
    var newWG = this.getVisibleWidthSum(oldVisibleItems,oldVisibleCount);
    
    var start = ((this.updateWidth().width-this.padding*2-this.arrowsSize*2)-newWG)/2+(this.arrowsSize+this.padding)+this.padding;
    this.animating = true;
    for(var i=0;i<oldVisibleCount-1;i++)
    {
        this.items[oldVisibleItems[i]].jqobject.animate({
            'left':start
        },200);
        start+=this.items[oldVisibleItems[i]].jqobject.width() + this.padding;
    }
    
    this.items[oldVisibleItems[oldVisibleCount-1]].updateDimensions();
    this.items[oldVisibleItems[oldVisibleCount-1]].jqobject.css({
        'display':'block',
        'opacity':'0'
    });
    this.items[oldVisibleItems[oldVisibleCount-1]].jqobject.css('left',start+this.items[oldVisibleItems[oldVisibleCount-1]].jqobject.width());
    this.items[oldVisibleItems[oldVisibleCount-1]].jqobject.animate({
        'left':start,
        'opacity':'1'
    },200,function(){
        this.animating = false;
    }.bind(this));
    this.removeFromLeft(shiftCount);
    
    this.visibleItems = oldVisibleItems.slice(0);
    this.visibleCount = oldVisibleCount;
    this.quenue.start = oldVisibleItems[0];
    this.quenue.stop = oldVisibleItems[oldVisibleCount-1];	
    return this;
};

SliderEngine.prototype.resize = function() {
		
	this.height = this.jqcontainer.height();
    this.width = this.jqcontainer.width();  // собсно ширина объекта с картинками
     
/*    
	this.quenue = new Object;
    this.quenue.items = [];
    this.quenue.start = -1;
    this.quenue.stop = -1;
*/
    this.animating = false; // flag that controls animation

    this.jqcontainer.css('position','relative');
    this.jqcontainer.parent().css('position','relative');
 	console.log(this.visibleItems);
	this.forceHideVisibleItems();		
	this.visibleItems = [];  // array of indexes of visible elements
    this.visibleCount = 0;  // num of visible items   
    this.fillVisible();
    this.positionVisible();		
    this.forceShowVisibleGroup();  
	//console.log(this.visibleItems);
	if (this.arrows.left != null) $(this.arrows.left).remove();
	if (this.arrows.right != null) $(this.arrows.right).remove();
    
	this.arrows.left = null;  // left arrow dom element link (jquery object)
    this.arrows.right = null;  // and for right

	if(this.items.length > this.visibleItems.length) this.addArrows();
}

SliderEngine.prototype.forceHideVisibleItems = function() {
		for (i in this.items) {
			this.items[i].forceHide();
		}
}

/*
 * step left
 */
SliderEngine.prototype.stepLeft = function()
{    
    if(this.animating) return false;
	var oldVisibleItems = this.visibleItems.slice(0);	
	var oldVisibleCount = this.visibleCount;
	
	var nextElement = (this.quenue.start==0)?this.quenue.items[this.quenue.items.length-1]:this.quenue.items[this.quenue.items.indexOf(this.quenue.start)-1];

    var shiftCount=0;

    while(!this.checkToAdd(nextElement,oldVisibleItems,oldVisibleCount))
    {
        oldVisibleItems.pop();
        oldVisibleCount--;
        shiftCount++;
    }

    oldVisibleItems.unshift(nextElement); // add our new element
    oldVisibleCount++;
    
    this.items[nextElement].updateDimensions();
    var newWG = this.getVisibleWidthSum(oldVisibleItems,oldVisibleCount);
    
    var start = ((this.updateWidth().width-this.padding*2-this.arrowsSize*2)-newWG)/2+this.padding*3+this.arrowsSize+this.items[oldVisibleItems[0]].jqobject.width();
    this.animating = true;
    for(var i=1;i<oldVisibleCount;i++)
    {
        this.items[oldVisibleItems[i]].jqobject.animate({
            'left':start
        },0);
        start+=this.items[oldVisibleItems[i]].jqobject.width() + this.padding;
    }
    
    start = ((this.updateWidth().width-this.padding*2-this.arrowsSize*2)-newWG)/2+(this.padding+this.arrowsSize)/*arrow*/+this.padding;
    
    this.items[nextElement].updateDimensions();
    this.items[oldVisibleItems[0]].jqobject.css({
        'display':'block',
        'opacity':'0'
    });
    this.items[oldVisibleItems[0]].jqobject.css('left',start-this.items[oldVisibleItems[0]].jqobject.width());
    this.items[oldVisibleItems[0]].jqobject.animate({
        'left':start,
        'opacity':'1'
    },0,function(){
        this.animating = false;
    }.bind(this));
    this.removeFromRight(shiftCount);
    
    this.visibleItems = oldVisibleItems.slice(0);
    this.visibleCount = oldVisibleCount;
    
    this.quenue.start = oldVisibleItems[0];
    this.quenue.stop = oldVisibleItems[oldVisibleCount-1];
    return this;
};

