document.observe('dom:loaded', function(){
    // Check if window.shoppingCart object exists, if not Create and set on global window object for access for any other javascript files.
    window.shoppingCart = new ShoppingCart();
    // We do show one of the shipping options selected by default, so for that we need to run setShippingOption method when page loads too
    window.shoppingCart.setShippingOption();

    $$('a.popup_link').each(function(e){
        Event.observe(e, 'mouseover', function() {
            Element.addClassName(e.select('div.hidePopup').first(), 'showPopup');
            Element.removeClassName(e.select('div.hidePopup').first(), 'hidePopup');
        });
        Event.observe(e, 'mouseout', function() {
            Element.addClassName(e.select('div.showPopup').first(), 'hidePopup');
            Element.removeClassName(e.select('div.showPopup').first(), 'showPopup');
        });
    })
});

/**
 *  ShoppingCart for web client. It works in sync with markup on page and server services. On init, find all UI shopping components and register them.
 *  1) Look for shopping cart icon (a microcart). A div with id 'microCart'. Add showMiniCart function to observe click event on direct child <a>. 
 *  2) Scan for html form with class 'addToCart'. A html Product/Qty form, make 'addToCart' function observe to form submit event. On Form submit Product is added to cart.
 *  3) MiniCart. Small, Editable cart view. Generally shown in popup.
 *  4) On addToCart action, ProductPurchase selections are posted. On complete an async server call is made to refresh cart data.
 *  5) On MicroCart button (icon) click, Show MiniCart div. 
 *  6) Remove button/links on UI should have css class 'removeCartItem'. On init, ShoppingCart object will scan page for all links with said class ($$('a.removeCartItem')) and add removeCartItem method to observe click event.     
 *  7) Update cart item quantity, Each shopping cart line item will be a form with qty txt input field. Assign "updateCartItemQty" class to all forms. On init ShoppingCart will scan for all form with said class ($$('form.updateCartItemQty')), and observe to submit event. 
 *      Fetch MiniCart from server and show.  is cached (not as important is first version) until page navigation happens or cart edit action is executed. 
 */

/********************************
ShoppingCart.
********************************/
var ShoppingCart = Class.create({

    initialize: function(){
        shoppingCartSection = $$('.shoppingCart').first();
        shippingMethodSection = $('shipMethod');
        // find <a> in microCart div and link showMiniCart function with it.
        if($('microCart') && $('microCart').down('a.viewCart') && $('microCart').down('a.viewCart').readAttribute('href') == "#") {
            $('microCart').down('a.viewCart').observe('click', this.showMiniCart);
        }
        // We might have Cart in screen loaded. Add event listeners to active elements.
        if (this.setupCartEventListeners()) {
            this.setupCartEventListeners().bind(this);
        }
        $$('form.addToCart').each(function(formElement){
            if(formElement.hasClassName('ajaxMe')){
                this.addToCart(formElement);
            }else{
                formElement.observe('submit',this.validateAddToCartForm.bind(this));
            }
        }, this);

        if ($$('[name=shipMethod]').size()) {
            $$('[name=shipMethod]').each(function(elt) {
                elt.observe('change', function() {
                    this.setShippingOption();
                });
            });
        }

        if ($('shipToPostalCode') && $('shipToPostalCode').hasClassName('updateShipping'))
            $('shipToPostalCode').observe('change',this.getShippingOption);

        if ($('cardSecurityCode')) {
            Event.observe($('billToCardSecurityCode'), "change", function(event) {
                new Ajax.Request('setCardSecurityCode', {asynchronous: false, parameters: {'cardSecurityCode': $F('billToCardSecurityCode'), 'paymentMethodId': $F('paymentMethodId')}});
            });
        }
    },

    validateAddToCartForm: function(event){
        var formElement = event.element();
        formValidator = new Validation(formElement, {immediate: true, onSubmit: true});
        if(!formValidator.validate()) {
            displayErrorMessage(formElement.down('.errorMsg'));
        }
    },
    
    setupCartEventListeners: function(){
        // console.log("setupCartEventListeners");
        if ($('AddCouponCodeToShoppingCart')) {
            var formElement = $('AddCouponCodeToShoppingCart');
            $('AddCouponCodeToShoppingCart').observe('submit', function(event) {
                event.stop();
                new Ajax.Request(formElement.action, {
                    parameters : formElement.serialize(),
                    onSuccess: function(transport) {
                        if(transport.responseText.isJSON()) {
                            var serverError = getServerError(transport.responseText);
                            // We need to identify whether the response from applying coupon code was a success or error.
                            // We need to set shipping methods only in case of success,
                            // so we will use the response type to decide between the two cases.
                            if(serverError) shoppingCartSection.down(".errorMessage").update(serverError);
                        } else {
                            var responseHTML = getHtmlFromString(transport.responseText);
                            if(!responseHTML.down(".shoppingCart")) {
                                shoppingCartSection.update(transport.responseText);
                            } else {
                                shoppingCartSection.update(responseHTML.down(".shoppingCart").innerHTML);
                                if(responseHTML.down("#shipMethod") && $('shipMethod'))
                                    shippingMethodSection.update(responseHTML.down("#shipMethod").innerHTML);
                            }
                            window.shoppingCart.setupCartEventListeners();
                            window.shoppingCart.setShippingOption();
                            window.shoppingCart.refreshMicroCart();
                        }
                    }
                });
            });
        }
        if ($('miniCart')) $('miniCart').down('a').observe('click', this.hideMiniCart);
        // locate forms to manage the data transport. 
        // ProductPruchaseOptions , ShippingCartItems (logical name for shoppingCartItems multi form. I guess we can use form widget for this as well.)
        // find all forms with class updateMiniCartItemQuantity, To handle quantity update from inline cart quantity field.
        $$('form.updateMiniCartItemQuantity').each(function(formElement){
            // get formElement child that has input name quantity.
            //console.log("adding event listener to  " + formElement.id);
            var quantity = formElement.down('input[name=quantity]');
            if (quantity) {
                // Add change event observer on each.
                //console.log("add change observer to " + quantity);
                quantity.observe('change',this.updateMiniCartItemQty.bind(this));
            }
        }, this);
        // Handle remove request from shopping cart items. One form per cartItem.
        $$('form.removeMiniCartItem').each(function(formElement){
            // Our remove cartItem form does have text element. Observing form submit instead.
            formElement.observe('submit',this.removeMiniCartItem.bind(this));
        }, this);

        $$('form.updateShoppingCartItemQuantity').each(function(formElement){
            // get formElement child that has input name quantity.
            //console.log("adding event listener to  " + formElement.id);
            var quantity = formElement.down('input[name=quantity]');
            if (quantity) {
                // Add change event observer on each.
                //console.log("add change observer to " + quantity);
                quantity.observe('change',this.updateShoppingCartItemQty.bind(this));
            }
        }, this);
        // Handle remove request from shopping cart items. One form per cartItem.
        $$('form.removeShoppingCartItem').each(function(formElement) {
            // Our remove cartItem form does have text element. Observing form submit instead.
            formElement.observe('submit',this.removeShoppingCartItem.bind(this));
        }, this);

        // #shipMethod is only updated on cart page and not on the checkout pages when updating cart,
        // so for cart page re-initiating obervers is necessary.
        // but on checkout page the number of ajax requests made onChange doubles everytime we choose an option,
        // which degrades performance, this should be fixed.
        // => we have improvements queued up to update shipping methods section on checkout page too when updating cart, that should fix it.

        if ($$('[name=shipMethod]').size()) {
            $$('[name=shipMethod]').each(function(elt) {
                elt.observe('change', function() {
                    this.setShippingOption();
                }.bind(this));
            }, this);
        }

        if ($('shipToPostalCode') && $('shipToPostalCode').hasClassName('updateShipping'))
            $('shipToPostalCode').observe('change',this.getShippingOption);
    },

    addToCart: function (formElement){
        var options = {
            formElt: formElement,
            toUpdate: 'header',
            displayErrorMethod: 'dialog',
            afterCallback: function(event, transport) {
                if(!transport) displayErrorMessage(formElement.down('.errorMsg'));
                if(transport && !transport.responseText.isJSON()) {
                    if($('miniLogin'))
                        initDialogObservers('header');

                    if($('microCart').down('a.viewCart').readAttribute('href') == "#")
                        $('microCart').down('a.viewCart').observe('click', this.showMiniCart);
                    this.refreshMicroCart(transport);
                    this.showMiniCart();
                    this.hideMiniCart.delay(1);

                    if(shoppingCartSection) {
                        var calcTaxParams = getCalcTaxParams();
                        new Ajax.Updater(shoppingCartSection, 'AjaxShoppingCart', {
                            asynchronous: false,
                            parameters : formElement.serialize() + '&' + calcTaxParams.toQueryString()
                        });
                    }
                    this.setupCartEventListeners();

                    displaySuccessMessage(formElement.down('.successMsg'));
                }
            }.bind(this)
        };
        ajaxifyForm(options);
    },

    removeMiniCartItem: function(event){
        // console.log(event.element());
        // We got submit event from remove item. 
        var formElement = event.element();
        // stop form submit because we'll submit using ajax.
        event.stop();
        // Use updater, refresh miniCart.
        new Ajax.Updater('header', formElement.action,{ parameters : formElement.serialize(), asynchronous: false,
            onComplete: function() {
                initValidations('header');
                initDialogObservers('header');
            }
        });
        // New html elements were loaded, so we need to add event listeners again.
        $('microCart').down('a.viewCart').observe('click', this.showMiniCart);
        this.setupCartEventListeners();
        this.showMiniCart();
    },

    removeShoppingCartItem: function(event){
        // console.log(event.element());
        // We got submit event from remove item. 
        var formElement = event.element();
        // stop form submit because we'll submit using ajax.
        event.stop();
        // Use updater, refresh miniCart.
        var calcTaxParams = getCalcTaxParams();
        new Ajax.Request(formElement.action, {
                asynchronous: false,
                parameters : formElement.serialize() + '&' + calcTaxParams.toQueryString(),
                onSuccess: function(transport) {
                    var responseHTML = getHtmlFromString(transport.responseText);
                    if(!responseHTML.down(".shoppingCart")) {
                        shoppingCartSection.update(transport.responseText);
                    } else {
                        shoppingCartSection.update(responseHTML.down(".shoppingCart").innerHTML);
                        if(responseHTML.down("#shipMethod") && $('shipMethod'))
                            shippingMethodSection.update(responseHTML.down("#shipMethod").innerHTML);
                    }
                    this.refreshMicroCart(transport);
                }.bind(this)
            }
        );
        // New html elements were loaded, so we need to add event listeners again.
        this.setupCartEventListeners();
        if(formElement.hasClassName('updateShipping')) {
            this.setShippingOption();
        }
    },

    updateShoppingCartItemQty: function(event){
        if(!Validation.validate(event.target)) return;
        var formElement = event.element().up('form');
        // Use updater, refresh miniCart.
        var calcTaxParams = getCalcTaxParams();
        new Ajax.Request(formElement.action, {
                asynchronous: false,
                parameters : formElement.serialize() + '&' + calcTaxParams.toQueryString(),
                onSuccess: function(transport) {
                    var responseHTML = getHtmlFromString(transport.responseText);
                    if(!responseHTML.down(".shoppingCart")) {
                        shoppingCartSection.update(transport.responseText);
                    } else {
                        shoppingCartSection.update(responseHTML.down(".shoppingCart").innerHTML);
                        if(responseHTML.down("#shipMethod") && $('shipMethod'))
                            shippingMethodSection.update(responseHTML.down("#shipMethod").innerHTML);
                    }
                    this.refreshMicroCart(transport);
                }.bind(this)
            }
        );
        // New html elements were loaded, so we need to add event listeners again.
        this.setupCartEventListeners();
        if(formElement.hasClassName('updateShipping')) {
            this.setShippingOption();
        }
    },

    updateMiniCartItemQty: function(event){
        if(!Validation.validate(event.target)) return;
        //console.log(event.element());
        // use event.element to walk upto the form element and submit it
        var formElement = event.element().up('form');
        // Use updater, refresh miniCart.
        new Ajax.Updater('miniCart', formElement.action,{ parameters : formElement.serialize(), asynchronous: false, onSuccess: this.refreshMicroCart.bind(this)});
        // New html elements were loaded, so we need to add event listeners again.
        this.setupCartEventListeners();
        // miniCartSummary form has data we use to show in micro cart
        // var totalQuantity = $('miniCartSummary').down('input[name=totalQuantity]').getValue();
        // this.refreshMicroCart(totalQuantity);
    },

    refreshMicroCart: function(transport){
        // on addToCart, call this function to update the microcart associated to ShoppingCart.
        // console.log(transport);
        // console.log(transport.getAllHeaders());
        shoppingCartSize = transport.getHeader("shoppingCartSize");
        shoppingCartTotal = transport.getHeader("shoppingCartTotal");
        if(shoppingCartSize > 1)
            $('microCartQuantity').update(shoppingCartSize + ' Items');
        else if(shoppingCartSize == 1)
            $('microCartQuantity').update(shoppingCartSize + ' Item');
        else
            window.location = "main";
        if($('microCartTotal'))
            $('microCartTotal').update(shoppingCartTotal);
    },

    showMiniCart: function(){
        //console.log("time to show minicart");
        if($('miniCart')) Effect.toggle('miniCart', 'appear');
    },

    hideMiniCart: function(){
        //console.log("time to hide minicart");
        if($('miniCart')) Effect.Fade('miniCart', {duration: 0.2, delay:2});
    },

    setShippingOption: function() {
        var calcTaxParams = getCalcTaxParams();
        if($('shipMethod'))
            if(Form.serializeElements($('shipMethod').descendants().concat($('shipMethod')))) {
                new Ajax.Updater(shoppingCartSection, $('shipMethod').title, {parameters : Form.serializeElements($('shipMethod').descendants().concat($('shipMethod'))) + '&' + calcTaxParams.toQueryString(), asynchronous: false, onSuccess: this.refreshMicroCart.bind(this)});
                this.setupCartEventListeners();
            }
    },

    getShippingOption: function() {
        if ($('processingShipOptions'))
            $('processingShipOptions').show();
        var calcTaxParams = getCalcTaxParams();
        new Ajax.Updater($('shipMethod'), 'ShippingMethods' , {asynchronous: false, parameters: calcTaxParams,
            onComplete: function(transport){
            if ($('processingShipOptions'))
                Effect.Fade('processingShipOptions');
        }});
    },

    calcAndAddTax: function() {
        var calcTaxParams = getCalcTaxParams();
        new Ajax.Updater('shoppingCart', 'calcAndAddTax', {asynchronous: false, parameters : calcTaxParams, onSuccess: this.refreshMicroCart.bind(this)});
        this.setupCartEventListeners();
    }
});

function getCalcTaxParams() {
    var calcTaxParams = $H();
    if($("shipToPostalCode") && $F("shipToPostalCode")) {
        var countryGeoId = ($("shipToCountryGeoId")) ? $F("shipToCountryGeoId") : null;
        var stateProvinceGeoId = ($("shipToStateProvinceGeoId")) ? $F("shipToStateProvinceGeoId") : null;
        calcTaxParams = $H({'postalCode': $F("shipToPostalCode"), 'countryGeoId': countryGeoId, 'stateProvinceGeoId': stateProvinceGeoId});
    } else if($("billToPostalCode") && $F("billToPostalCode") && $('doNotUseBillingAddressForShipping')) {
        var countryGeoId = ($("billToCountryGeoId")) ? $F("billToCountryGeoId") : null;
        var stateProvinceGeoId = ($("billToStateProvinceGeoId")) ? $F("billToStateProvinceGeoId") : null;
        calcTaxParams = $H({'postalCode': $F("billToPostalCode"), 'countryGeoId': countryGeoId, 'stateProvinceGeoId': stateProvinceGeoId});
    }
    return calcTaxParams;
}
