Products = Class.create();

Products.Details = Class.create();
/**
 * Represents a Product Details pop-up
 */
Products.Details.prototype = {
    cart : null,
    
    productId : null,
    
    /**
    * Creates a new instance of the Product Details pop-up
    *
    * @param HTMLElement contentId the DOM ID of the pop-up container element
    */
    initialize : function(contentId) {
        this.contentId = contentId;        
        
        new DeepFreeze(this, ['addToCart']);        
    },
    
    /**
    * Returns the shopping cart object
    *
    * @return Object
    */
    getCart : function() {
        var cartElement = $('mini-cart');
        
        if (cartElement) {
            this.cart = cartElement.cart;
        }        
        
        return this.cart;
    },
    
    /**
    * Displays the product details pop-up in the current screen
    *
    * @param Integer productId              the product ID to display
    * @param Boolean showAvailability       whether or not to display product availability information
    * @param String alternativeProductsUrl  the URL for the alternative product listing
    */
    show : function(productId, showAvailability, alternativeProductsUrl) {
        /* moves the detail container outside the listing     */
        var container = $(this.contentId);
        if (container.parentNode.tagName.toLowerCase() != 'body') {
            document.getElementsByTagName('body')[0].appendChild(container);
        }
        
        if(!showAvailability || showAvailability == 'undefined'){
            showAvailability = '';
        }else{
            showAvailability = '/' + showAvailability;
        }
        if(!alternativeProductsUrl || alternativeProductsUrl == 'undefined'){
            alternativeProductsUrl = '';
        }else{
            alternativeProductsUrl = '/' + alternativeProductsUrl;
        }
        var requestParameters = {
            alternativeProductsUrl: alternativeProductsUrl             
        };
        
        if (this.featuredProductId) {
            requestParameters.featuredProductId = this.featuredProductId;
        }        
         
        new Ajax.Updater(
            this.contentId + '-content', 
            Paths.www + '/uni' + '_products/showDetails/' + productId + '/' + this.contentId + showAvailability,
            {
                parameters: requestParameters,
                onSuccess : function() {                                                        
                    $(this.contentId).details = this;
                    $(this.contentId).unipopup.show();   
                    this.productId = productId;     
                }.bindAsEventListener(this),
                evalScripts: true
            }
        );
    },
    
    /**
    * Hides this product details pop-up
    */
    hide : function() {
        $(this.contentId).unipopup.hide();   
    },
    
    
    /**
    * Adds the selected product to the mini-cart, if available
    */
    addToCart : function() {
        var productId = $(this.contentId + '-product-id').value;
        var products = {};
        products[productId] = $(this.contentId + '-quantity').value;
                
        this.getCart().observeOnce(
            MyUnisource.MiniCart.Event.PRODUCTS_SUBMITTED,        
            function() {
                this.hide();
                this.unfreeze();
            }.bind(this)
        );
        
        this.cart.submitProducts(products);
        
    }
}

Products.Listing = Class.create();


Products.Listing.quantities = {};
Products.Listing.listings = [];

Products.Listing.Event = {
    /**
    * Invoked when the product listing is about to be 
    * submitted in order to add products to the current
    * order
    */
    SUBMITTING : 'submitting', 
    
    /**
    * Invoked after the products have been added to the 
    * order.
    */
    SUBMITTED : 'submitted'
}

/**
 * Clears all retained quantities
 */
Products.Listing.clearAll = function() {
    Products.Listing.quantities = {};
}

/**
 * Retains quantities in all product listings that are still
 * on the page at the moment this function
 * is called.
 */
Products.Listing.retainAll = function() {
	Products.Listing.forAll(
		function(listing) {
			listing.retain();
		}
	);
}


/**
* Removes listings that are no longer part of the DOM
*/
Products.Listing.clearZombies = function()
{
    if (Products.Listing.listings)
    {
        var clearedListings =
            $A(Products.Listing.listings).findAll(
                function(listingId) {
                    var listingElement = $(listingId);
                    return listingElement && listingElement.listing && listingElement.listing.getForm();
                }
            );
            
        Products.Listing.listings = clearedListings;
    }
}

/**
 * Restores quantities in all product listings that are still
 * on the page.
 */
Products.Listing.restoreAll = function() {
    Products.Listing.clearZombies();
	Products.Listing.forAll(
		function(listing) {
			listing.restore();			
		}                  
	);                     
}

Products.Listing.forgetAll = function(productIds) {
    Products.Listing.forAll (
		function(listing) {
			listing.forgetQuantities(productIds);
		}
	);
}

Products.Listing.forAll = function(lambda) {
	if (Products.Listing.listings) {
		$A(Products.Listing.listings).each(	
			function(listingId){
				var listingElement = $(listingId);
				if (listingElement 
					&& listingElement.listing
					&& listingElement.listing instanceof Products.Listing) {
					lambda(listingElement.listing);
				}			
			}
		);
	}
}

/**
 * Function called whenever a listing is loaded.
 *
 * Overwrite with your favorite JS code.
 * e.g.
 * Products.Listing.onLoad = function (listing) {
 *      alert('Listing contains ' + listing.productIds + ' product(s).');
 * }
 *
 * @access public
 * @param Listing listing The loaded product listing
 * @return boolean False
 */
Products.Listing.onLoad = function (loaded) {
    /* overwrite */
    return false;   
}

/**
 * A Listing implementation for the Unisource product listing
 * that provides value retention for pagination
 */        
Products.Listing.prototype = Object.extend(new Listing(), 
{    

    /**
     * Retained product quantities for listings with an "Add to Cart" option
     */
    quantities : {},
    
    
    /**
    * The ID of the root element for the mini cart, to
    * which the actual cart object is attached
    *
    * @var string
    */
    miniCartId : 'mini-cart',
    
    
    /**
    * Indicates whether or not this listing is "frozen", in 
    * which case 
    *
    * @var bool
    */
    frozen: false,
    
    /**
     * The main constructor
     *
     * @param id                the listing ID
     * @param productIds        the products contained in this product listing
     * @param checkboxEnabled   indicates whether checkboxes should be enabled
     * @param enditableQuantity indicates whether or not the quantities are editable
     */
    initialize : function(id, productIds, checkboxEnabled, editableQuantity) {
        this.id = id;
        this.productIds = productIds;
        this.checkboxEnabled = checkboxEnabled || false;
        this.editableQuantity = editableQuantity || false;
        this.replacementPopup = new Products.ReplacementPopup(this.id + '-product-replacement-popup');
        
        
        if ($(id)) {
        this.attach();
        } else {
            Event.observe(
                window,
                'load',
                this.attach.bindAsEventListener(this)                
            );
        }        
        
        if (Products.Listing.listings && $A(Products.Listing.listings).indexOf(id) == -1) {
            Products.Listing.listings.push(id);
        } else {
            Products.Listing.listings = [this];
        }
        
        this.refreshQuantities();
        Products.Listing.onLoad(this);
        this.observeOnce(
            Listing.Event.PAGE_CHANGED,
            this.defaultPageChangedHandler.bind(this)
        );
        
        this.observe(
            Products.Listing.Event.SUBMITTED,
            function() {
                this.unfreeze();
            }.bind(this)
        );
        

        new DeepFreeze(this, ['handleSubmit']);        
                    
    },
    
    defaultPageChangedHandler : function() {
        window.setTimeout(
        	function() {
                if( ! $(this.id) )
                    return;

		        /* Focus on the first quantity field */
		        var inputs = $(this.id).getElementsByTagName('input');
		        if (inputs) {
			        for (var i = 0, c = inputs.length; i < c; i++) {
			            if (inputs[i].id.match(/quantity/)) {
			                var input = inputs[i];  
			                $(this.id).scrollTo();
			                input.focus();
			                break;
			            }
			        }
				} 
			}.bind(this),
			200
		);
    
    },
    
    /**
    * Returns the form data associated with this listing
    */
    getForm : function() {
        return $(this.id).getForm();
    },
    
    
    /**
     * Attaches the event handlers required by this listing
     */
    attach : function() {
        Listing.prototype.initialize.apply(this, [this.id, this.productIds]);

        this.listing = $(this.id);
                
        
        /* Register pagination event handlers */
        if (this.editableQuantity) {
            this.observeOnce(
                Listing.Event.PAGE_CHANGING,
                this.retain.bind(this)
            );
            
            this.observeOnce(
                Listing.Event.PAGE_CHANGED,
                this.restore.bind(this)
            );

            this.observeOnce(
                Listing.Event.SORTING,
                this.retain.bind(this)
            );
            
            this.observeOnce(
                Listing.Event.SORTED,
                this.restore.bind(this)
            );
            
        }  
        var up = this;
        var exec = function(form) {
            var cart = up.getCart();
            if (cart) {
                if (form && up.editableQuantity) {
                    Event.observe(
                        form, 
                        'submit',
                        function(e, form) {
                            Event.stop(e);
                            var form = $(up.id).getForm();    
                            if (form) {
                                up.handleSubmit(form);
                            }
                        }.bindAsEventListener(up)
                    );
                } 
                cart.observe(
                    MyUnisource.MiniCart.Event.PRODUCT_SUBMISSION_FAILED,
                    function() {
                        up.unfreeze();
                    }.bind(up)
                );   
                
            } else if (form) {
                Event.observe(
                    form,
                    'submit',
                    up.directSubmit.bind(up)
                );
            } 
            
        };

        if( $(this.id) && $(this.id).getForm) {
            var form = $(this.id).getForm();
            exec(form);            
        } else {
            document.observe( 'dom:loaded', function() {                        
                var element = $(up.id);
                if (element.getForm) {
                    var form = $(up.id).getForm();
                    exec(form);
                }
            } );
        }
            
    },
    
    checkReplacement : function() {
        var result = true;
        if (this.order) {
            var outOfStockProducts = $H(this.order.products)
                .findAll(function(entry) { return !entry.value.in_stock && entry.value.replacement && this.order.getQuantity(entry.value.id) > 0 }.bind(this))
                .pluck('value')
                .map(
                    function(product) {
                        product.quantity = this.order.getQuantity(product.id);
                        product.uom = this.order.getUnitOfMeasure(product.id, 'quantity');
                        return product;                        
                    }.bind(this)
                ); 
                
            if (outOfStockProducts.any()) {
                result = false;
                this.unfreeze();
                this.showReplacementPopup(outOfStockProducts);
            }
        }
        
        return result;
	},

    clearOnSubmit : function() {
        var cart = this.getCart();
        if (cart) {
            cart.observeOnce(
                MyUnisource.MiniCart.Event.PRODUCTS_SUBMITTED,
                function(products) {
                    for(var i = 0, length = products.length;i<length;i++) {
                        var product = products[i];
                        var quantityElement = this.getQuantityElement(product.id);
                        var oldValue = 0;
                        if (quantityElement) {            
                            oldValue = quantityElement.value;            
                            quantityElement.value = '';                                
                        } 
                        if (this.order) {
                            if (this.order.hasProduct(product.id)) {
                                this.order.refreshLine(product.id, oldValue, 0);
                            }
                        }
                    }
                    var productIds = $A(products).map(
                        function(product) {
                            return product.id;
                        }
                    ).toArray()

                    this.forgetQuantities(productIds);                                        
                    if (Products.Listing.listings) {
                        Products.Listing.forgetAll(productIds);                    
                    }                         

                }.bind(this)
            );            
        }     
    },
    
    showReplacementPopup : function(products) {
        this.replacementPopup.show(products);
    },
    
    submitReplacements : function() {
        this.handleSubmit(
            this.order.form,
            {
                force: true
            }
        );
    },
    
    
    /**
     * Creates hidden elements for retained quantities before submitting
     * the form
     *
     * @param event the submit event
     * @param form  the form being submitted
     * @param options  submission options
     */
    handleSubmit : function(form, options) {


        if (!options) {
            options = {};
        }
        if (options.force || this.checkReplacement()) {
            
        Products.Listing.retainAll();
        
        $A(Products.Listing.listings).each(
            function(listingId) {    
                var listingElement = $(listingId);
                
                if (listingElement && listingElement.listing instanceof Products.Listing) {
                    listingElement.listing.populateRetainedQuantities(form);
                }
            }
        );
        
            this.submitToMiniCart(form);        
        }        
    },
    
    directSubmit : function() {
        if (this.options['close-on-submit']) {
            window.setTimeout(
                function() {
                    window.close();
                },
                500
            )
        }
    },
    
    directSubmit : function() {
        if (this.options['close-on-submit']) {
            window.setTimeout(
                function() {
                    window.close();
                },
                3000
            )
        }
    },
    
    /**
    * Removes the quantities for the specified products from this 
    * listing.
    * 
    * @param Array productIds an array of product IDs
    */
    forgetQuantities: function(productIds) {
    	$A(productIds).findAll(
    		function(productId) {
    			var result =
                ( 
                    this.quantities[productId]
                    ||
                    Products.Listing.quantities[productId]
                );
                return result;    
    		}.bind(this)
    	).each(
    		function(productId) {
                if (this.quantities[productId]) {
    			    delete this.quantities[productId];    			
                }
                if (Products.Listing.quantities[productId]) {
                    delete Products.Listing.quantities[productId];
                }
    		}.bind(this)
    	);
    },
    
    /**
    * Adds all products in this form to the mini-cart, including
    * the ones that have been retained from pagination.
    */
    submitToMiniCart: function(form) {
        if (!form) {
            form = this.order.form;
        }
        this.fire(Products.Listing.Event.SUBMITTING);
    	this.retain();
    	this.refreshQuantities();
    	var quantities = {};
    	$H(this.quantities).each(
    		function(entry) {
    			if (entry.value > 0) {    			
    				quantities[entry.key] = entry.value;
    			}
    		}
    	);    	
        
    	var cart = this.getCart();
        var options = {};
        if (form) {
            $(form).getInputs().findAll(function(e) {return e.type == 'hidden'}).each(
                function(e) { 
                    
                    var match = e.name.match(/^Options\[(.+)\]$/)            
                    if (match) {
                        options[match[1]] = e.value;
                    }
                }
            );
        }
        
        this.clearOnSubmit();
    	cart.submitProducts(quantities, options);
    },
    
    /**
    * Returns the mini-cart instance to which products will 
    * be added
    */
    getCart : function() {
    	var cartElement = $(this.miniCartId);
    	var cart = (cartElement) ? cartElement.cart : null;
        if (cart) {
            if (!cart.productListings) {
                cart.productListings = {};
            }
            if (cart.productListings[this.id] !== this) {
                cart.productListings[this.id] = this;
                cart.observe(
                    MyUnisource.MiniCart.Event.PRODUCTS_SUBMITTED,
                    function() {
                        this.fire(Products.Listing.Event.SUBMITTED);
                    }
                );
            }        
        }
        return cart;
    },
    
    
    /**
     * Creates hidden elements only for this listing's retained
     * quantities
     *
     * @param form the form to which new elements will be added
     */
    populateRetainedQuantities : function(form) {
        this.refreshQuantities();
        
        $H(this.quantities).each(
            function(entry) {       
                if (!this.hasProduct(entry.key)) {
                    var input = this.createQuantityInput(form, entry.key, entry.value);
                    form.appendChild(input);
                }
            }.bind(this)
        );    
    },
    
    refreshQuantities: function() {
    	this.quantities = Products.Listing.quantities;
    },
    
    
    /**
     * Determines if the specified product is part of 
     * the current listing.
     *
     * @param productId the ID of the product to check
     */
    hasProduct: function(productId) {
        return $A(this.productIds).indexOf(productId) != -1
    },
    
    
    /**
     * Creates a hidden form element for a single product quantity
     * and returns it
     *
     * @return HTMLElement
     */
    createQuantityInput : function(form, productId, quantity) {
        var input = document.createElement('input');
        input.type = 'hidden';
        input.name = this.getQuantityElementName(productId);
        input.value = quantity;
        return input;
    },
        
    
    /**
     * Returns the name of the quantity element for the specified
     * product ID
     *
     * @param productId the ID of the product
     */
    getQuantityElementName : function(productId) {
        return 'quantity[' + productId + ']';
    },
    
    
    /**
     * Returns the element for the product quantity
     *
     * @param productId the ID of the product
     */
    getQuantityElement : function(productId) {
        return $(this.id + '-product-' + productId + '-quantity');
    },
    
    /**
     * Retains product selections
     */
    retain : function() {
        $A(this.productIds).each(
            function(productId) {
                var quantityElement = this.getQuantityElement(productId);
                
                if (quantityElement && (quantityElement.value !== '')) {
                    Products.Listing.quantities[productId] 
                        = this.quantities[productId] 
                        = quantityElement.value;
                }
                
            }.bind(this)
        );        
    },
    
    
    
    /**
     * Restores previously selected quantities
     */        
    restore : function() {
        $H(Products.Listing.quantities).each(
            function(entry) {
                var quantityElement = this.getQuantityElement(entry.key);
                    
                if (quantityElement) {
                	var oldValue = quantityElement.value;
                    quantityElement.value = entry.value;
                } 
            }.bind(this)                        
        ); 
        
        if (this.order) {
            this.order.updateAllAmounts();
        }
        
        
    },
    
    /**
    * "Freezes" this listing, thereby preventing submit events
    * from being handled.
    */
    freeze : function() {
        this.frozen = true;
    },
    
    /**
    * Removes the flag causing this listing to ignore "submit" 
    * events
    */
    unfreeze : function() {
        this.frozen = false;
    },
    
    /**
    * Displays an inventory popup for a product
    * 
    * @param string The product ID
    */
    showInventoryFor: function (productID) {
        
        if (typeof this.inventoryRequest != 'undefined' 
                && this.inventoryRequest != null)
        {
            this.inventoryRequest.transport.onreadystatechange = Prototype.emptyFunction;
            this.inventoryRequest.transport.abort();
            Ajax.activeRequestCount--;
        }
        this.inventoryRequest = new Ajax.Request(
            Paths.www + '/uni'+'_products/fetchInventory/' + productID, 
            {
                onSuccess: function (response) {
                    var result = response.responseJSON;                
                    if (result.success && result.quantity > 0) {
                        $(this.id + '-inventory-quantity-box-quantity').update(result.quantity);
                        $(this.id + '-inventory-quantity-box-uom-code').update(result.uom_code);
                        $(this.id + '-inventory-quantity-box').show();
                        $(this.id + '-inventory-error-message').hide();                        
                    } else {
                        $(this.id + '-inventory-quantity-box').hide();
                        $(this.id + '-inventory-error-message').show();
                        
                    }
                    $(this.id + '-inventory').unipopup.show();
                    this.inventoryRequest = null;
                }.bind(this)
            }
        );
        
    }    
    
}
);

Products.ReplacementPopup = Class.create(
    {
        initialize : function(popupId) {
            this.popup = $(popupId);            
            this.table = $(popupId + '-products-table');
        },
        
        show : function(products) {
            this.build(products)
            this.popup.unipopup.show();
        },
        
        hide : function() {
            this.popup.unipopup.hide();
        },
        
        build : function(products) {
            var tableBody = $(this.table.tBodies[0]);
            $A(tableBody.select('tr')).each(
                function(element) {$(element).remove()}
            );
            
            $A(products).each(
                function(product) {
                    var row = document.createElement('tr');
                    tableBody.appendChild(row);
                    
                    var productCodeCell = document.createElement('td');
                    $(productCodeCell).update(product.code);
                    $(productCodeCell).addClassName('code');
                    row.appendChild(productCodeCell);
                    
                    var productDescriptionCell = document.createElement('td');
                    $(productDescriptionCell)
                        .update(product.description)
                        .addClassName('description');
                    row.appendChild(productDescriptionCell);
                    
                    var quantityCell = document.createElement('td');
                    $(quantityCell).update(
                        product.quantity + ' ' + product.uom
                    );
                    row.appendChild(quantityCell);
                    
                    var replacementProductCell = document.createElement('td');
                    $(replacementProductCell).addClassName('code');
                    $(replacementProductCell).update(product.replacement.product_code);
                    row.appendChild(replacementProductCell);
                    
                    var replacementProductDescriptionCell = document.createElement('td');
                    $(replacementProductDescriptionCell)
                        .update(product.replacement.description)
                        .addClassName('description');
                    row.appendChild(replacementProductDescriptionCell);
                    
                }.bind(this)
            );             
        }
    }
);

