/*
    TODO:
        - Add in fast-forward buttons for filmstrip to move "pages" at a time
    FIXME:
        - Add in a threshold for when to pan images
            - Maybe have it display these centered so it doesn't crop off just one side?
        - Fix slide-over transitions to always actually have something underneath
*/
(function($) {
    $.fn.awesomeGallery = function(options) {
        /* Internal Instance Variables */
        var current_index = 0;
        var previous_index = -1;
        var final_index = 0;
        var stop_filmstrip_left = false;
        var stop_filmstrip_right = false;
        var strip_middle;
        var opts;
        var gallery;

        return this.each(function() {
            var settings = $.extend({}, $.fn.awesomeGallery.defaults, options);
            opts = settings;

            gallery = $(this);
            
            var image_container = gallery.find(settings.image_container);
            settings.image_container_width = parseInt(image_container.css("width"));
            settings.image_container_height = parseInt(image_container.css("height"));
            var images = image_container.find("img");
            var img_count = images.length;
            images.hide()

            var first_image = images.eq(0);
            var last_image = images.eq(img_count - 1);
            first_image.removeClass(settings.image);
            first_image.addClass(settings.current_image);
            first_image.show();
            last_image.removeClass(settings.image);
            last_image.addClass(settings.previous_image);
            last_image.show();

            var thumb_container = gallery.find(settings.thumbnail_container);
            var thumbstrip = gallery.find(settings.thumbnail_strip);
            var thumbs = thumbstrip.find("img");
            var first_thumb = thumbs.eq(0);
            if (settings.show_filmstrip) {
                first_thumb.bind("load", function() {
                    /* Width of a thumbnail + any margin, border or padding */
                    settings.thumbnail_width = first_thumb.outerWidth() + parseInt(first_thumb.css("margin-right"));
                    first_thumb.removeClass(settings.thumbnail);
                    first_thumb.addClass(settings.current_thumbnail);

                    thumbstrip.css("width", settings.thumbnail_width * img_count);
                    strip_middle = (parseInt(thumb_container.css("width")) - settings.thumbnail_width) / 2;
                });
            }
            final_index = img_count - 1;
            next_image_if_auto_play();

            /* Mouse Events */
            if (settings.show_controls) {
                gallery.find(settings.previous_button).bind("click", function(e) {
                    goto_thumb("previous");
                });

                gallery.find(settings.next_button).bind("click", function(e) {
                    goto_thumb("next");
                });
            }

            if (settings.show_filmstrip) {
                thumbs.bind("click", function(e) {
                    goto_thumb(parseInt(this.id.substr(opts.thumb_id_prefix.length)));
                });

                gallery.find(settings.previous_thumb_button).bind("mouseenter", function(e){
                    filmstrip_slide_left(false);
                });

                gallery.find(settings.next_thumb_button).bind("mouseenter", function(e){
                    filmstrip_slide_right(false);
                });

                gallery.find(settings.previous_thumb_button).bind("mouseleave", function(e){
                    filmstrip_slide_left(true);
                });

                gallery.find(settings.next_thumb_button).bind("mouseleave", function(e){
                    filmstrip_slide_right(true);
                });
            }
        });

        function next_image() {
            goto_thumb(current_index + 1);
        }

        function goto_thumb(thumb) {
            gallery.find(opts.image_container).stopTime('next_image');
            var rnd_effect = (Math.floor(Math.random() * 1001)) % opts.transitions.length;
            var rnd_order = (Math.floor(Math.random() * 1001)) % 2;

            if (thumb == "previous")
                var index = current_index - 1;
            else if (thumb == "next")
                var index = current_index + 1;
            else
                var index = parseInt(thumb);

            if (index < 0)
                index = final_index;
            else if (index > final_index)
                index = 0;

            var next_img = gallery.find('#' + opts.image_id_prefix + index);
            
            var img_to_container_ratio = (opts.image_container_width / next_img.attr("width"));

            next_img.css("left", -3000);
            next_img.show();
            var funcname = '_' + opts.transitions[rnd_effect];
            var func = eval(funcname);
            if (index > current_index) {
                if (img_to_container_ratio < 0.80 && parseInt(next_img.attr("width")) > opts.image_container_width) {
                        if (rnd_order == 0)
                            _pan(index, "left");
                        else
                        _quick_pan(index, "left");
                } else if (funcname == '_fade') {
                        func(index, "right");
                    } else if (funcname == '_slide' && rnd_order == 0) {
                        func(index, "left", "over", "easeOutBounce");
                    } else if (funcname == '_slide' && rnd_order == 1) {
                        func(index, "left", "under", "easeInBack");
                    } else if (funcname == '_jump') {
                        func(index, "easeOutBack");
                    } else if (funcname == '_fall') {
                        func(index, "easeOutBounce");
                    } else if (funcname == '_push' && rnd_order == 0) {
                        func(index, "left");
                    } else {
                        func(index, "right");
                    }
                    var forward = true;
            } else {
                    if (img_to_container_ratio < 0.80 && parseInt(next_img.attr("width")) > opts.image_container_width) {
                        if (rnd_order == 0)
                            _pan(index, "right");
                        else
                        _quick_pan(index, "right");
                } else if (funcname == '_fade') {
                        func(index, "right");
                    } else if (funcname == '_slide' && rnd_order == 0) {
                        func(index, "right", "over", "easeOutBounce");
                    } else if (funcname == '_slide' && rnd_order == 1) {
                        func(index, "right", "under", "easeInBack");
                    } else if (funcname == '_jump') {
                        func(index, "easeOutBack");
                    } else if (funcname == '_fall') {
                        func(index, "easeOutBounce");
                    } else if (funcname == '_push' && rnd_order == 0) {
                        func(index, "left");
                    } else {
                        func(index, "right");
                    }
                    var forward = false;
            }

            gallery.find(opts.image_container).animate({
                height: parseInt(next_img.attr("height")) + "px"
            }, opts.thumbnail_strip_speed);

            previous_index = current_index;
            current_index = index;
            if (opts.show_filmstrip) {
                _move_filmstrip(forward);
                _update_thumbs(index);
            }
        }

        function next_image_if_auto_play() {
            if (opts.auto_play)
                gallery.find(opts.image_container).oneTime(opts.delay, 'next_image', next_image);
        }

        function filmstrip_slide_left(stop) {
            stop_filmstrip_right = true;  
            gallery.find(opts.image_container).stopTime('next_image');
            if (stop == false)
                stop_filmstrip_left = false;

            var thumb_strip = gallery.find(opts.thumbnail_strip);

            if (parseInt(thumb_strip.css("left")) > 0) {
                stop_filmstrip_left = true;
                thumb_strip.animate({
                    left: 0
                }, 25, "easeOutElastic");
            }

            if (!stop && !stop_filmstrip_left) {
                thumb_strip.css("left", parseInt(thumb_strip.css("left")) + 2);
                gallery.find(opts.next_thumb_button).oneTime(10, function() { filmstrip_slide_left();});
            } else {
                stop_filmstrip_left = true;
                next_image_if_auto_play();
            }
        }

        function filmstrip_slide_right(stop) {
            stop_filmstrip_left = true;
            gallery.find(opts.image_container).stopTime('next_image');
            if (stop == false)
                stop_filmstrip_right = false;

            var thumb_container = gallery.find(opts.thumbnail_container);
            var thumb_strip = gallery.find(opts.thumbnail_strip);

            if (parseInt(thumb_strip.css("left")) + parseInt(thumb_strip.css("width")) <= parseInt(thumb_container.css("width"))) {
                stop_filmstrip_right = true;
                thumb_strip.animate({
                    left: -(parseInt(thumb_strip.css("width")) - parseInt(thumb_container.css("width")))
                }, 25, "easeOutElastic");
            }

            if (!stop && !stop_filmstrip_right) {
                thumb_strip.css("left", parseInt(thumb_strip.css("left")) - 2);
                gallery.find(opts.next_thumb_button).oneTime(10, function() { filmstrip_slide_right();});
            } else {
                stop_filmstrip_right = true;
                next_image_if_auto_play();
            }
        }

        function filmstrip_page_left() {
            /* FIXME: move the filmstrip left by 1 "page", or if that would take you too far, move it to the end */
        }

        function filmstrip_page_right() {
            /* FIXME: move the filmstrip right by 1 "page", or if that would take you too far, move it to the end */
        }

        function _fade(img, direction) {
            var old_img = gallery.find('.' + opts.current_image);
            var new_img = gallery.find('#' + opts.image_id_prefix + img);
            _update_classes();

            new_img.removeClass(opts.image);
            new_img.addClass(opts.current_image);
            new_img.hide();

            new_img.css("left", "0");
            new_img.css("top", "0");

            old_img.fadeOut(opts.fade_speed);
            new_img.fadeIn(opts.fade_speed, next_image_if_auto_play);
        }

        function _jump(img, easing) {
            var old_img = gallery.find('.' + opts.current_image);
            var new_img = gallery.find('#' + opts.image_id_prefix + img);
            _update_classes();

            new_img.removeClass(opts.image);
            new_img.addClass(opts.current_image);
            new_img.css("top", opts.image_container_height);
            new_img.css("left", "0");
            new_img.show();
            new_img.animate({
                    top: 0
            }, opts.slide_speed, easing, next_image_if_auto_play);
        }

        function _push(img, direction) {
            var pre_img = gallery.find('.' + opts.previous_image);
            var old_img = gallery.find('.' + opts.current_image);
            var new_img = gallery.find('#' + opts.image_id_prefix + img);
            
            pre_img.stop(true, true);
            _update_classes();

            new_img.removeClass(opts.image);
            new_img.addClass(opts.current_image);

            if (direction == "left") {
                new_img.css("left", opts.image_container_width);
                new_img.css("top", "0");
                new_img.show();

                new_img.animate({
                    left: 0
                }, opts.slide_speed);
                
                old_img.animate({
                    left: -(parseInt(old_img.attr("width")) - opts.image_container_width)
                }, opts.slide_speed, next_image_if_auto_play);

            } else {
                new_img.css("left", -opts.image_container_width);
                new_img.css("top", "0");
                new_img.show();

                new_img.animate({
                    left: 0
                }, opts.slide_speed);

                old_img.stop().css("left", parseInt(new_img.css("left")) + parseInt(new_img.attr("width")));

                old_img.animate({
                    left: opts.image_container_width
                }, opts.slide_speed, next_image_if_auto_play);

            }
        }

        function _fall(img, easing) {
            var old_img = gallery.find('.' + opts.current_image);
            var new_img = gallery.find('#' + opts.image_id_prefix + img);
            _update_classes();

            new_img.removeClass(opts.image);
            new_img.addClass(opts.current_image);
            new_img.css("top", -new_img.attr("height"));
            new_img.css("left", "0");
            new_img.show();
            new_img.animate({
                    top: 0
            }, opts.slide_speed, easing, next_image_if_auto_play);
        }

        function _slide(img, direction, order, easing) {
            var old_img = gallery.find('.' + opts.current_image);
            var new_img = gallery.find('#' + opts.image_id_prefix + img);

            _update_classes();

            new_img.removeClass(opts.image);
            new_img.addClass(opts.current_image);
            new_img.css("top", "0");

            if (direction == "left")
                new_img.css("left", opts.image_container_width);
            else
                new_img.css("left", -new_img.attr("width"));

            new_img.show();
            new_img.animate({
                    left: 0
            }, opts.slide_speed, easing, next_image_if_auto_play);
                /* FIXME: This transition is broken...
                var prev_img = gallery.find('.' + opts.previous_image)
                prev_img.addClass(opts.image);
                prev_img.removeClass(opts.previous_image);
                prev_img.hide();
                new_img.addClass(opts.previous_image);
                new_img.removeClass(opts.image);

                if (direction == "left")
                    var to_pos = -parseInt(old_img.attr("width"));
                else
                    var to_pos = opts.image_container_width;

                new_img.show();
                old_img.animate({
                        left: to_pos
                }, opts.slide_speed, easing, function() {
                    new_img.removeClass(opts.previous_image);
                    new_img.addClass(opts.current_image);
                    old_img.addClass(opts.previous_image);
                    old_img.removeClass(opts.current_image);
                    
                    next_image_if_auto_play();
                });
            }*/
        }

        function _pan(img, direction) {
            _update_classes();

            var new_img = gallery.find('#' + opts.image_id_prefix + img);

            new_img.removeClass(opts.image);
            new_img.addClass(opts.current_image);
            new_img.css("top", "0");

            if (direction == "left") {
                    new_img.css("left", opts.image_container_width);
                    new_img.show();
                    new_img.animate({
                            left: opts.image_container_width - new_img.attr("width")
                    }, opts.pan_speed, next_image_if_auto_play);
            } else {
                new_img.css("left", -new_img.attr("width"));
                    new_img.show();
                    new_img.animate({
                            left: 0
                    }, opts.pan_speed, next_image_if_auto_play);
            }
        }

        function _quick_pan(img, direction) {
            /* Variation of pan where the image slides quickly into view and then slows once it has
                filled the viewport
                NB: The automagic easing that jQuery does makes this sort of screwy, so I chose the
                    only easing function I found that didn't appear to make it slow funny between
                    the two chained animations */
            var new_img = gallery.find('#' + opts.image_id_prefix + img);
            _update_classes();

            new_img.removeClass(opts.image);
            new_img.addClass(opts.current_image);
            new_img.css("top", "0");

            if (direction == "left") {
                    new_img.css("left", opts.image_container_width);
                    new_img.show();
                    new_img.animate({
                        left:  0
                    }, opts.pan_speed / 2, "easeInBack", function() {
                        new_img.animate({
                                left: opts.image_container_width - new_img.attr("width")
                        }, opts.pan_speed, next_image_if_auto_play);
                    });
            } else {
                    new_img.css("left", -new_img.attr("width"));
                    new_img.show();
                    new_img.animate({
                        left: opts.image_container_width - new_img.attr("width")
                    }, opts.pan_speed / 2, "easeInBack", function() {
                        new_img.animate({
                                left: 0
                        }, opts.pan_speed, next_image_if_auto_play);
                    });
            }
        }

        function _update_thumbs(img) {
            var old_img = gallery.find('.' + opts.current_thumbnail);
            var new_img = gallery.find('#' + opts.thumb_id_prefix + img);
            old_img.addClass(opts.thumbnail);
            old_img.removeClass(opts.current_thumbnail);
            new_img.removeClass(opts.thumbnail);
            new_img.addClass(opts.current_thumbnail);
        }

        function _update_classes() {
            var old_img = gallery.find('.' + opts.previous_image);
            var new_img = gallery.find('.' + opts.current_image);
            
            old_img.addClass(opts.image);
            old_img.removeClass(opts.previous_image);
            old_img.hide();

            new_img.addClass(opts.previous_image);
            new_img.removeClass(opts.current_image);
        }

        function _move_filmstrip(forward) {
            var thumbnail_strip_width = parseInt(gallery.find(opts.thumbnail_strip).css("width"));
            var thumbnail_container_width = parseInt(gallery.find(opts.thumbnail_container).css("width"));

            var current_width = (current_index * opts.thumbnail_width) + 1;
            var thumb_strip = gallery.find(opts.thumbnail_strip);

            var distance = current_index * opts.thumbnail_width;
            var diststr = (distance>=0?'-':'')+Math.abs(distance - strip_middle)+'px';

            var new_left = parseInt(thumb_strip.css("left"));

            if (current_width > strip_middle && current_width < thumbnail_strip_width - strip_middle)
                var new_left = diststr;
            else if (current_index == 0 || current_width < strip_middle)
                var new_left = 0;
            else if (current_index == final_index || current_width > thumbnail_strip_width - strip_middle)
                var new_left = -(thumbnail_strip_width - thumbnail_container_width);

            thumb_strip.animate({
                left: new_left
            }, opts.thumbnail_strip_speed);
        }
    };

    $.fn.awesomeGallery.defaults = {
        /* Element Selectors */
        image_container: '.images',
        thumbnail_container: '.thumb_container',
        thumbnail_strip: '.thumbs',
        previous_button: '.prev',
        next_button: '.next',
        previous_thumb_button: '.thumb_prev',
        next_thumb_button: '.thumb_next',
        /* CSS Classes */
        current_image: 'current',
        previous_image: 'previous',
        image: 'slideshow',
        current_thumbnail: 'thumb_current',
        thumbnail: 'thumb',
        image_id_prefix: 'img_',
        thumb_id_prefix: 'thumb_',
        /* Settings */
        fade_speed: 1000,
        slide_speed: 2200,
        pan_speed: 2500,
        auto_play: false,
        delay: 4500,
        show_filmstrip: true,
        show_controls: true,
        pan_large_images: true,
        thumbnail_strip_speed: 800,
        /* Pan and Quick Pan are special transitions and not put in here */
        transitions: new Array('fade', 'slide', 'jump', 'push', 'fall')
    }
})(jQuery);
