MYG = window.MYG || {};

MYG.Iterator = new Class({
    ar : [],
    pointer : 0,
    initialize : function(ar){
        this.ar = ar;
    },
    setArray : function(ar){
        this.ar = ar;
    },
    stepIndex : function(forward,from){
        forward = forward===false?false:true;
        var i = ( from!=undefined ? from : this.pointer)+(forward?1:-1);
        if( i >= this.ar.length ){
            i = 0;
        }else if( i < 0){
            i = this.ar.length-1;
        }
        return i;
    },
    count : function(){
        return this.ar.length;
    },
    nextIndex : function(from){
        return this.stepIndex(true,from);
    },
    prevIndex : function(from){
        return this.stepIndex(false,from);
    },
    next : function(moveTo){
        return this.seek(this.nextIndex(),moveTo);
    },
    prev : function(moveTo){
        return this.seek(this.prevIndex(),moveTo);
    },
    current : function(){
        return this.ar[this.pointer] || null;
    },
    seek : function(i,moveTo){
        if(this.ar[i]){
            if(moveTo!==false){
                this.pointer = i;
            }
            return this.ar[i];
        }
        return null;
    },
    find : function(obj){
        return this.ar.indexOf(obj);
    },
    search : function(fn, key){
        for(var i=0; i<this.ar.length; i++){
            if( fn(this.ar[i]) ){
                return key===true ? i : this.ar[i];
            }
        }
        return null;
    },
    getIndex : function(){
        return this.pointer;
    },
    getArray : function(){
        return this.ar;
    },
    add : function(o){
        this.ar[this.ar.length] = o;
        return this;
    },
    remove : function(i){
        if(this.ar[i]){
            return this.ar.splice(i,1);
        }
        return false;
    },
    each : function(fn){
        this.ar.each(fn);
    }
});
MYG.Image = new Class({
    
    initialize : function(options){
        this.setOptions(options);
        this.loaded = {};
        this.preloaded={};
        ['large','thumb'].each(function(type){
            this[type] = this.options[type];
            this[type].width = parseInt(this[type].width);
            this[type].height = parseInt(this[type].height);
            this[type].preload = new Image();
            this[type].preload.onload = this.onLoad.bind(this, type);
        }.bind(this));
    },
    
    preload : function(type){
        if(!type){ type = 'large'; }
        if(this.loaded[type]){ return; }
        this[type].preload.src = this[type].url;
    },
    
    onReady : function(type, loadFn, waitFn, scope){
        if( !this.loaded[type] ){
            this.addEvent('load'+type, loadFn.bind(scope) );
            if( waitFn ){
                waitFn.call(scope || window, this);
            }
            this.preload(type);
        }else{
            loadFn.apply(scope || window, [this, false]);
        }
    },
    
    onLoad : function(type){
        this.loaded[type] = true;
        if( this[type].preload ){
            this[type].width = this[type].preload.width;
            this[type].height = this[type].preload.height;
        }
        this.fireEvent('load'+type, [this, true]);
        delete this[type].preload;
    }
    
});
MYG.Image.implement( new Options );
MYG.Image.implement( new Events );

MYG.Gallery = new Class({
    
    ct : null,
    
    elements : {},
    
    fx : {},
    
    index : 0,
    
    slides : [],
    
    options: {
        pause : 6000,
        transitionDuration: 500,
        slideIn: false,
        emptyImageUrl:"http:/"+"/extjs.cachefly.net/ext-2.2/resources/images/default/s.gif"
    },
    
    initialize : function(el, options){
        
        this.elements={};
        this.fx={};
        this.elements.ct = $(el);
        
        if( !this.elements.ct ){
            throw "No Container Element Found!";
        }
        
        
        
        this.setOptions(options);
        var images = this.options.images;
        for(var i=0; i<images.length; i++){
            images[i] = new MYG.Image(images[i]);
        }
        this.images = new MYG.Iterator(images);
        //this.subAlbums = new MYG.Iterator(this.options.images);
        this.setup();
        if( this.images.count() < 2 ){
            // there is really no reason for a gallery here. lets ditch.
        }
        if( this.options.autoPlay && this.images.count() > 1 ){
            this.delayedNext();
        }
        this.setPlayingState(this.options.autoPlay, false);
        //this.play();
        this.fireEvent('change', [this.images.current(), this.activeSlide]);
        window.gallery = this;
    },
    
    setPlayingState : function(playing, display){
        var same = this.playing == playing;
        this.playing = playing;
        this.elements.ct.addClass('myg-'+((playing)?'playing':'paused'));
        this.elements.ct.removeClass('myg-'+((playing)?'paused':'playing'));
        if( display !== false && !same){
            // display
            this.elements.ct.addClass('myg-status-update');
            (function(){
                this.elements.ct.removeClass('myg-status-update');
            }).delay(1000, this);
        }
    },
    
    setup : function(){
        // initialize container framework
        // first, lets swap out the image for a div with a bg-image
        this.elements.frame = this.elements.ct.getElement('.myg-image-frame');
        this.elements.scroller = new Element('div',{'class':'myg-image-scroller'}).injectInside(this.elements.frame);
        this.elements.title = this.elements.ct.getElement('.myg-image-title');
        var img = this.elements.frame.getElement('img');
        
        var index = this.images.search(function(obj){
            return obj.large.url == img.href;
        }, true);
        
        var current = index ? this.images.seek(index) : this.images.current();
        
        current.onReady( 'large' , function(){
            this.elements.frame.setStyles({
                width       :current.large.width+'px',
                height      :current.large.height+'px'
            });
            this.activeSlide = this.createSlide(current);
            if( img ){
                img.remove();
            }
            this.addEvent('change', this.onChange);
        }.bind(this));
        
        
        this.fx.frame = this.elements.frame.effects({duration:this.options.transitionDuration});
        this.fx.frame.start = function(){
            var ret = Fx.Styles.prototype.start.apply(this, arguments);
            if( this.to['width'] ){
                if( this.to['width']%2 != this.from['width']%2 ){
                    this.to['width']-=1;
                }
            }
            return ret;
        };
        this.fx.frame.increase = function(){
            // smooth width transitions...
            if( this.to['width'] ){
                var m = this.from['width']%2;
                var w = Math.round(this.now['width']);
                if( w%2 != m && this.now['width'] != this.to['width']){
                    w-=1;
                }
                this.now['width'] = w;
            }
            return Fx.Styles.prototype.increase.call(this);
        };
        
        
        this.elements.scroller.addEvent('mouseenter', function(){
            this.elements.frame.addClass('myg-image-frame-hover');
            this.elements.scroller.addEvent('mousemove', this.onFrameMouseMove.bind(this) );
        }.bind(this));
        this.elements.scroller.addEvent('mouseleave', function(){
            this.elements.frame.removeClass('myg-image-frame-hover');
            this.elements.scroller.removeEvent('mousemove', this.onFrameMouseMove.bind(this) );
            this.closeThumbs();
        }.bind(this));
        
        // loading indicator
        this.elements.loader = new Element('div',{'class':'myg-loader'}).injectInside(this.elements.scroller);
        this.elements.loaderBg = new Element('div',{'class':'myg-loader-bg'}).injectInside(this.elements.loader);
        this.elements.loaderSpinner = new Element('div',{'class':'myg-loader-spinner'}).injectInside(this.elements.loader).setHTML('Loading...');
        
        // play / pause button
        this.elements.playBtn = new Element('a',{'class':'myg-play-btn'}).injectInside(this.elements.scroller);
        this.elements.playBtn.addEvent('click', this.togglePlay.bind(this) );
        
        // nav buttons
        this.elements.prev = new Element('a', {'class':'myg-nav-btn myg-nav-btn-prev'}).injectInside(this.elements.frame);
        this.elements.next = new Element('a', {'class':'myg-nav-btn myg-nav-btn-next'}).injectInside(this.elements.frame);
        // add arrows
        this.elements.prevArrow = new Element('span', {'class':'myg-nav-btn-arrow'}).injectInside(this.elements.prev);
        this.elements.nextArrow = new Element('span', {'class':'myg-nav-btn-arrow'}).injectInside(this.elements.next);
        
        this.elements.prev.addEvent('click', this.prevBtn.bind(this));
        this.elements.next.addEvent('click', this.nextBtn.bind(this));
        
        // okay, lets setup the thumbs...
        // owladmin_mjoom5 | c8e61cc
        // 
        this.elements.thumbBox = new Element('div', {'class':'myg-thumbbox'}).injectInside(this.elements.scroller);
        this.fx.thumbBox = this.elements.thumbBox.effects({duration: 300});
        
        this.elements.thumbNavPrev = new Element('a', {'class':'myg-thumb-nav myg-thumb-nav-prev'}).injectInside(this.elements.thumbBox);
        this.elements.thumbNavNext = new Element('a', {'class':'myg-thumb-nav myg-thumb-nav-next'}).injectInside(this.elements.thumbBox);
        this.elements.thumbNavPrev.addEvent('click', this.onThumbNavPrevClick.bind(this) );
        this.elements.thumbNavNext.addEvent('click', this.onThumbNavNextClick.bind(this) );
        
        this.elements.thumbBoxBg = new Element('div', {'class':'myg-thumbbox-bg'}).injectInside(this.elements.thumbBox);
        this.elements.thumbScroller = new Element('div', {'class':'myg-thumbbox-scroller'}).injectInside(this.elements.thumbBox);
        
        
        this.elements.thumbTable = new Element('table',{'cellpadding':0,'cellspacing':0}).injectInside(this.elements.thumbScroller);
        this.fx.thumbScroller = new Fx.Elements([this.elements.thumbScroller, this.elements.thumbTable], {duration: this.options.transitionDuration});
        this.elements.thumbTbody = new Element('tbody').injectInside(this.elements.thumbTable);
        this.elements.thumbRow = new Element('tr').injectInside(this.elements.thumbTbody);        
        this.populateThumbs();
        this.thumbsOpen = false;
        this.setTitle();
        
        this.elements.thumbBox.addEvent('mousewheel', this.onWheelThumbs.bind(this) );
        
        
    },
    
    
    populateThumbs : function(){
        this.images.each( function(image, index){
            var thumbTd = new Element('td').injectInside(this.elements.thumbRow);
            var thumb = new Element('img', {'class':'myg-thumb', 'styles':{
                width           :image.thumb.width+'px',
                height          :image.thumb.height+'px'
            }, 'src': image.thumb.url, title: image.options.title, unselectable:'on'}).injectInside( thumbTd );
            image.thumb.el = thumb;
            image.thumb.td = thumbTd;
            thumb.addEvent('click', this.thumbClick.bind(this, index));
        }.bind(this));
    },
    
    onFrameMouseMove : function(e){
        e = new Event(e);
        if( !this.thumbBuffer ){
            this.thumbBuffer = this.elements.thumbScroller.getSize().size.y;
            if( this.options.thumbBuffer ){
                this.thumbBuffer += this.options.thumbBuffer;
            }
            else{
                this.thumbBuffer *= 2;
            }
        }
        var t = this.elements.frame.getTop();
        
        if( e.page.y - t < this.thumbBuffer ){
            if( !this.thumbsOpen ){
                this.openThumbs();
            }
        }
        else{
            if( this.thumbsOpen ){
                this.closeThumbs();
            }
        }
    },
    
    thumbClick : function(index){
        this.setPlayingState(false);
        this.seek(index);
    },
    
    scrollThumbs : function(dir){
        var i = this.images.find(this._currentThumb);
        if( (dir < 0 && i < 1 ) || (dir > 0 && i >= this.images.count()-1) ){
            return;
        }
        i += dir;
        var seekTo = this.images.seek(i,false);
        this.scrollThumb(seekTo);
    },
    
    onThumbNavPrevClick : function(){
        this.pause();
        this.scrollThumbs(-1);
    },
    
    onThumbNavNextClick : function(){
        this.pause();
        this.scrollThumbs(1);
    },
    
    onWheelThumbs : function(e){
        e = new Event(e);
        e.stop();
        this.scrollThumbs(e.wheel*-1);
    },
    
    openThumbs : function(){
        var y = this.elements.thumbScroller.getSize().size.y;
        this.fx.thumbBox.stop();
        this.fx.thumbBox.start({height:y});
        this.thumbsOpen = true;
    },
    
    closeThumbs : function(){
        this.fx.thumbBox.stop();
        this.fx.thumbBox.start({height:0});
        this.thumbsOpen = false;
    },
    
    onChange : function(image, active, old){
        this.highlightThumb(image);
        this.scrollThumb(image);
        this._lastImage = image;
        // change hash?
        // window.location.hash = '#'+image.options.id;
    },
    
    highlightThumb : function(image){
        if( this._lastImage){
            this._lastImage.thumb.el.removeClass('myg-thumb-active');
        }
        image.thumb.el.addClass('myg-thumb-active');
    },
    
    scrollThumb : function(image){
        this._currentThumb = image;
        var margin = this.elements.thumbTable.getCoordinates().left - image.thumb.td.getCoordinates().left;
        this.fx.thumbScroller.stop();
        this.fx.thumbScroller.start({
            0: {
                'marginLeft':( -1 * image.thumb.td.getSize().size.x/2)+'px'
            },
            1: {
                'marginLeft':margin
            }
        });
        
    },
    
    createSlide : function(image,styles){
        styles = $merge({
            'display':'block',
            'backgroundImage':'url('+image.large.url+')',
            'width':image.large.width+'px',
            'height':image.large.height+'px',
            'marginTop':(image.large.height/2*-1),
            'marginLeft':(image.large.width/2*-1)
        },styles||{});
        var slide = new Element('div', {
            'class':'myg-slide',
            'styles':styles
            // ,'src':this.options.emptyImageUrl
        });
        this.slides[this.slides.length] = slide;
        return slide.injectInside(this.elements.scroller);
    },
    
    delayedNext : function(){
        this.images.next(false).preload('large');
        this.timeout = this.next.delay(this.options.pause, this);
    },
    
    togglePlay : function(){
        this.playing ? this.pause() : this.play();
    },
    
    play : function(){
        this.setPlayingState(true);
        this.images.next(false).preload('large');
        this.next();
    },
    
    pause : function(display){
        this.setPlayingState(false,display);
        $clear(this.timeout);
    },
    
    clearSlides : function(){
        while( this.slides.length > 1 ){
            this.slides.each(function(slide, i){
                if( slide && slide != this.activeSlide ){
                    this.slides.splice(i,1).remove();
                    slide.remove();
                }
            }.bind(this));
        }
    },
    
    next : function(){
        this.images.next().onReady('large', this.updateImage, this.showLoading, this);
    },
    
    nextBtn : function(){
        this.pause(false);
        this.next();
    },
    
    prevBtn : function(){
        this.pause(false);
        this.prev();
    },
    
    prev : function(){
        this.images.prev().onReady('large', this.updateImage, this.showLoading, this);
    },
    
    seek : function(index){
        this.images.seek(index).onReady('large', this.updateImage, this.showLoading, this);
    },
    
    showLoading : function(){
        // TODO: display waiting indication
        // console.log('loading....');
        if( this._showLoadingTimeout ){
            $clear(this._showLoadingTimeout);
        }
        this._showLoadingTimeout = this.elements.ct.addClass.delay(100,this.elements.ct,'myg-loading');
    },
    
    setTitle : function(){
        this.elements.title.setHTML(this.images.current().options.title);
    },
    
    emptyFn : function(){},
    
    updateImage : function(image, wasWaiting){
        image.onReady( 'large', function(){
            $clear(this.timeout);
            if( this._showLoadingTimeout ){
                $clear(this._showLoadingTimeout);
            }
            this.elements.ct.removeClass('myg-loading');
            if( wasWaiting ){
                // TODO: remove waiting graphic
                // console.log('loaded');
                
            }
            var old = this.activeSlide;
            this.activeSlide = this.createSlide(image,{opacity:0});
            if( old ){
                // grab the left and rights
                // current width
                var props = {opacity:[1,0]};
                if( this.options.slideIn){
                    var cw = this._lastImage.large.width;
                    props.left = [cw/2,-cw/2];
                }
                old.effects({duration:this.options.transitionDuration}).start(props).chain(this.clearSlides.bind(this));
            }
            var props = {opacity:[0,1]};
            if( this.options.slideIn ){
                var nw = image.large.width;
                props.left = ['200%', nw/2];
            }
            this.activeSlide.effects({duration:this.options.transitionDuration})
                .start(props)
                .chain(this.setTitle.bind(this))
                ;
            this.fx.frame
                .start({width:image.large.width,height:image.large.height})
                .chain(function(){
                    if( this.playing ) this.delayedNext();
                    this.images.next(false).preload('large');
                    this.images.prev(false).preload('large');
                }.bind(this));
                
            this.fireEvent('change', [image, this.activeSlide, old]);
        }.bind(this));
    }
    
});
MYG.Gallery.implement( new Events );
MYG.Gallery.implement( new Options );

(function(){
    var instances = [];
    MYG.Gallery.create = function(el, options){
        window.addEvent('domready', function(){
            window.gallery = instances[instances.length] = new MYG.Gallery(el,options);
        });
    }
})();
