var AdvancedAnimation = ( function( isInternetExplorer8AndOlder ) {
    var $ = jQuery;
    var $head = $( 'head' );
    var intAdvancedAnimationLastId = 0;

    var AdvancedAnimation = function( $this ) {
        if ( $this.is( '[data-advanced-animation-id]') ) {
            return;
        }
        this.cols = 1;
        this.rows = 1;
        this.direction = 'forward';
        this.animation = true;
        this.$element = $this;
        this.$element.filter( ':not( [data-advanced-animation-id] )' ).attr( 'data-advanced-animation-id', ++intAdvancedAnimationLastId );
        this.$element.find( '.pt-item' ).wrap( '<div class="pt-page" />' );
        this.$element.wrapInner( '<div class="pt-part" />' );
        this.generateHtml();
        this._showParts( this.$element.find( '.pt-part' ), 1 );
    };

    AdvancedAnimation.prototype = {
        next: function() {
            var $next = this.$element.find( '.pt-part' ).eq( this.direction === 'backward' ? -1 : 0 ).find( '.pt-page-current:not(.pt-page-prev)' ).next();

            if ( $next.length ) {
                this.show( $next );
            }
            else {
                this.show( this.$element.find( '.pt-part' ).eq( this.direction === 'backward' ? -1 : 0 ).find( '.pt-page' ).eq( 0 ) );
            }
        },

        show: function( $new, options ) {
            var intPtPageNumber = $new.prevAll().length + 1;

            if ( $new.is( '.pt-page-current:not(.pt-page-prev)' ) ) {
                return;
            }
            options = $.extend( {}, {
                animation: true
            }, options );
            this.animation = options.animation;
            this._waveJumpToEnd();
            if ( this.animation ) {
                this._runWave( intPtPageNumber );
            } else {
                this._showParts( this.$element.find( '.pt-part' ), intPtPageNumber );
            }
            this.intPrevPtPageNumber = intPtPageNumber;
        },

        setQuantityParts: function( intCols, intRows ) {
            this.cols = intCols;
            this.rows = intRows;
            this.generateHtml();
        },

        setAnimationProperties: function( options ) {
            var intId = this.$element.attr( 'data-advanced-animation-id' );
            var $stylesheet = $head.find( 'style[data-advanced-animation-id="' + intId + '"]' );

            this.duration = options.duration;
            if ( isInternetExplorer8AndOlder() ) {
                return;
            }
            if ( $stylesheet.length === 0 ) {
                $stylesheet = $head.append( '<style type="text/css" data-advanced-animation-id="' + intId + '" />' ).children( ':last-child' );
            }
            $stylesheet.html('\
                [data-advanced-animation-id="' + intId + '"] .pt-page {\
                    -webkit-animation-duration: ' + options.duration + ';\
                    -moz-animation-duration: ' + options.duration + ';\
                    animation-duration: ' + options.duration + ';\
                    -webkit-animation-timing-function: ' + options.transitionTimingFunction + ';\
                    -moz-animation-timing-function: ' + options.transitionTimingFunction + ';\
                    animation-timing-function: ' + options.transitionTimingFunction + ';\
                }\
            ');
        },

        setHideEffect: function( hideEffect ) {
            this.prevHideEffect = this.hideEffect;
            this.hideEffect = hideEffect;
            if ( /moveTo|rotateRoom|rotateCarousel|rotateSlideOut/.test( hideEffect ) ) {
                this.$element.find( '.pt-part' ).addClass( 'hide-overflow' );
            }
            else {
                this.$element.find( '.pt-part' ).removeClass( 'hide-overflow' );                
            }
        },

        setShowEffect: function( showEffect ) {
            this.prevShowEffect = this.showEffect;
            this.showEffect = showEffect;
        },

        setDirection: function( direction ) {
            this.direction = direction;
        },

        _runWave: function( intPtPageNumber ) {
            this.$element.find( '.pt-part' ).addClass( 'in-queue' );
            this._showNextPart( intPtPageNumber );
        },

        _waveJumpToEnd: function() {
            clearTimeout( this.showPartsTimeout );
            if ( typeof this.intPrevPtPageNumber !== 'undefined' ) {
                this._showParts( this.$element.find( '.pt-part.in-queue' ).removeClass( 'in-queue' ), this.intPrevPtPageNumber );
            }
        },

        _showNextPart: function( intPtPageNumber ) {
            var self = this;
            var $part = this.$element.find( '.pt-part.in-queue' ).eq( this.direction === 'backward' ? -1 : 0 );

            if ( $part.length === 0 ) {
                return;
            }
            this._showParts( $part.removeClass( 'in-queue' ), intPtPageNumber );
            if ( $part.length === 0 ) {
                return;
            }
            clearTimeout( this.showPartsTimeout );
            this.showPartsTimeout = setTimeout( function() {
                self._showNextPart( intPtPageNumber );
            }, parseFloat( this.duration ) * 1000 * 0.25 / Math.max( 1, this.$element.find( '.pt-part' ).length - 1 ) );
        },

        _showParts: function( $this, intPtPageNumber ) {
            $this.find( '.pt-page-current.pt-page-prev' ).removeClass( 'pt-page-prev' ).removeClass( 'pt-page-current' );
            $this.find( '.pt-page-current' ).addClass( 'pt-page-prev' );
            $this.find( '.pt-page:nth-child(' + intPtPageNumber + ')' ).addClass( 'pt-page-current' );
            $this.find( '.pt-page' ).removeClass( this.hideEffect ).removeClass( this.showEffect );
            if ( typeof this.prevHideEffect !== 'undefined' ) {
                $this.find( '.pt-page' ).removeClass( this.prevHideEffect );
            }
            if ( typeof this.prevShowEffect !== 'undefined' ) {
                $this.find( '.pt-page' ).removeClass( this.prevShowEffect );
            }
            if ( this.animation ) {
                $this.find( '.pt-page-prev' ).addClass( this.hideEffect );
                $this.find( '.pt-page-current:not(.pt-page-prev)' ).addClass( this.showEffect );
            }
        },

        hideActive: function() {
            this.$element.find( '.pt-page-current' ).addClass( 'pt-page-prev' ).addClass( this.hideEffect );
        },

        generateHtml: function() {
            var intI;
            var intJ;
            var $content;

            this.$element.html( this.$element.find( '.pt-part' ).eq( 0 ).html() );
            $content = this.$element.html();
            this.$element.children( '.pt-part' ).remove();
            for ( intJ = 0; intJ < this.rows; intJ++ ) {
                for ( intI = 0; intI < this.cols; intI++ ) {
                    this.$element
                        .append( '<div class="pt-part pt-perspective" data-col-no="' + intI + '" data-row-no="' + intJ + '" style="position: absolute;"></div>' )
                        .children( ':last-child' )
                        .html( $content )
                        .find( '.pt-item' );
                }
            }
            this.setPositionParts();
            this.$element.children( ':not(.pt-part)' ).remove();
        },

        setPositionParts: function() {
            var self = this;
            var intWidth = this.$element.outerWidth();
            var intHeight = this.$element.outerHeight();
            var intPartWidth = intWidth / this.cols;
            var intPartHeight = intHeight / this.rows;

            this.$element.find( '.pt-part' ).each( function() {
                var $this = $( this );
                var intI = $this.attr( 'data-col-no' );
                var intJ = $this.attr( 'data-row-no' );

                $this
                .css( {
                    left: self.$element.outerWidth() * ( 100 / self.cols * intI ) / 100 + 'px',
                    top: self.$element.outerHeight() * ( 100 / self.rows * intJ ) / 100 + 'px',
                    width: self.$element.outerWidth() * ( 100 / self.cols ) / 100 + 1 + 'px',
                    height: self.$element.outerHeight() * ( 100 / self.rows ) / 100 + 1 + 'px'                   
                } )
                .find( '.pt-item' )
                .css( {
                    width: intWidth,
                    height: intHeight,
                    left: - intPartWidth * intI,
                    top: - intPartHeight * intJ
                } );
            } );          
        }
    };
    
    return AdvancedAnimation;
} )( isInternetExplorer8AndOlder );