Checkout = {
    
    FORM_NAME:              'opcCheckout',
    DEFAULT:                'opcCheckoutPersonal.user.profile.defaultAddress.',
    DELIVERY:               'opcCheckoutDelivery.newDeliveryAddress.',
    BILLING:                'opcCheckoutPayment.newBillingAddress.',
    NEWADDRESS:				'site.Address.',
    NEWDEFAULTADDRESS:		'site.DefaultAddress.',
    NEWREGISTERADDRESS:		'site.Register2.',
    NEWPROFILEADDRESS:		'site.EditProfile.profile.defaultAddress.',
    
    
    PERSONAL_STATE:         1,
    DELIVERY_STATE:         2,
    PAYMENT_STATE:          3,
    CONFIRM_STATE:          4,
    
    PERSONAL_SECTION:       'details',
    DELIVERY_SECTION:       'delivery',
    PAYMENT_SECTION:        'payment',
    CONFIRM_SECTION:        'confirmation',
    
    ADDRESS1_FIELD:         'address1',
    ADDRESS2_FIELD:         'address2',
    ADDRESS3_FIELD:         'address3',
    CITY_FIELD:             'city',
    COUNTY_FIELD:           'county',
    POSTCODE_FIELD:         'postcode',
    COUNTRY_FIELD:          'country',
    
    MPI_STATUS_ENROLLED:    '200',
    
    FADE_OPACITY:           0.5,
    FADE_TIME:              0.4,
    
    getInputs: function(form, name) {
        var inputs = form.descendants('input, textarea, select');
        if (name) inputs = inputs.filter(function(element){
            return element.node.name == name;
        });
        return inputs;
    },
    
    incrementState: function(currentState, newState) {
        return newState > currentState ? newState : currentState;
    },
    
    showBlock: function(block, h) {
        if(block.getStyle('display') == 'none') {
            block.setStyle({height: 0, overflow: 'hidden'})
                .show()
                .animate({
                    height: {to: h}
                }, 1.0)
                .setStyle({overflow: ''});
        }
    },
    
    hideBlock: function(block) {
        if (block.getStyle('display') != 'none') {
            block.setStyle({overflow: 'hidden'})
                .animate({
                    height: {to: 0}
                }, 1.0)
                .hide();
        }
    },
    
    activateSection: function(state) {
        if (state == Checkout.PERSONAL_STATE) {
            Checkout.summarise(Ojay.byId('deliveryDetails'));
            Checkout.summarise(Ojay.byId('paymentDetails'));
            Checkout.deactivate(Ojay.byId('confirmation'));
            Checkout.activate(Ojay.byId('personalDetails'));
        } else if (state == Checkout.DELIVERY_STATE) {
            Checkout.summarise(Ojay.byId('personalDetails'));
            Checkout.summarise(Ojay.byId('paymentDetails'));
            Checkout.deactivate(Ojay.byId('confirmation'));
            Checkout.activate(Ojay.byId('deliveryDetails'));
        } else if (state == Checkout.PAYMENT_STATE) {
            Checkout.summarise(Ojay.byId('personalDetails'));
            Checkout.summarise(Ojay.byId('deliveryDetails'));
            Checkout.deactivate(Ojay.byId('confirmation'));
            Checkout.activate(Ojay.byId('paymentDetails'));
        } else if (state == Checkout.CONFIRM_STATE) {
            Checkout.summarise(Ojay.byId('personalDetails'));
            Checkout.summarise(Ojay.byId('deliveryDetails'));
            Checkout.summarise(Ojay.byId('paymentDetails'));
            Checkout.activate(Ojay.byId('confirmation'));
        }
    },
    
    summarise: function(el) {
        el = Ojay(el);
        if (el.hasClass('opc-inactive') ||
            el.hasClass('opc-complete')) return;
        
        var height = el.getHeight();
        el.setStyle({height: height + 'px', overflow: 'hidden'})
          .animate({height: {to: 100}}, 1.5)
          .setStyle({height: '', overflow: ''})
          .addClass('opc-complete')       
          .removeClass('opc-active');
    },
    
    activate: function(el){
        el = Ojay(el);
        if (el.hasClass('opc-active')) return;
        
        var current = el.getHeight();
        el.addClass('opc-active')       
          .removeClass('opc-complete')
          .removeClass('opc-inactive');
        
        var height = el.getHeight();
        el.setStyle({height: current + 'px', overflow: 'hidden'})
          .animate({height: {to: height}}, 1.5)
          .setStyle({height: '', overflow: ''});
    },
    
    deactivate: function(el){
        if(!el.hasClass('opc-inactive')){
            el.addClass('opc-inactive');        
            el.removeClass('opc-active');
        }
    },
    
    clearErrors : function(){
        Ojay('P.fielderror').forEach(function(el){
            el.setContent('');
            el.setStyle({display : 'none'});
        });
        Ojay.byId('payment-error').setStyle({display : 'none'});
        Ojay.byId('currency-unshippable').setStyle({display : 'none'});
    },
    
    displayErrors: function(errors){
        Checkout.clearErrors();
        errors.forEach(function(error){
            var el = Ojay.byId(error.field + '-error');
            el.setContent(error.errorMessage);
            el.setStyle({display: 'block'});
        });
    },
    
    isNewAddress: function(fieldId){
        return Ojay.byId(fieldId).node.checked;
    },
    
    isNewPaymentCard: function(){
        return Ojay.byId('payment-card-new').node.checked;
    },
    
    displayError: function(field, message){
        var errorEl = field.ancestors('.opc-field').at(0).descendants('.fielderror').at(0);
        errorEl.setContent(message).setStyle({display: 'block'});
    },
    
    hideError: function(field){
        var errorEl = field.ancestors('.opc-field').at(0).descendants('.fielderror').at(0);
        errorEl.hide();
    },
    
    populateDetails: function(data, section){
        if(section == Checkout.PERSONAL_SECTION){
            var fullName = '',
                details = '',
                user = data.user,
                address = data.defaultAddress;
            
            fullName = user.title + ' ' + user.firstName + ' ' + user.lastName;
            details = fullName + '<br>' + 'Email address: ' + user.email + '<br>';
            details += 'Telephone: ' + user.telephone;
            Ojay.byId('personal-text').setContent('Name: ' + details);
            
            var addressLabel = address.address1 + ', ';
            if(address.address2)
                addressLabel += address.address2 + ', ';
            if(address.address3)
                addressLabel += address.address3 + ', ';
            addressLabel += address.city + ', ';
            if(address.county)
                addressLabel += address.county + ', ';
            if(address.postcode)
                addressLabel += address.postcode + ', ';
            addressLabel += address.country;
            Ojay.byId('delivery-text').setContent(fullName + ' - ' + addressLabel);
            Ojay.byId('delivery-main-address').setContent(fullName + ' - ' + addressLabel);
            Ojay.byId('billing-main-address').setContent(addressLabel);
        }
        else if(section == Checkout.DELIVERY_SECTION){
            var details = '',
                address = data.deliveryAddress;
            
            details = address.title + ' ' + address.firstName + ' ' + address.lastName + ', ';
            details += address.address1 + ', ';
            if(address.address2)
                details += address.address2 + ', ';
            if(address.address3)
                details += address.address3 + ', ';
            details += address.city + ', ';
            if(address.county)
                details += address.county + ', ';
            if(address.postcode)
                details += address.postcode + ', ';
            details += address.country;
            Ojay.byId('delivery-text').setContent(details);
        }
        else if(section == Checkout.PAYMENT_SECTION){
            var paymentDetails = data.payment.name + ' - ' + data.payment.cardType + ' ' + data.payment.number;
            Ojay('.payment-text').setContent(paymentDetails);
        }
    },
    
    /****************************************************************
    @class Checkout.AccountLookup
    ****************************************************************/
    AccountLookup: new JS.Class({
        include: Ojay.Observable,
        
        extend: {
            CHECK_URL:              '/checkout/ajax/check-login.html',
            EMAIL_FIELD:            'opcCheckoutStart.email',
            PASSWORD_FIELD:         'prefill-password',
            ACTION_METHOD_ID:       'actionMethod'
        },
        
        initialize: function(form, options) {
            Checkout._accountLookup = this;
            
            this._form              = Ojay(form);
            this._options           = options || {};
            this._checkUrl          = this._options.checkUrl    || this.klass.CHECK_URL;
            var email               = this._options.emailField  || this.klass.EMAIL_FIELD;
            this._email             = Checkout.getInputs(this._form, email);
            this._actionMethodId    = this._options.actionMethodId || this.klass.ACTION_METHOD_ID;
            this._basketPage        = this._options.basketPage || 'y';
            this._password          = Checkout.getInputs(this._form, this.klass.PASSWORD_FIELD);
            
            this._forgottenPasswordLink  = Ojay.byId('forgotten-password');
            
            this._email.on('keypress')._(this).wait(0.001)._(function() {
                this._updateForgottenPasswordLink();
                this._checkAddress();
            }.bind(this));
            
            this._passwordSection   = Ojay.byId('login-password');
            this._continueButton    = Ojay.byId('continue1');
            this._actionMethod      = Ojay.byId(this._actionMethodId);
            
            this._passwordHeight    = this._passwordSection.getHeight();
            
            this[!(options.hasPasswordError || options.existingEmail) ? '_passwordSection' : '_continueButton'].hide();
            
            this._emailField        = Checkout.getInputs(this._form, this.klass.EMAIL_FIELD);
            
            var button, status, container = Ojay.HTML.span({className: 'account-lookup'}, function(HTML) {
                button = Ojay( HTML.button({className: 'account-lookup'}, 'Retrieve Details') );
                status = Ojay( HTML.span({className: 'login-status'}) );
            });
            this._password.insert(container, 'after');
            this._statusDisplay = status;
        },
        
        _updateForgottenPasswordLink: function() {
            if (this._forgottenPasswordLink.length < 1) return this;
            
            var link = this._forgottenPasswordLink.node,
                uri  = Ojay.URI.parse(link.href);
            
            uri.setParam('email', this._email.node.value);
            link.href = uri.toString();
            
            return this;
        },
        
        _checkAddress: function() {
            var email = this._email.node.value;
            
            if (this._loginEmail === email) return;
            
            Ojay('p.email-error').forEach(function(el) {
                el.setContent('');
            });
            
            if (!Ojay.EMAIL_FORMAT.test(email)) {
                this.hidePasswordSection();
                return;
            }
            
            this._loginEmail = email;
            
            Ojay.HTTP.GET(this._checkUrl, {email: this._loginEmail}, {
                onSuccess: function(response) {
                    if (response.responseText.trim() == 'true') {
                        this.showPasswordSection();
                    } else {
                        this.hidePasswordSection();
                    }
                }.bind(this)
            });
        },
        
        showPasswordSection : function() {
            if(this._passwordSection.getStyle('display') != 'none') return;
            
            if(Ojay('#emailError'))
                Ojay('#emailError').setStyle({display: 'none'});
            this._actionMethod.node.value = 'doExistingCustomer';
            this._continueButton.hide();
            this._passwordSection
            .setStyle({height: 0, overflow: 'hidden', opacity: 0})
            .show()
            .animate({
                height: {to: this._passwordHeight},
                opacity: {to: 1}
            }, 0.4)
            .setStyle({overflow: ''});
        },
        
        hidePasswordSection : function() {
            if(this._passwordSection.getStyle('display') == 'none') return;
            if(this._basketPage == 'y')
                this._actionMethod.node.value = 'doNewCustomer';
            else
                this._actionMethod.node.value = 'doSavePersonal';
            this._continueButton.show();
            this._passwordSection
            .animate({
                height: {to: 0},
                opacity: {to: 0}
            }, 0.4)
            .setStyle({overflow: 'hidden'})
            .hide();
        },
        
        setActionMethod: function(method) {
            this._actionMethod.node.value = method;
        }
        
    }),
    
   /****************************************************************
    @class Checkout.AddressHelper
    ****************************************************************/
    AddressHelper: new JS.Class({
        extend: {
            SEARCH_URL:     '/checkout/ajax/addresses.xml',
            POSTCODE_REGEX: /\b[A-PR-UWYZ][A-HK-Y0-9][A-HJKSTUW0-9]?[ABEHMNPRVWXY0-9]? *[0-9][ABD-HJLN-UW-Z]{2}\b/i
        },
        
        initialize: function(form, container, prefix, options) {
            this._form = Ojay(form);
            this._container = this._form.descendants(container);
            prefix = prefix || Checkout.DEFAULT;
            this._prefix = prefix;
            this._container.insert(this.getHTML());
            options = options || {};
            
            this._address1Field     = options.address1 || Checkout.getInputs(this._form, this._prefix + Checkout.ADDRESS1_FIELD);
            this._address2Field     = options.address2 || Checkout.getInputs(this._form, this._prefix + Checkout.ADDRESS2_FIELD);
            this._address3Field     = options.address3 || Checkout.getInputs(this._form, this._prefix + Checkout.ADDRESS3_FIELD);
            this._cityField         = options.city     || Checkout.getInputs(this._form, this._prefix + Checkout.CITY_FIELD);
            this._countyField       = options.county   || Checkout.getInputs(this._form, this._prefix + Checkout.COUNTY_FIELD);
            this._postcodeField     = options.postcode || Checkout.getInputs(this._form, this._prefix + Checkout.POSTCODE_FIELD);
            this._countryField      = options.country  || Checkout.getInputs(this._form, 'P' + this._prefix + Checkout.COUNTRY_FIELD);
        },
        
        getHTML: function() {
            if (this._html) return this._html;
            
            var self = this;
            
            this._html = Ojay(Ojay.HTML.form({className: 'postcode-lookup-form'}, function(H) {
                H.div({className: 'clear'}, function(H) {
                    H.div({className: 'grid c185'},
                        H.label({htmlFor: self._prefix + 'pclookup'}, 'Enter a UK postcode and search:'));                    
                    H.div({className: 'grid grid-spacer c15'});
                    
                    H.div({className: 'grid c300'}, function(H) {
                        self._postcode = H.input({id: self._prefix + 'pclookup', name: 'postcode', className: 'primary text c150'});
                        self._button   = Ojay(H.input({type: 'submit', value: 'Lookup address', className: 'submit submit-white secondary'}));
                    });
                });
                
                self._menu = Ojay(H.div({className: 'clear'}, function(H) {
                    H.div({className: 'grid c185'},
                        H.label({htmlFor: self._prefix + 'pcselect'}, 'Choose your address from the list:'));
                    
                    H.div({className: 'grid grid-spacer c15'});
                    
                    H.div({className: 'grid c300'}, function(H) {
                        self._select = Ojay(H.select({className: 'select address-helper-select'}));
                    });
                }));
                
                self._error = Ojay(H.p({className: 'fielderror'},
                    H.span('error message goes here')));
            }));
            
            this._html.on('submit', Ojay.stopEvent)._(this)._postcodeSearch();
            this._select.on('change')._(this)._selectAddress();
            
            this._select.hide();
            this._error.hide();
            this._menu.hide();
            
            return this._html;
        },
        
        getMenu: function() {
            return this._menu;
        },
        
        _postcodeSearch: function() {
            var postcode = this._postcode.value;
            
            if (!this.klass.POSTCODE_REGEX.test(postcode)) {
                this.showError('Post code not valid');
                return;
            }
            
            this._error.hide();
            this._button.setStyle({width: this._button.getWidth() + 'px'});
            this._button.set({value: 'Searching...'});
            
            Ojay.HTTP.GET(this.klass.SEARCH_URL, {postcode: postcode}, {
                onSuccess: function(response) {
                    var addresses = Checkout.Address.fromXML(response.responseXML);
                    this.setAddresses(addresses);
                    
                    this._select.show();
                }.bind(this),
                
                onComplete: function() {
                    this._button.set({value: 'Lookup address'});
                }.bind(this)
            });
        },
        
        _selectAddress: function(index) {
            index = (index === undefined) ? Number( this._select.node.value ) : index;
            var address = this._addresses[index];
            if (!address) return;
            
            Checkout.Address.FIELDS.forEach(function(field) {
                var input = this['_' + field + 'Field'];
                if (!input) return;
                Ojay.Forms.setValue(input, address[field]);
            }, this);
            this._countryField.node.selectedIndex = 0;
        },
        
        setAddresses: function(addresses, update) {
            this._addresses = addresses || [];
            var html;
            if (addresses.length === 0) {
                this.showError('No addresses found for this post code');
                return;
            } else {
                html = "<option>Please select an address</option>";
                addresses.forEach(function(address, i) {
                    html += '<option value="' + i + '">' + address.toShortAddress() + '</option>';
                });
            }
            this._select.setContent(html);
            this._select.node.options[0].selected = true;
            
            var hasAddress = (this._addresses.length > 0);
            this._menu[hasAddress ? 'show' : 'hide']();
            
            if (update) this._selectAddress(0);
        },
        
        showError: function(msg) {
            this._error.setContent(msg);
            this._error.show();
        }
    }),
    
    /****************************************************************
    @class Checkout.Address
    ****************************************************************/
    Address: new JS.Class({
        initialize: function(properties) {
            this.klass.FIELDS.forEach(function(field) {
               this[field] = "";
            }, this);
            this.set(properties);
        },
        
        set: function(properties) {
            JS.extend(this, properties);
        },
        
        toString: function() {
            return this.klass.FIELDS.map(function(key) { return this[key] }, this).join(', ');
        },
        
        toShortAddress: function() {
            return this.address1 + (this.address2 ? ', ' + this.address2 : '') + ', ' + this.city;
        },
        
        extend: {
            fromXML: function(xml) {
                var doc = xml.documentElement;
                var addressNodes = doc.getElementsByTagName('address');
                var addresses = [], address, node;
                
                for (var i = 0, n = addressNodes.length; i < n; i++) {
                    node = addressNodes.item(i).firstChild,
                    address = new this;
                    addresses.push(address);
                    
                    while (node) {
                        if (node.nodeType == Ojay.HTML.ELEMENT_NODE && node.firstChild)
                            address[node.nodeName] = node.firstChild.data;
                        node = node.nextSibling;
                    }
                }
                return addresses;
            },
            
            FIELDS: 'addressName title firstName lastName address1 address2 address3 city county country postcode'.split(' ')
        }
    })    
};

