BasketCounter = new JS.Singleton('BasketCounter', {
    COOKIE_NAME:       'basketTotal',
    CURRENCY_SYMBOLS:  {GBP: '&pound;', EUR: '&euro;', USD: '&#36;'},
    
    COUNTER_CLASSNAME: 'basket-count',
    TOTAL_CLASSNAME:   'basket-total',
    
    NORMAL_COLOUR:     '#555555',
    HIGHLIGHT_COLOUR:  '#ec1c24',
    FADE_TIME:         4.0,
    
    currency:          'GBP',
    
    /**
     * The BasketCounter is decoupled from the backend: it simply reads its
     * initial values from the user's cookie. This means that the application
     * can be less dynamic, but the interface is still responsive to state.
     */
    initialize: function() {
        var cookieString = this._getCookie();
        this._items = parseInt(cookieString[0], 10) || 0;
        this._total = parseFloat(cookieString[1])   || 0.0;
    },
    
    /**
     * Lets the BasketCounter know where it should display. The argument should
     * be a link, or a CSS selector pointing to a link. Because the number of
     * items and total cost are not initially displayed, this method also
     * creates the HTML needed to display them.
     */
    displayIn: function(link, options) {
        this._setOptions();
        
        this._link      = Ojay(link).at(0);
        this._uri       = this._link.node.href;
        
        this._itemsHTML = Ojay(Ojay.HTML.span({className: this.COUNTER_CLASSNAME})).setContent(this._itemsText());
        this._totalHTML = Ojay(Ojay.HTML.span({className: this.TOTAL_CLASSNAME})).setContent(this._totalText());
        
        this._link.insert(this._itemsHTML, 'bottom').insert(this._totalHTML, 'bottom');
    },
    
    /**
     * Update the internal counters that keep track of how many items are in
     * the user's basket, and what the total is. This method also updates the
     * display, and highlights it to make it clear to the user that it is up
     * to date.
     */
    update: function(items, total) {
        this._items = parseInt(items, 10);
        this._total = parseFloat(total);
        
        this._setHTML()._highlightLink();
    },
    
    /**
     * Part of the external API, for other objects which might need to link to
     * the basket.
     */
    getBasketURI: function() {
        return this._uri;
    },
    
    /**
     * The number of items currently in the basket.
     */
    numItems: function() {
        return this._items;
    },
    
    /**
     * Change the currency in use.
     */
    setCurrency: function(currency) {
        this.currency = currency;
        
        if (this._itemsHTML && this._totalHTML) {
            this._setHTML();
        }
    },
    
    /**
     * Configure various options.
     */
    _setOptions: function(options) {
        this._options = options || {};
    },
    
    /**
     * Basket total cookies are in the form count|total|session hash, i.e.
     *
     *     1|250.00|78d94caed31207b917ef983e34047d22
     *
     * This method simply returns the cookie string, split into an array for
     * convenience.
     */
    _getCookie: function() {
        var str = YAHOO.util.Cookie.get(this.COOKIE_NAME);
        return str && str.length > 0 ? str.split('|') : [];
    },
    
    _setHTML: function() {
        this._itemsHTML.setContent(this._itemsText());
        this._totalHTML.setContent(this._totalText());
        
        return this;
    },
    
    _itemsText: function() {
        return this._items == 1 ? '1 item' : (this._items.toString() + ' items');
    },
    
    _totalText: function() {
        return this.CURRENCY_SYMBOLS[this.currency] + this._total.toFixed(2);
    },
    
    _highlightLink: function() {
        return this._link.animate({
            color: {
                from: this.HIGHLIGHT_COLOUR,
                to:   this.NORMAL_COLOUR
            }
        }, this.FADE_TIME)._(this);
    }
});

