(function($){
var o,
    $win, $doc, $body, viewPort,
    $wrap, $store, $navPrev, $navNext,
    $currentImage, $prevImage, $nextImage,
    currentImage = new Image(), prevImage = new Image(), nextImage = new Image(),
    selectedArray = [], selectedIndex = 0, currentArray = [], currentIndex = 0, prevIndex = -1, nextIndex = -1,
    busy = false,
    isIE = $.browser.msie && $.browser.version < 7,


/* private methods */
    imageFlowAbort = function(){
        $store.empty();
        currentImage.onload = null;
        prevImage.onload = null;
        nextImage.onload = null;
    },

    imageFlowGetViewPort = function(){
        return [ $win.width(), $win.height(), $doc.scrollLeft(), $doc.scrollTop() ];
    },

    imageFlowInit = function(){
        $win = $(window);
        $doc = $(document);
        $body = $('body');

        if ($('#imageFlowWrap').length) {
            return;
        }

        $wrap = $('<div id="imageFlowWrap"></div>').append(
            $store = $('<div id="imageFlowStore" />')
        ).appendTo($body).click(function(){
            $wrap.blur();
            imageFlowHide();
        }).dblclick(function(){
            $wrap.blur();
        });

        // Custom background
        $wrap.prepend('<div class="imageFlowWrp"><div class="imageFlowWrpBot"><div class="imageFlowWrpTop"></div></div></div>')
        
        $body.append(
            $navPrev = $('<div id="imageFlowPrev"><div></div></div>'),
            $navNext = $('<div id="imageFlowNext"><div></div></div>')
        );

        $navPrev.click(function(e) {
            imageFlowPrev();
        });

        $navNext.click(function(e) {
            imageFlowNext();
        });
    },

    imageFlowStart = function(){
        imageFlowAbort();

        currentArray = selectedArray;
        currentIndex = selectedIndex;
        prevIndex = currentIndex > 0 ? currentIndex - 1 : -1;
        nextIndex = currentArray.length > currentIndex + 1 ? currentIndex + 1 : -1;

        imageFlowShow();
    },

    imageFlowProcessImage = function(image, index){
        var $img = $('<img />').attr({
            'id'    : 'imageFlowIMG' + index,
            'src'   : image.src
        }).data('size', [image.width, image.height]),
        $div = $('<div class="img_wrp" />').append($img),
        $info = $(currentArray[index]).next('div.preview-info');
        $div.append('<div class="image-title">'+ currentArray[index].title +'</div>');
        if($info.length){
            $info.clone().appendTo($div);
            o.vPadding = 78;
        }
        $store.append($div);
        return $img;
    },

    imageFlowShow = function(){
        viewPort = imageFlowGetViewPort();
        
        if(isIE){
            $wrap.css({'height': viewPort[1], 'top': viewPort[3]}).fadeIn(o.fadeInSpeed);
            $navPrev.css({'height': viewPort[1], 'top': viewPort[3]});
            $navNext.css({'height': viewPort[1], 'top': viewPort[3]});
        } else {
            $wrap.css({'height': viewPort[1]}).fadeIn(o.fadeInSpeed);
            $navPrev.css({'height': viewPort[1]});
            $navNext.css({'height': viewPort[1]});
        }
        
        if (prevIndex > -1) {
            $navPrev.fadeIn(o.fadeInSpeed);
        }

        if (nextIndex > -1) {
            $navNext.fadeIn(o.fadeInSpeed);
        }

        currentImage = new Image();
        currentImage.onload = function(){
            currentImage.onload = null;
            $currentImage = imageFlowProcessImage(currentImage, currentIndex);
            $currentImage.imageFlowScale();
                
            $currentImage.parent().css({
                'left': viewPort[0] / 2 - $currentImage.data('resize')[0] / 2,
                'top': viewPort[1] / 2 - $currentImage.data('resize')[1] / 2
            }).fadeIn(o.fadeInSpeed);

            if (prevIndex > -1) {
                prevImage = new Image();
                prevImage.onload = function(){
                    prevImage.onload = null;
                    $prevImage = imageFlowProcessImage(prevImage, prevIndex);
                    $prevImage.imageFlowScale();
                    $prevImage.parent().css({
                        'left': -$prevImage.data('resize')[0] + o.sides,
                        'top': viewPort[1] / 2 - $prevImage.data('resize')[1] / 2
                    }).fadeIn(o.fadeInSpeed);
                };
                prevImage.src = currentArray[prevIndex].href;
            }

            if (nextIndex > -1) {
                nextImage = new Image();
                nextImage.onload = function(){
                    nextImage.onload = null;
                    $nextImage = imageFlowProcessImage(nextImage, nextIndex);
                    $nextImage.imageFlowScale();
                    $nextImage.parent().css({
                        'left': viewPort[0] - o.sides,
                        'top': viewPort[1] / 2 - $nextImage.data('resize')[1] / 2
                    }).fadeIn(o.fadeInSpeed);
                };
                nextImage.src = currentArray[nextIndex].href;
            }
        };
        currentImage.src = currentArray[currentIndex].href;

        $win.bind('resize.if',function(){
            viewPort = imageFlowGetViewPort();
            // add scaling to wrapper 
            if(isIE){
                $wrap.css({'height': viewPort[1], 'top': viewPort[3]}).fadeIn(o.fadeInSpeed);
                $navPrev.css({'height': viewPort[1], 'top': viewPort[3]});
                $navNext.css({'height': viewPort[1], 'top': viewPort[3]});
            } else {
                $wrap.css({'height': viewPort[1]}).fadeIn(o.fadeInSpeed);
                $navPrev.css({'height': viewPort[1]});
                $navNext.css({'height': viewPort[1]});
            }
            
            $currentImage.imageFlowScale()
            $currentImage.parent().css({
                'left': viewPort[0] / 2 - $currentImage.data('resize')[0] / 2,
                'top': viewPort[1] / 2 - $currentImage.data('resize')[1] / 2
            });

            if (prevIndex > -1) {
                $prevImage.imageFlowScale()
                $prevImage.parent().css({
                    'left': -$prevImage.data('resize')[0] + o.sides,
                    'top': viewPort[1] / 2 - $prevImage.data('resize')[1] / 2
                });
            }

            if (nextIndex > -1) {
                $nextImage.imageFlowScale();
                $nextImage.parent().css({
                    'left': viewPort[0] - o.sides,
                    'top': viewPort[1] / 2 - $nextImage.data('resize')[1] / 2
                });
            }
        }).bind('mousewheel.if', function(){
            return false;
        }).bind('keydown.if', function(e) {
            if (e.keyCode == 27) {
                e.preventDefault();
                imageFlowHide();

            } else if (e.keyCode == 37 || e.keyCode == 39) {
                e.preventDefault();
                if (e.keyCode == 37){
                    imageFlowPrev();
                } else {
                    imageFlowNext();
                }
            }
        });
        if(isIE){
            $win.bind('scroll.if',function(){
                viewPort = imageFlowGetViewPort();
                $wrap.css({'top': viewPort[3]});
                $navPrev.css({'top': viewPort[3]});
                $navNext.css({'top': viewPort[3]});
            });
        }
    },

    imageFlowHide = function(){
        $wrap.fadeOut(o.fadeOutSpeed, function(){
            imageFlowAbort();
        });
        $navPrev.fadeOut(o.fadeOutSpeed);
        $navNext.fadeOut(o.fadeOutSpeed);
        $win.unbind('mousewheel.if').unbind('keydown.if').unbind('scroll.if').unbind('resize.if');
    },

    imageFlowPrev = function(){
        if (!busy && prevIndex > -1) {
            busy = true;

            $navNext.fadeIn(o.fadeInSpeed);

            if (nextIndex > -1) {
                $nextImage.parent().fadeOut(o.fadeOutSpeed);
            }

            $currentImage.parent().animate({
                'left': viewPort[0] - o.sides
            }, {queue: false, duration: o.moveSpeed, easing: 'easeOutExpo'});

            currentIndex--;
            prevIndex = currentIndex > 0 ? currentIndex - 1 : -1;
            nextIndex = currentArray.length > currentIndex + 1 ? currentIndex + 1 : -1;

            if (prevIndex > -1) {
                $navPrev.fadeIn(o.fadeInSpeed);
            } else {
                $navPrev.fadeOut(o.fadeOutSpeed);
            }

            $prevImage.parent().animate({
                'left': viewPort[0] / 2 - $prevImage[0].width / 2
            }, {queue: false, duration: o.moveSpeed, easing: 'easeOutExpo',
                complete: function(){
                    $nextImage = $currentImage;
                    $currentImage = $prevImage;

                    if (prevIndex > -1) {
                        prevImage = new Image();
                        prevImage.onload = function(){
                            prevImage.onload = null;
                            $prevImage = imageFlowProcessImage(prevImage, prevIndex);
                            $prevImage.imageFlowScale()
                            $prevImage.parent().css({
                                'left': -$prevImage.data('resize')[0] + o.sides,
                                'top': viewPort[1] / 2 - $prevImage.data('resize')[1] / 2
                            }).fadeIn(o.fadeInSpeed, function(){
                                busy = false;
                            });
                        }
                        prevImage.src = currentArray[prevIndex].href;
                    } else {
                        busy = false;
                    }
                }
            });
        }
    },

    imageFlowNext = function() {
        if (!busy && nextIndex > -1) {
            busy = true;

            $navPrev.fadeIn(o.fadeInSpeed);

            if (prevIndex > -1) {
                $prevImage.parent().fadeOut(o.fadeOutSpeed);
            }

            $currentImage.parent().animate({
                'left': -$currentImage[0].width + o.sides
            }, {queue: false, duration: o.moveSpeed, easing: 'easeOutExpo'});

            currentIndex++;
            prevIndex = currentIndex > 0 ? currentIndex - 1 : -1;
            nextIndex = currentArray.length > currentIndex + 1 ? currentIndex + 1 : -1;

            if (nextIndex > -1) {
                $navNext.fadeIn(o.fadeInSpeed);
            } else {
                $navNext.fadeOut(o.fadeOutSpeed);
            }

            $nextImage.parent().animate({
                'left': viewPort[0] / 2 - $nextImage[0].width / 2
            }, {queue: false, duration: o.moveSpeed, easing: 'easeOutExpo',
                complete: function(){
                    $prevImage = $currentImage;
                    $currentImage = $nextImage;

                    if (nextIndex > -1) {
                        nextImage = new Image();
                        nextImage.onload = function(){
                            nextImage.onload = null;
                            $nextImage = imageFlowProcessImage(nextImage, nextIndex);;
                            $nextImage.imageFlowScale();
                                
                            $nextImage.parent().css({
                                'left': viewPort[0] - o.sides,
                                'top': viewPort[1] / 2 - $nextImage.data('resize')[1] / 2
                            }).fadeIn(o.fadeInSpeed, function(){
                                busy = false;
                            });
                        }
                        nextImage.src = currentArray[nextIndex].href;
                    } else {
                        busy = false;
                    }
                }
            });
        }
    };


/* public methods */
    $.fn.imageFlow = function(options){
        o = $.extend({}, $.fn.imageFlow.defaults, options);

        $(this).unbind('click.if').bind('click.if', function(e){
            e.preventDefault();

            $(this).blur();

            selectedArray   = [];
            selectedIndex   = 0;

            var rel = $(this).attr('rel') || '';

            if (!rel || rel == '' || rel === 'nofollow') {
                selectedArray.push(this);
            } else {
                selectedArray   = $('a[rel=' + rel + '], area[rel=' + rel + ']');
                selectedIndex   = selectedArray.index(this);
            }

            imageFlowStart();
        });

            return this;
    };


    $.fn.imageFlowScale = function(){
        var $img = $(this),
            width = $img.data('size')[0],
            height = $img.data('size')[1],
            resize = [width, height],
            scale = 1,
            hScale = (viewPort[0] - o.hPadding * 2) / width,
            vScale = (viewPort[1] - o.vPadding * 2) / height;

        if (hScale < 1 && hScale <= vScale) {
            scale = hScale;
        } else if (vScale < 1 && vScale <= hScale) {
            scale = vScale;
        }

        resize[0] = width * scale;
        resize[1] = height * scale;

        $img.css({
            'width': resize[0],
            'height': resize[1]
        }).data('resize', resize);

        $img.parent().css({
            'width': resize[0],
            'height': resize[1]
        })

        return $img;
    };


    $.fn.imageFlow.defaults = {
        moveSpeed: 500,
        fadeInSpeed: 300,
        fadeOutSpeed: 300,
        overlayOpacity: 0.5,
        hPadding: 100,
        vPadding: 50,
        sides: 30
    };


    $(function(){
        imageFlowInit();
    });
})(jQuery);
