/**
 * imageViewer.js
 * @author Brian Crescimanno <brian.crescimanno@autotrader.com>
 *
 * Turns an element into a styled image viewer.  Images can be passed to the viewer to be loaded in a stylized
 * (animated) fashion.
 *
 * The following effects are supported:
 * snapFade (default):  The current image immediately vanishes and the new image fades in
 * fadeInOut:           The current image fades out completely, the new image fades in
 * snap:                The current image immediately vanishes and the new image immediately appears
 * blend:               The current image fades as the new image fades in simultaneously "blending" the images
 * random:              Chooses a random effect above.
 *
 * @requires Prototype >= 1.6.0, Script.aculo.us >= 1.8.0
 */

if(typeof Prototype == undefined) throw("ImageViewer requires the Prototype library");
if(typeof Effect == undefined) throw("ImageViewer requires the Scriptaculous library");

var ImageViewer = Class.create({
    initialize: function(id, options){
        this.viewport = $(id);
        if(this.viewport == undefined) throw("Could not find element: " + id);
        this.viewportHeight = this.viewport.clientHeight;
        this.viewportWidth = this.viewport.clientWidth;

        this._extendOptions(options);

        if(this.options.allowZoom && $(id+'zoom')){
            this._setupZoom(id);
        } else {
            this.options.allowZoom = false;
        }
        if($(id+'videoPlay')){
            this._setupVideo(id);
        }
        if($(id+'photoPlay')){
            this._setupPhotos(id);
        }
        this._createRandomEffectsArray();
    },

    preloadImage: function(url){
        var img = new Image();
        img.src = url;
        this.preload = Element.extend(img);
    },

    show: function(url, caption, effect, prepared){
        this.viewport.previous().removeClassName("videoactive");
        if(effect == undefined) effect = this.options.effect;
        if(!prepared){
            url = this._fixUrl(url);
            this._prepareImage(url);
        }
        if(this.imageLoaded){
            var title = this._handleTitle(caption);
            if(title){
                this._linkWrap(title);
            } else {
                var showCaption = this._showCaption.bind(this, caption);
            }
            this._fitToViewer();
            switch(effect){
                case "snapFade":
                    this._snapFade(this.currentImage, showCaption);
                    break;
                case "fadeInOut":
                    this._fadeInOut(this.currentImage, showCaption);
                    break;
                case "snap":
                    this._snap(this.currentImage, showCaption);
                    break;
                case "random":
                    var randno = Math.floor(Math.random()*this.effects.length);
                    this.show(url, caption,  this.effects[randno], true);
                    break;
            }
            this._zoomButtonStartState();
        } else {
            setTimeout(function(){ this.show(url, caption, effect, true);}.bind(this), 250);
        }

    },

    showVideo: function(el){
        if(Object.isUndefined(this.vp)){
            this.vpContainer = new Element("div", {id: "videoWonderland"});
            this.vp = new VideoPlayer("videoWonderland", this.options.path, this.viewportWidth, this.viewportHeight);
        }
        this.viewport.previous().addClassName("videoactive");
        var videoOptions = el.className.split(" ");
        this._loadVideo(videoOptions);
        this.zoomable = false;
        this._zoomButtonStartState();
    },

    clickToZoom: function(e){
        var el = e.element(); /* the image that was clicked */
        this.viewport.fire("viewport:zooming");

        /* Begin loading the large image first */
        this._prepareImage(this._fixUrlFullSize(el.src));

        /* Now just hold off until the large image is loaded; there's nothing we can do until then */
        if(this.imageLoaded){
            var offsets = this.viewport.cumulativeOffset();
            var clickCoords = new Array();
            clickCoords[0] = Math.floor(e.pointerX() - offsets.left);
            clickCoords[1] = Math.floor(e.pointerY() - offsets.top);

            var newOffsets = this._zoomCoordinates(clickCoords, el);

            el.setStyle({position: "relative"});

            this._performZoom(el, newOffsets);
            if(this.options.allowZoom){
                this.zoomButton.hide();
                this.zoomOutButton.show();
            }
        } else {
            setTimeout(function(){ this.clickToZoom(e);}.bind(this), 250);
        }

    },

    zoom: function(e){
        var el = this.viewport.down('img');
        var largeUrl = this._fixUrlFullSize(el.src);

        this.viewport.fire("viewport:zooming");

        this._prepareImage(largeUrl);

        if(this.imageLoaded){

            var center = new Array();
            center[0] = Math.floor(el.getWidth() / 2);
            center[1]  = Math.floor(el.getHeight() / 2);

            var offsets = this._zoomCoordinates(center, el);

            el.setStyle({position: "relative"});
            this._performZoom(el, offsets);
            this.zoomButton.hide();
            this.zoomOutButton.show();

        } else {
            setTimeout(function(){this.zoom();}.bind(this), 250);
        }

    },

    zoomOut: function(){
        this.viewport.fire("viewport:zoomout");
        this.zoomButton.show();
        this.zoomOutButton.hide();
    },

    /******* PRIVATE METHODS *******/

    _addImageBorder: function(img){
         if(img.height < (this.viewportHeight-4)){
            var heightDiff = Math.floor((this.viewportHeight - img.height) / 2);
            img.setStyle({marginTop: heightDiff+"px", borderTop: "1px solid #fff", borderBottom: "1px solid #fff"});
        }

        if(img.width < (this.viewportWidth-4)){
            var widthDiff = Math.floor((this.viewportWidth - img.width) / 2);
            img.setStyle({marginLeft: widthDiff+"px", borderLeft: "1px solid #fff", borderRight: "1px solid #fff"});
        }
        return img;
    },

    _checkPreloadedImage: function(url){
        var i = null;
        if((this.preload) != null){
            if(this.preload.src == url){
                this.loadingImageUrl = url;
                i = this.preload;
            }
        }
        this.preload = null;
        return i;
    },

    _checkZoomable: function(img){
        if(!this.options.allowZoom){
            this.zoomable = false;
            return false;
        }
        if(img.src.include('/scaler/')){
            /* If both dimensions are smaller, then we don't have a zoom state */
            this.zoomable = !((img.width < (this.viewportWidth - 6)) && (img.height < (this.viewportHeight - 6)));
        } else {
            /* We aren't using the scaler, so we won't zoom */
            this.zoomable = false;
        }
    },

    _createRandomEffectsArray: function(){
        this.effects = $A(["snapFade", "fadeInOut", "snap", "blend"]);
    },

    _extendOptions: function(options){
        options.allowZoom = options.allowZoom != "false";
        this.options = Object.extend({
            fps: 50,
            speed: 0.5,
            effect: "snapFade",
            path: "/",
            fireEvents: false,
            scale: true,
            allowZoom: true
        }, options || {});
    },

    _fitToViewer: function(){
        if(this.currentImage.width > this.viewportWidth)
            this.currentImage.setStyle({width: this.viewportWidth+"px"});
        if(this.currentImage.height > this.viewportHeight)
            this.currentImage.setStyle({height: this.viewportHeight+"px"});
    },

    _fixUrl: function(url){
        if(!this.options.scale){
            return url;
        }
        var newUrl = "/scaler/" + this.viewportWidth + "/" + this.viewportHeight;
        if(url.indexOf("http://") > -1){
            url = url.substr(7).split("/");
            if(url[1] != "images") newUrl = "";
            for(var i=1; i<url.length; i++){
                newUrl += "/" + url[i];
            }
        } else {
            newUrl += url;
        }
        return newUrl;
    },

    _fixUrlFullSize: function(url){
        var newUrl = "";
        if(url.indexOf("http://" > -1)){
            url = url.substr(7).split("/").reverse();
            url.pop();
            url.reverse();
        }

        for(var i=3; i<url.length; i++){
            newUrl += "/" + url[i];
        }

        return newUrl;
    },

    _handleTitle: function(text){
        if(text == undefined || text == "") return false;
        text = text.gsub("'", "\"");
        if(!text.isJSON()) return false;
        return text.evalJSON();
    },

    _linkWrap: function(linkObj){
        switch(linkObj.linkType){
            case "profile":
                this._linkWrapBuild("/community/profile/viewProfile.xhtml?user=" + linkObj.linkId);
                break;
            case "article":
                this._linkWrapBuild("/articles/templates/generalArticle.xhtml?articleId=" + linkObj.linkId +"&entityId=" + linkObj.linkId);
                break;
            case "cars":
                this._linkWrapBuild("/find/vehicle/vehicleDetail.xhtml?adId=" + linkObj.linkId);
                break;
            case "rides":
                this._linkWrapBuild("/community/vehicle/vehicleDetail.xhtml?adId=" + linkObj.linkId +"&entityId=" + linkObj.linkId);
                break;
            case "event":
                this._linkWrapBuild("/events/eventDetail.xhtml?eventId=" + linkObj.linkId +"&entityId=" + linkObj.linkId + "&currentUserEvent=false");
                break;
            case "dealer":
                this._linkWrapBuild("/find/commercialseller/commercialSellerDetail.xhtml?ownerId=" + linkObj.linkId);
                break;
            case "parts":
                this._linkWrapBuild("/find/part/partDetail.xhtml?partId=" + linkObj.linkId);
                break;
            case "other":
                this._linkWrapBuild(linkObj.linkId);
                break;
        }
    },

    _linkWrapBuild: function(url){
        this.currentImage = new Element("a", {href: url}).update(this.currentImage);
    },

    _loadVideo: function(videoOptions){
        this.viewport.update(this.vpContainer);
        if(videoOptions[1] == "youtube"){
            this.vp.renderYoutube(videoOptions[2].toString());
        } else if(videoOptions[1] == "brightcove") {
            this.vp.renderBrightcove(videoOptions[2].toString());
        }
    },

    _performZoom: function(el, newOffsets){
        new Effect.Morph(el, {
                    style: " left:" + newOffsets[0] +"px; top: " + newOffsets[1] + "px; height: " + this.currentImage.height + "px; width: " + this.currentImage.width +"px; margin: 0px",
                    duration: 0.7,
                    fps: 75,
                    afterFinish: function(){
                        this.currentImage.setStyle({margin: 0, position: "relative", left: newOffsets[0] + "px", top: newOffsets[1] + "px", cursor: "move"});
                        this._snap(this.currentImage);
                        this._showCaption("Click &amp Drag to move the photo.  Click \"Zoom Out\" to return to original size.");
                        var minLeft = this.viewportWidth - this.currentImage.getWidth();
                        var minTop = this.viewportHeight - this.currentImage.getHeight();
                        var testDrag = new AtxDraggable(this.currentImage, {
                            atcMinLeft: minLeft,
                            atcMinTop: minTop,
                            starteffect: function(){},
                            endeffect: function(){}
                        });
                    }.bind(this)
                });
    },

    _prepareImage: function(url, img){
        if(img == undefined){
            this.imageLoaded = false; /* Only set on OUTSIDE calls */
            img = this._checkPreloadedImage(url);
            if(img == null){
                this.loadingImageUrl = url;
                img = new Image();
                img.src = url;
                img = Element.extend(img);
            }
        }

        if(img.complete){
            clearTimeout(this._imageLoadT);
            img = this._addImageBorder(img);
            this._checkZoomable(img);
            this.currentImage = Element.extend(img);
            this.imageLoaded = true;
            if(this.zoomable){
                this.currentImage.observe("click", this.clickToZoom.bindAsEventListener(this));
            }
        } else {
            this._imageLoadT = setTimeout(function(){ this._prepareImage(url, img);}.bind(this), 250);
        }
    },

    _setupPhotos: function(id){
        this.photoPlayButton = $(id+'photoPlay');
        this.playBtnContainer = this.photoPlayButton.up().up();
        var photoClickHandler = function(){
            if(this.options.fireEvents){
                this.viewport.fire("viewport:showphotos");
            } else {
                this.show("", "", "snapFade", true);
            }
            this.photoPlayButton.removeClassName("inactiveIvTab");
            if (this.videoPlayButton) {
                this.videoPlayButton.addClassName("inactiveIvTab");
                this.playBtnContainer.removeClassName("videoactive");
            }
        }.bind(this);
        this.photoPlayButton.observe("click", photoClickHandler);
    },

    _setupVideo: function(id){
        this.videoPlayButton = $(id+'videoPlay');
        this.playBtnContainer = this.videoPlayButton.up().up();
        var videoClickHandler = function(e){
            if(this.options.fireEvents){
                this.viewport.fire("viewport:showvideos");
            } else {
                this.showVideo(e.element());
            }
            this.photoPlayButton.addClassName("inactiveIvTab");
            this.videoPlayButton.removeClassName("inactiveIvTab");
            if (this.playBtnContainer.hasClassName("photosandvideos")) {this.playBtnContainer.addClassName("videoactive");}
        }.bind(this);
        this.videoPlayButton.observe("click", videoClickHandler);
    },

    _setupZoom: function(id){
        this.zoomButton = $(id+'zoom');
        this.zoomOutButton = $(id+'zoomout');
        this.zoomButton.observe("click", this.zoom.bindAsEventListener(this));
        this.zoomOutButton.observe("click", this.zoomOut.bindAsEventListener(this));
    },

    _showCaption: function(text){
        if(text == undefined || text == "") return false;
        var caption = new Element("div", {'class': 'carousel-caption', style: "margin-top: " + (this.viewportHeight-25) + "px"}).update(text).setOpacity(0);
        this.viewport.insert({top: caption});
        new Effect.Opacity(caption, {from: 0.0, to: 0.75, duration: 0.25, fps: 100});
    },

    _zoomButtonStartState: function(){
        if(this.options.allowZoom){
            if(!this.zoomable){
                this.zoomButton.hide();
                this.zoomOutButton.hide();
                return false;
            } else {
                this.zoomButton.show();
                this.zoomOutButton.hide();
            }
        }
    },

    _zoomCoordinates: function(coords, el){

        var movingTo = new Array();
        movingTo[0] = (coords[0] / el.getWidth()) * this.currentImage.width;
        movingTo[1] = (coords[1] / el.getHeight()) * this.currentImage.height;

        var vpMid = new Array();
        vpMid[0] = Math.floor(this.viewportWidth / 2);
        vpMid[1] = Math.floor(this.viewportHeight / 2);

        var newOffsets = new Array();
        newOffsets[0] = Math.floor(vpMid[0] - movingTo[0]);
        newOffsets[1] = Math.floor(vpMid[1] - movingTo[1]);

        var minimums = new Array();
        minimums[0] = this.viewportWidth - this.currentImage.width;
        minimums[1] = this.viewportHeight - this.currentImage.height;

        if(newOffsets[0] < minimums[0])
            newOffsets[0] = minimums[0];
        if(newOffsets[0] > 0)
            newOffsets[0] = 0;
        if(newOffsets[1] < minimums[1])
            newOffsets[1] = minimums[1];
        if(newOffsets[1] > 0)
            newOffsets[1] = 0;

        return newOffsets;

    },

    /* ---------- Begin Effect Methods ------------------------------------------------------------------------------ */

    _blend: function(img, showCaption){
        //Blend not implemented.
    },

    _fadeInOut: function(img, showCaption){
        if(showCaption == undefined) showCaption = function(){};
        var oldImg = this.viewport.down();
        if(typeof oldImg  == 'undefined'){
            this._snapFade(img);
            return;
        }
        img.hide();
        new Effect.Fade(oldImg, {
            from: 1.0,
            to: 0.0,
            duration: this.options.speed,
            fps: this.options.fps,
            afterFinish: function(){
                oldImg.remove();
                this.viewport.update(img);
                new Effect.Appear(img, {
                    from: 0.0,
                    to: 1.0,
                    duration: this.options.speed,
                    fps: this.options.fps,
                    afterFinish: showCaption
                });
            }.bind(this)
        });
    },

    _snap: function(img, showCaption){
        if(showCaption == undefined) showCaption = function(){};
        this.viewport.update(img);
        showCaption();
    },

    _snapFade: function(img, showCaption){
        if(showCaption == undefined) showCaption = function(){};
        img.hide();
        this.viewport.update(img);
        new Effect.Appear(img, {
            from: 0.0, to: 1.0,
            duration: this.options.speed,
            fps: this.options.fps,
            afterFinish: showCaption
        });
    }

});