/**
 * Abstract ratings class is responsible for the AJAX requests, modals and alert
 * messages.
 *
 * @author Edd
 */
var Rating = Class.create({

    initialize: function(element, options)
    {
        this.options = {
            productId: null,
            ratingValue: -1,
            onSave: null,
            showNeverSendModal: false,
            sendSoonDisabledReason: null,
            sendSoonEnabled: false,
            inModal: false,
            globalSettings: {}
        };

        this.setOptions(options);

        /**
         * Extend the Rating class with any global settings that are present
         */
        Object.extend(Rating, this.options.globalSettings || {});

        /**
         * Some config
         */
        this.ratingDetails = {
            '0': {
                value: 0,
                helpText: 'never send me this',
                buttonElement: null
            },
            '-1': {
                value: -1,
                helpText: 'happy to try',
                buttonElement: null
            },
            '1': {
                value: 1,
                helpText: 'send occasionally',
                buttonElement: null
            },
            '2': {
                value: 2,
                helpText: 'send regularly',
                buttonElement: null
            }
        };

    	this.element = element;
        
        this.helpElement = this.element.down('.ratingHelp');
        this.sendSoonButton = this.element.down('.ratingSendSoon'); // May not be present

        this.addListeners();

        this.register();
    },

    register: function()
    {
        if (!Rating.globalStore[this.options.productId]) {
            Rating.globalStore[this.options.productId] = new Array();
        }
        Rating.globalStore[this.options.productId].push(this);
    },

    setOptions: function(options)
    {
        Object.extend(this.options, options || {});
    },

    addListeners: function()
    {
        var ratingButtons = this.element.select('.ratingButton');

        ratingButtons.each(function(ratingButton){
            var ratingValue = '' + ratingButton.getParam('rating');

            /**
             * Reference the rating details object to this element
             */
            this.ratingDetails[ratingValue].buttonElement = ratingButton;

            /**
             * Set the help text on hover
             */
            var helpText = this.ratingDetails[ratingValue].helpText;

            ratingButton.observe('mouseover', function(){
                this.setHelp(helpText);
            }.bind(this));

            ratingButton.observe('mouseout', function(){
                this.setHelp();
            }.bind(this));

            /**
             * Intercept clicks
             */
            ratingButton.observe('click', function(e){
                this.lastRatingButtonClicked = ratingButton;
                e.stop();
                this.setRating(ratingValue);
            }.bind(this));
        }.bind(this));

        /**
         * Add the 'send soon' button
         */
        if (this.sendSoonButton) {
            this.sendSoonButton.observe('click', function(e){
                e.stop();

                if (!this.isSendSoonEnabled()) {
                    this.showSendSoonDisabledReason();
                    return;
                }

                if (! this.isSendSoonActive() && Rating.sendSoonCanBeHidden) {
                    this.showIsHiddenMessage();
                }
                else if (Rating.showNutritionBoxMessage && ! Rating.sendSoonCanBeHidden) {
                    this.showNutritionBoxMessage();
                }
                else {
                    this.toggleSendSoon();
                }
            }.bind(this));

            this.sendSoonButton.observe('mouseover', function(e){
                this.showSendSoonWelcome();
            }.bind(this));

            this.sendSoonButton.observe('mouseout', function(e){
                this.hideSendSoonWelcome();
            }.bind(this));
        }
    },

    loading: function(show)
    {
        if (!this.loader) {
            var buttonsElement = this.element.down('.ratingButtons');
            this.loader = new Loader({ elementToOverlay: buttonsElement, zIndex: this.options.inModal ? 10000 : null });
        }
        
        if (show) {
            this.loader.show();
        }
        else {
            this.loader.hide();
        }
    },

    isLoading: function()
    {
        return this.loader && this.loader.visible();
    },

    setRating: function(ratingValue, overrideNeverSendModal, additionalRequestParams)
    {
        if (this.isLoading()) {
            return;
        }

        this.loading(true);

        Rating.closeOpenSpeechBubble();

        var params = {
            p: this.options.productId,
            r: ratingValue,
            currentIngredient: Rating.currentIngredient,
            overrideNeverSendModal: overrideNeverSendModal ? 1 : 0 // @move
        };
        Object.extend(params, additionalRequestParams || {});

        new AjaxRequest('/products/rate', {
            parameters: params,
            onSuccess: function(json) {
                if (json.neverSendModal) {
                    this.loading(false);
                    this.showNeverSendModal(json);
                    return;
                }

                this.loading(false);

                // @move
                if (ratingValue == 0) {
                    this.loading(false);
                }
                else {
                    this.loading(false, true);
                }

                this.setRatingSuccessHandler(json);

                if (json.modal) {
                    /**
                     * Open a modal with options direct from the JSON response
                     */
                    modal.open(json.modal);
                }
                else if (json.speechBubble) {
                    // set up default dialog options
                    var bubbleOptions = {
                        attachment: this.lastRatingButtonClicked,
                        attachmentOffset: {
                            top: 0,
                            left: 15
                        },
                        width: 180
                    };

                    // extend with JSON data
                    Object.extend(bubbleOptions, json.speechBubble);

                    // open the speech bubble
                    this.openSpeechBubble(bubbleOptions);
                }
            }.bind(this),
            maxActiveConnections: -1
        });
    },

    setRatingMultiple: function(ratingValue, productIds, onSuccess)
    {
        new AjaxRequest('/products/rate-multiple', {
            parameters: {
                'products[]': productIds,
                rating: ratingValue
            },
            onSuccess: function(json){
                for (var productId in json.products) {
                    var ratingObjs = Rating.getByProductId(productId);
                    ratingObjs.each(function(ratingObj){
                        ratingObj.setRatingSuccessHandler(json.products[productId]);
                    }.bind(this));

                    if (onSuccess) {
                        onSuccess(json);
                    }
                }
            }.bind(this)
        });
    },
    
    setRatingSuccessHandler: function(json)
    {
        /**
         * Find ALL rating products on the page with this product ID
         * and update their rating displays (include this one!)
         */
        var otherRatingObjs = Rating.getByProductId(this.options.productId);
        
        otherRatingObjs.each(function(ratingObj){
            ratingObj.setRatingDisplay(json.rating);
            ratingObj.options.ratingValue = json.rating;
            ratingObj.options.sendSoonEnabled = json.sendSoonEnabled;
            ratingObj.setSendSoonDisplay(json.sendSoonActive);
        });

        if (this.options.onSave) {
            this.options.onSave(this, json || {});
        }

        Rating.onRatingSave.each(function(ratingSaveHandler){
            ratingSaveHandler(this, json || {});
        }.bind(this));
    },

    setRatingDisplay: function(ratingValue)
    {
        for (var ratingVal in this.ratingDetails) {
            this.ratingDetails[ratingVal].buttonElement.down('span').removeClassName('active');
        }

        this.ratingDetails[ratingValue].buttonElement.down('span').addClassName('active');
    },

    setHelp: function(helpText)
    {
        if (!helpText) {
            helpText = '&nbsp;';
        }

        this.helpElement.update(helpText);
    },

    /**
     * The 'never send' modal provides an option to bin all products in the
     * current category (e.g. olives).
     *
     * This is not the same as the speech bubble that appears offering the
     * ability to bin all products with a certain ingredient.
     */
    showNeverSendModal: function(json)
    {
        new ModalObj({
            style: 'modalQuestion',
            width: 550,
            contents: json.neverSendModal,
            onOpen: function(thisModal){
                /**
                 * Listen for clicks to the cancel button (just closes
                 * the modal)
                 */
                thisModal.down('input.modalButton.secondary').observe('click', function(){
                    thisModal.close();
                });

                /**
                 * Listen for clicks to the confirm button - either just
                 * bins this product, or all in the category if the
                 * checkbox is ticked
                 */
                thisModal.down('input.modalButton').observe('click', function(){
                    var includeAll = thisModal.down('.includeAllInCategory input').checked;

                    thisModal.close(true);

                    if (includeAll) {
                        this.loading(true);
                        this.setRatingMultiple(0, json.products, function(){
                            this.loading(false);
                        }.bind(this));
                    }
                    else {
                        this.setRating(0, true);
                    }
                }.bind(this));
            }.bind(this),
            onClose: function(){
                this.loading(false);
            }.bind(this)
        });
    },


    showProductInfoModal: function()
    {
        if (! Product) {
            return;
        }
        
        this.closeSpeechBubble('none');

        Product.showDetailsModal(this.options.productId, {
            onClose: function(){
                Rating.closeOpenSpeechBubble('none');
            }
        });
    },
    
    toggleSendSoon: function(isHidden)
    {
        if (!this.sendSoonButton) {
            return;
        }

        if (!this.isSendSoonEnabled()) {
            return;
        }

        Rating.closeOpenSpeechBubble('fade');

        var loader = new Loader({elementToOverlay: this.sendSoonButton, zIndex: this.options.inModal ? 10000 : null });
        loader.show();

        new AjaxRequest('/products/send-soon', {
            parameters: {
                p: this.options.productId,
                active : ! this.isSendSoonActive(),
                isHidden : isHidden
            },
            onComplete: function(){
                loader.hide();
            }.bind(this),
            onSuccess: function(json) {
                /**
                 * Find all Rating objects with this product ID (including this one)
                 */
                var ratingObjs = Rating.getByProductId(this.options.productId);
                ratingObjs.each(function(ratingObj){
                    ratingObj.options.sendSoonEnabled = json.enabled;
                    ratingObj.setSendSoonDisplay(json.active);
                });
            }.bind(this),
            maxActiveConnections: -1
        });
    },

    setSendSoonDisplay: function(active)
    {
        if (!this.sendSoonButton) {
            return;
        }

        if (active) {
            this.sendSoonButton.down('span').addClassName('active');
        }
        else {
            this.sendSoonButton.down('span').removeClassName('active');
        }

        this.enableDisableSendSoon(this.options.ratingValue != 0 && (active || this.options.sendSoonEnabled));
    },

    enableDisableSendSoon: function(enable)
    {
        if (!this.sendSoonButton) {
            return;
        }

        if (enable) {
            this.sendSoonButton.down('span').removeClassName('disabled');
        }
        else {
            this.sendSoonButton.down('span').addClassName('disabled');
        }
    },

    isSendSoonEnabled: function()
    {
        if (!this.sendSoonButton) {
            return false;
        }

        return !this.sendSoonButton.down('span').hasClassName('disabled');
    },

    isSendSoonActive: function()
    {
        if (!this.sendSoonButton) {
            return false;
        }

        return this.sendSoonButton.down('span').hasClassName('active');
    },

    showSendSoonDisabledReason: function()
    {
        if (this.sendSoonDisabledReasonOpen) {
            return;
        }

        Rating.closeOpenSpeechBubble();

        var disabledReason;
        if (! this.options.sendSoonEnabled) {
            disabledReason = '<strong>send soon</strong> is not currently available for this product';
        }
        else {
            disabledReason = 'please rate me as <strong>try, like or love</strong> to use send soon';
        }

        this.closeSpeechBubble();

        this.openSpeechBubble({
            width: 170,
            tipPosition: 140,
            attachment: this.sendSoonButton,
            attachmentOffset: { top: 0, left: 12 },
            contents: disabledReason
        }, 3);

        this.sendSoonDisabledReasonOpen = true;
    },

    showSendSoonWelcome: function()
    {
        if (!this.isSendSoonEnabled()) {
            return;
        }

        var message = "<h1><span>NEW</span> send soon</h1><div>tick here and we'll do our best to send you this product in your next box - once it's sent, we'll untick the box for you</div>";

        var openSpeechBubble = function(){
            this.openSpeechBubble({
                width: 200,
                tipPosition: 170,
                attachment: this.sendSoonButton,
                attachmentOffset: { top: 0, left: 12 },
                contents: message
            });
        }.bind(this);

        this.sendSoonWelcomeTimer = openSpeechBubble.delay(1);
    },

    hideSendSoonWelcome: function()
    {
        if (this.sendSoonWelcomeTimer) {
            window.clearTimeout(this.sendSoonWelcomeTimer);
            this.sendSoonWelcomeTimer = null;
        }
        this.closeSpeechBubble('fade')
    },

    openSpeechBubble: function(options, autoCloseSeconds)
    {
        var sbOptions = {
            inModal: this.options.inModal
        };
        Object.extend(sbOptions, options || {});

        Rating.closeOpenSpeechBubble();

        this.speechBubble = new SpeechBubble(sbOptions);
        Rating.hasOpenSpeechBubble = this;

        if (autoCloseSeconds) {
            var closeSpeechBubble = function(){
                this.closeSpeechBubble('fade');
            }.bind(this);
            this.closeSpeechBubbleTimer = closeSpeechBubble.delay(autoCloseSeconds);
        }
    },

    closeSpeechBubble: function(closeEffect)
    {
        this.sendSoonDisabledReasonOpen = false;

        if (this.closeSpeechBubbleTimer) {
            window.clearTimeout(this.closeSpeechBubbleTimer);
            this.closeSpeechBubbleTimer = null;
        }

        if (this.speechBubble) {
            this.speechBubble.close(closeEffect || null);
        }
    },

    showNutritionBoxMessage: function()
    {
        modal.question(
            "Just so you know, any boxes with 'send soon' products may not fall into your chosen nutritional plan", [
            {
                text: "That's fine",
                clickHandler: function(){
                    Rating.showNutritionBoxMessage = false;
                    new AjaxRequest('/help/ajaxclosemessage', {
                        parameters: {
                            name: 'sendSoonNutritionBox'
                        }
                    });
                },
                style: 'modalButton'
            },
            {
                text : 'Oops, stick to my nutrition plan',
                style: 'modalButton secondary'
            }
        ]);
    },

    showIsHiddenMessage: function()
    {
        modal.question(
            "Do you want this 'send soon' to be hidden or visible to the customer?", [
            {
                text: 'Hidden',
                clickHandler: function(){
                    this.toggleSendSoon(true);
                }.bind(this)
            },
            {
                text: 'Visible',
                clickHandler: function(){
                    this.toggleSendSoon();
                }.bind(this)
            }
        ]);
    }
    
});

/**
 * Static properties on Rating to some global settings.
 */
Rating.sendSoonCanBeHidden = false;
Rating.showNutritionBoxMessage = false;

/**
 * Global store of all active Rating objects
 */
Rating.globalStore = [];

/**
 * Fetches a list of Rating objs for a given product ID
 */
Rating.getByProductId = function(productId) {
    if (Rating.globalStore[productId]) {
        return Rating.globalStore[productId];
    }
    return new Array();
};

/**
 * Holds the Rating.Product object which has an open speech bubble, or null if
 * none
 */
Rating.hasOpenSpeechBubble = null;

Rating.closeOpenSpeechBubble = function(closeEffect)
{
    if (Rating.hasOpenSpeechBubble) {
        Rating.hasOpenSpeechBubble.closeSpeechBubble(closeEffect || null);
    }

    Rating.hasOpenSpeechBubble = null;
};

/**
 * Static variable to store a general method to be
 * called by any Rating.Product object when a rating is saved
 * with 2 arguments: first containing the Rating object that
 * called it, and second containing the JSON object returned
 * from the save AJAX request
 */
Rating.onRatingSave = [];

/**
 * Stores the current ingredient to prevent odd messages from appearing when
 * binning something from an ingredient page
 */
Rating.currentIngredient = null;

/**
 * Finds all current Rating object instances and bins them all
 */
Rating.binAllOnPage = function(loaderElementToOverlay)
{
    modal.confirm('are you sure you want to bin all the products on this page?', function(){
        if (loaderElementToOverlay) {
            var loader = new Loader({elementToOverlay:loaderElementToOverlay});
            loader.show();
        }

        var prids = [];
        var lastRatingObj = null;
        for (var prid in Rating.globalStore) {
            var ratingObjs = Rating.globalStore[prid];

            if (!Object.isArray(ratingObjs)) {
                continue;
            }

            ratingObjs.each(function(ratingObj){
                lastRatingObj = ratingObj;
            });

            prids.push(prid);
        }

        lastRatingObj.setRatingMultiple(0, prids, function(){
            if (loader) {
                loader.hide();
            }
        });
    });
};

/**
 * Rating Product class is the Javascript for the whole product image & ratings
 * object.
 *
 * @author Edd
 */
Rating.Product = Class.create(Rating, {

    initialize: function($super, element, options)
    {
        $super(element, options);
        
		/**
         * Find inner elements
         */
		this.productName = this.element.down('.ratingProductName');
		this.productImageContainer = this.element.down('.ratingProductImage');
		this.disabler = this.productImageContainer.down('.ratingProductDisabler'); // May not be present
        this.textOverlay = this.element.down('.ratingProductTextOverlay'); // May not be present
        this.loader = this.element.down('.ratingProductLoader');

        /**
         * Listen for clicks to load the details modal
         */
        var handleClick = function(e){
            e.stop();
            (function(){this.showProductInfoModal()}.bind(this)).defer();
        }.bindAsEventListener(this);

        this.productName.observe('click', handleClick);
        this.productImageContainer.observe('click', handleClick);
        if (this.textOverlay) {
            this.textOverlay.observe('click', handleClick);
        }
        
        /**
         * Mouse over events on the whole rating product
         */
        this.productImageContainer.observe('mouseover', function(){
            this.element.addClassName('ratingProductHover');
        }.bind(this));
        this.productImageContainer.observe('mouseout', function(){
            this.element.removeClassName('ratingProductHover');
        }.bind(this));
    },

    loading: function(show, hideWithDone)
    {
        if (show) {
            this.loader.show();
        }
        else {
            if (hideWithDone) {
                this.loader.addClassName('done');

                var fadeLoader = function(){
                    new Effect.Fade(this.loader, {
                        duration: 0.2,
                        afterFinish: function(){
                            this.loader.removeClassName('done');
                        }.bind(this)
                    });
                }.bind(this);

                fadeLoader.delay(0.6);
            }
            else {
                this.loader.hide();
            }
        }
    },

    isLoading: function()
    {
        return this.loader.visible();
    },

    setRatingDisplay: function($super, ratingValue)
    {
        $super(ratingValue);

        if (ratingValue == '0') {
            /**
             * We have a bin, so display the 'disabler'
             */
            this.disabler.show();
            
            if (this.textOverlay) {
                this.textOverlay.hide();
            }
        }
        else {
            this.disabler.hide();

            if (this.textOverlay) {
                this.textOverlay.show();
            }
        }
    }    
});

/**
 * Large rating object
 */
Rating.ProductLarge = Class.create(Rating, {

    initialize: function($super, element, options)
    {
        $super(element, options);

        this.productImage = this.element.down('.productLargeImage');

        if (this.productImage) {
            this.productImage.observe('click', function(e){
                e.stop();
                this.showProductInfoModal();
            }.bind(this));
        }

        this.binnedOverlay = this.element.down('.productLargeImageBinned');

        if (Utils.isIE6()) {
            document.observe('dom:loaded', function(){
                this.resizeBinnedOverlay();
            }.bind(this));
        }
    },

    setRatingDisplay: function($super, ratingValue)
    {
        $super(ratingValue);

        if (this.binnedOverlay) {
            if (ratingValue == '0') {
                this.binnedOverlay.show();
            }
            else {
                this.binnedOverlay.hide();
            }
        }
    },

    resizeBinnedOverlay: function()
    {
        if (! this.binnedOverlay || ! this.productImage) {
            return;
        }

        this.binnedOverlay.setStyle({height: this.productImage.getHeight() + 'px'});
    }

});
