/**
 * @constructor
 * @class PhotoGallery
 */
var PhotoGallery = new JS.Class({
    include: JS.State,
    
    /**
     * @param {Array} images
     */
    initialize: function(images, launcher) {
        this._launcher = launcher;
        this._images = images.map(function(image) {
            return new this.klass.Item(this, image);
        }, this);
        this._mask = new Ojay.PageMask({color: '#000000', opacity: this.klass.MASK_OPACITY});
        this._stripsVisible = true;
        this._mask.show('fade');
        this._region = Ojay.getVisibleRegion();
        this._setupFilmStrip();
        this._setupImageViewer();
        this._setupMouseListener();
        this._cacheImages();
        this.applyTheme(this.klass.DEFAULT_THEME);
        this.setState('READY');
        this.showItem(this._images[0]);
    },
    
    _cacheImages: function() {
        this._images.forEach(function(image) { Ojay.HTML.img({src: image._fullSrc}) });
    },
    
    _setupFilmStrip: function() {
        this._filmStripOverlay = new Ojay.ContentOverlay({
            className: this.klass.FILMSTRIP_CLASS
        });
        this._filmStripOverlay.positionInFront(this._mask);
        
        var filmstrip = Ojay( Ojay.HTML.div() );
        this._images.map('getHTML').forEach(filmstrip.method('insert'));
        this._filmStripOverlay.setContent(filmstrip);
        
        var width = this._region.getWidth(),
            height = this.klass.FILMSTRIP_HEIGHT,
            padding = this.klass.FILMSTRIP_PADDING;
        
        this._filmStripHeight = height;
        
        this._filmStripOverlay
            .setSize(width, height + padding)
            .setPosition(0, this._region.bottom - height - padding);
        
        if (this._images.length >= 2)
        	this._filmStripOverlay.show('fade');
        
        this._paginator = new Ojay.Paginator(filmstrip, {
            width: (width - padding) + 'px',
            height: '90px'
        });
        this._paginator.setup();
        
        var controls = this._paginator.addControls('before');
        controls.getPreviousButton().setContent('<div class="previous-btn"><div>Previous</div></div>');
        controls.getNextButton().setContent('<div class="next-btn"><div>Next</div></div>');
        
        this._filmStripOverlay.insert(Ojay.HTML.div({className: this.klass.THEMES_CLASS}, function(HTML) {
            var key, element;
            for (key in this.klass.Themes) {
                element = Ojay( HTML.div() );
                this.klass.Themes[key].element = element;
                element.setStyle({background: this.klass.Themes[key].identity})
                    .on('click')._(this).applyTheme(key);
            }
        }.bind(this)), 'top');
        
        this._imageIndicator = Ojay( Ojay.HTML.div({className: this.klass.INDICATOR_CLASS}) );
        this._filmStripOverlay.insert(this._imageIndicator.node, 'top');
        
        var container = this._filmStripOverlay.getContainer(),
            content = this._filmStripOverlay.getContentElement();
        
        content.setStyle({position: 'absolute', left: 0, top: 0});
        this.hideStrips();
    },
    
    _setupMouseListener: function() {
        var last = Number(new Date), limit = this.klass.MOUSE_TIMEOUT;
        Ojay.Mouse.subscribe(function() { last = Number(new Date); this.showStrips() }, this);
        setInterval(function() {
            var time = Number(new Date);
            if ((time - last) / 1000 > limit) this.hideStrips();
        }.bind(this), (limit / 4) * 1000);
    },
    
    _setupImageViewer: function() {
        this._imageViewerOverlay = new Ojay.ContentOverlay({className: this.klass.VIEWER_CLASS});
        this._imageViewerOverlay.positionInFront(this._mask);
        this._pieceTitle = Ojay( Ojay.HTML.h3() );
        this._image = Ojay( Ojay.HTML.img() );
        this._imageViewerOverlay.insert(this._image);
        this._captionOverlay = new Ojay.ContentOverlay({className: this.klass.CAPTION_CLASS});
        this._captionOverlay.positionInFront(this._imageViewerOverlay);
        this._captionOverlay.getContentElement().setStyle({opacity: 0.8});
        
        this._captionToggle = Ojay( Ojay.HTML.div({className: this.klass.CAPTION_TOGGLE}, 'Show caption') );
        this._captionToggle.on('click')._(this)._toggleCaption();
        this._imageViewerOverlay.insert(this._captionToggle);
        
        this._closeButton = Ojay( Ojay.HTML.div({className: 'close-button'}, 'Close') );
        this._closeButton.on('click')._(this).destroy();
        this._imageViewerOverlay.insert(this._closeButton);
    },
    
    /**
     */
    destroy: function() {
        if (this._launcher) return this._launcher.changeState({gallery: null});
        else this._handleDestroy();
    },
    
    /**
     */
    _handleDestroy: function() {
        this._imageViewerOverlay.close();
        this._hideCaption();
        this._filmStripOverlay.close();
        this._mask.close('fade');
        this.setState('CLOSED');
    },
    
    /**
     * @param {String} theme
     */
    applyTheme: function(theme) {
        theme = this.klass.Themes[theme];
        if (!theme) return;
        this._mask.setColor(theme.mask);
        this._imageViewerOverlay.getContentElement().setStyle({background: theme.overlay});
        [this._captionToggle, this._closeButton].forEach({setStyle: {color: theme.text}});
        this._captionOverlay.getContentElement().setStyle({background: theme.caption, color: theme.captionText});
        if (this._currentTheme) this._currentTheme.element.removeClass('selected');
        this._currentTheme = theme;
        theme.element.addClass('selected');
    },
    
    states: {
        READY: {
            /**
             * @param {PhotoGallery.Item} item
             */
            showItem: function(item) {
                var self = this;
                this._currentItem = item;
                this._imageIndicator.setContent('Image ' + (this._images.indexOf(item) + 1) +
                        ' of ' + this._images.length);
                this._hideCaption();
                this.setState('CHANGING_ITEM');
                var chain = this._imageViewerOverlay.hide('fade')._(function() {
                    self._populateCaption();
                    self._captionToggle[item._caption ? 'show' : 'hide']();
                    self._pieceTitle.setContent(item._title);
                    self._image.set({src: item._fullSrc});
                    self._setViewerSize(item);
                    return this.wait(0.2).fitToContent().center();
                }).show('fade')._(this).setState('READY');
                
                if (this.klass.INITIAL_CAPTION !== false)
                	chain._(this)._showCaption();
            },
            
            /**
             */
            _toggleCaption: function() {
                this[this._captionVisible ? '_hideCaption' : '_showCaption']();
            },
            
            /**
             */
            _showCaption: function() {
                if (!this._currentItem._hasCaption()) return;
                var region = this._image.getRegion();
                if (YAHOO.env.ua.ie) region.shift(-2,-2);
                this._captionOverlay.getContentElement().animate({marginTop: {from: -40, to: 0}}, 0.3);
                
                this._captionToggle.replaceClass('caption-hidden', 'caption-visible').setContent('Hide caption');
                this._captionVisible = true;
                
                this._captionOverlay
                    .setSize(region.getWidth(), 150)
                    .setPosition(region.left, region.top)
                    .show('fade');
            },
            
            /**
             */
            _hideCaption: function() {
                this._captionOverlay.getContentElement().animate({marginTop: {to: -40}}, 0.3);
                
                this._captionToggle.replaceClass('caption-visible', 'caption-hidden').setContent('Show caption');
                this._captionVisible = false;
                
                this._captionOverlay.hide('fade');
            },
            
            /**
             */
            showStrips: function() {
            	if (this._images.length < 2) return;
            	if (this._stripsVisible) return;
                this._stripsVisible = true;
                var content = this._filmStripOverlay.getContentElement();
                content.animate({opacity: {to: 1}, top: {to: 0}}, 0.3, {easing: 'easeOutStrong'});
            },
            
            /**
             */
            hideStrips: function() {
                if (!this._stripsVisible) return;
                this._stripsVisible = false;
                var content = this._filmStripOverlay.getContentElement();
                content.animate({opacity: {to: 0}, top: {to: this._filmStripHeight}}, 0.3, {easing: 'easeIn'});
            }
        },
        
        CHANGING_ITEM: {
            /**
             * @param {PhotoGallery.Item} item
             */
            _setViewerSize: function(item) {
                var region = Ojay.getVisibleRegion().scale(0.9),
                    w = item._width, h = item._height,
                    aspect1 = region.getWidth() / region.getHeight(), aspect2 = w/h,
                    isWide = aspect2 > aspect1;
                
                var f = isWide ? (region.getWidth() / w) : (region.getHeight() / h);
                if (f > 1) f = 1;
                this._image.set({width: f*w, height: f*h});
                this._imageViewerOverlay.getContentElement().setStyle({width: (f*w) + 'px'});
            },
            
            /**
             */
            _populateCaption: function() {
                var overlay = this._captionOverlay, item = this._currentItem;
                overlay.setContent('');
                overlay.insert(this._pieceTitle).insert( Ojay.HTML.p(item._caption, function(HTML) {
                    HTML.br(); HTML.br();
                    HTML.span({className: 'copyright'}, item._copyright);
                }) );
                if (item._pictopiaURL) overlay.insert( Ojay.HTML.a({className: 'pictopia', href: item._pictopiaURL}, 'Buy this photo') );
            }
        },
        
        CLOSED: {}
    },
    
    extend: {
        FILMSTRIP_CLASS:    'filmstrip',
        FILMSTRIP_HEIGHT:   122,
        FILMSTRIP_PADDING:  32,
        MASK_OPACITY:       YAHOO.env.ua.ie ? 1.0 : 0.8,
        MOUSE_TIMEOUT:      1.5,
        VIEWER_CLASS:       'image-viewer',
        CAPTION_CLASS:      'caption',
        CAPTION_TOGGLE:     'caption-toggle',
        INDICATOR_CLASS:    'indicator',
        THEMES_CLASS:       'themes',
        INITIAL_CAPTION:	false,
        
        DEFAULT_THEME:		'black',
        
        /**
         * @constructor
         * @class PhotoGallery.Item
         */
        Item: new JS.Class({
            extend: {
                CONTAINER_CLASS:    'thumb'
            },
            
            /**
             * @param {Object} config
             */
            initialize: function(gallery, config) {
                this._gallery       = gallery;
                this._thumbSrc      = config.thumb || null;
                this._fullSrc       = config.fullsize || null;
                this._width         = config.fullWidth;
                this._height        = config.fullHeight;
                this._pictopiaURL   = config.pictopia || '';
                this._title         = config.name || '';
                this._caption       = config.caption || '';
                this._copyright     = config.copyright || '';
                this._elements      = {};
            },
            
            /**
             * @returns {Ojay.DomCollection}
             */
            getHTML: function() {
                var elements = this._elements, self = this;
                if (elements._container) return elements._container;
                elements._container = Ojay( Ojay.HTML.div({className: this.klass.CONTAINER_CLASS}, function(HTML) {
                    HTML.img({src: self._thumbSrc});
                }) );
                elements._container.on('click')._(this._gallery).showItem(this);
                return elements._container;
            },
            
            /**
             * @returns {Boolean}
             */
            _hasCaption: function() {
                return !!(this._caption || this._pictopiaURL);
            }
        }),
        
        /**
         * @constructor
         * @class PhotoGallery.Launcher
         */
        Launcher: new JS.Class({
            include: JS.State,
            
            initialize: function() {
                this._mask = new Ojay.PageMask({color: '#000000', opacity: 0.8});
                this.setState('CLOSED');
            },
            
            /**
             * @returns {Object}
             */
            getInitialState: function() {
                return {gallery: null};
            },
            
            /**
             * @param {Object} state
             */
            changeState: function(state) {
                if (state.gallery === null) this._handleClose();
                else this._handleOpen(state.gallery);
            },
            
            states: {
                CLOSED: {
                    /**
                     * @param {String} url
                     */
                    open: function(url) {
                        var uri = Ojay.URI.parse(url);
                        url = uri.path + '?' + uri.getQueryString();
                        this.changeState({ gallery: url });
                    },
                    
                    /**
                     * @param {String} url
                     */
                    _handleOpen: function(url) {
                        var data = null;
                        Ojay.HTTP.GET(url, {}, {
                            onSuccess: function(response) {
                                try { data = YAHOO.lang.JSON.parse(response.responseText.replace(/\s+/g, ' ')); }
                                catch (e) { alert('Badly formed JSON response') }
                                if (data) this._gallery = new PhotoGallery(data.images, this);
                            }.bind(this)
                        });
                        this.setState('OPEN');
                    }
                },
                
                OPEN: {
                    /**
                     */
                    close: function() {
                        this.changeState({ gallery: null });
                    },
                    
                    /**
                     */
                    _handleClose: function() {
                        this._gallery._handleDestroy();
                        this.setState('CLOSED');
                    }
            }   }   
        }),
        
        Themes: {
            black:  {
                identity:   '#000000',
                mask:       '#000000',
                overlay:    '#111111',
                caption:    '#111111',
                text:       '#ffffff',
                captionText:'#ffffff'
            },
            white: {
                identity:   '#ffffff',
                mask:       '#ffffff',
                overlay:    '#eeeeee',
                caption:    '#333333',
                text:       '#222222',
                captionText:'#ffffff'
            }
        }
    }
});

