function Filter() {
    var typesCount = 8;
    var rangesCount = 5;
    var rangeMap = ['All', 'City', 'Crossover', 'Sport', '4x4', 'LCV'];

    function init() {
        // Do some accounting
        var tCount = {};
        var rCount = {};
        var minPrice = {};
        var absMinPrice = 99999;
        var absMaxPrice = 0;
        for (var i = 0; i < offers.length; i++) {
            var offer = offers[i];
            if (!tCount[offer.type]) {
                tCount[offer.type] = 0;
            }
            tCount[offer.type]++;
            if (!rCount[offer.range]) {
                rCount[offer.range] = 0;
            }
            rCount[offer.range]++;

            if (offer.price > 1) {
                if (!minPrice[offer.model] || offer.price < minPrice[offer.model]) {
                    minPrice[offer.model] = offer.price;
                }
                if (offer.price < absMinPrice) {
                    absMinPrice = offer.price;
                }
                if (offer.price > absMaxPrice) {
                    absMaxPrice = offer.price;
                }
            }
        }
        tCount[0] = offers.length;
        rCount.All = offers.length;

        // Set up onclick events and update counts
        for (i = 0; i <= typesCount; i++) {
            $('#fType' + i).click(function() {
                checkAll('#fType', this);
                filterOffers();
            });
            $('#fTypeCount' + i).text('(' + (tCount[i] || 0) + ')');
        }
        for (i = 0; i <= rangesCount; i++) {
            $('#fRange' + i).click(function() {
                checkAll('#fRange', this);
                filterOffers();
            });
            $('#fRangeCount' + i).text('(' + (rCount[rangeMap[i]] || 0) + ')');
        }

        // Update from prices
        $('#contentArea .offerModule').each(function(i) {
            var $module = $(this);
            if (!$module.attr('id').match(/offerModule(\d+)/)) {
                return;
            }
            var model = RegExp.$1;
            if (minPrice[model]) {
                $module.find('.omFrom').text(format_currency(minPrice[model]));
            }
        });

        // Set up price range slider
        var sliderMin = Math.round(absMinPrice / 1000) * 1000;
        var sliderMax = Math.round(absMaxPrice / 1000 + 0.5) * 1000;
        $('#rangeslider').slider({
            range: true,
            min: sliderMin,
            max: sliderMax,
            step: 500,
            values: [sliderMin, sliderMax],
            slide: function(event, ui) {
                var $handle = $(ui.handle);
                $($handle.hasClass('rangeslider-handle-left') ? '#priceMin' : '#priceMax').text(format_currency(ui.value));
                filterOffers();
            }
        });
        $('.ui-slider').each(function() {
            $('.ui-slider-handle:first', this).addClass('rangeslider-handle-left');
            $('.ui-slider-handle:last', this).addClass('rangeslider-handle-right');
        });
        $('#priceMin').text(format_currency(sliderMin));
        $('#priceMax').text(format_currency(sliderMax));

        // Preselect an Offer type
        if (window.location.search.match(/[?&;]offer=(\d+)/)) {
            var offer = RegExp.$1;
            var $offer = $('#fType' + offer);
            if (offer != 0 && $offer.length) {
                // Uncheck everything
                $('#fType0').attr('checked', false);
                checkAll('#fType', $('#fType0'));
                $offer.attr('checked', true);
            }
        }

        // Preselect a Range
        if (window.location.search.match(/[?&;]range=([^&;]+)/)) {
            var range = RegExp.$1;
            for (i = 1; i < rangeMap.length; i++) {
                if (range.toLowerCase() == rangeMap[i].toLowerCase()) {
                    // Uncheck everything
                    $('#fRange0').attr('checked', false);
                    checkAll('#fRange', $('#fRange0'));
                    $('#fRange' + i).attr('checked', true);
                    break;
                }
            }
        }

// Save the filter selection when the user clicks on a model so that the filter
// can be used by the carousel
        $('.offerModule a').click(filterSave);

        filterOffers();
    }

    function filterOffers() {
        var types = [];
        for (var i = 0; i <= typesCount; i++) {
            types.push($('#fType' + i).attr('checked'));
        }
        var ranges = {
            0: $('#fRange0').attr('checked')
        };
        for (i = 1; i <= rangesCount; i++) {
            ranges[rangeMap[i]] = $('#fRange' + i).attr('checked');
        }
        var prices = $('#rangeslider').slider('option', 'values');

        // Loop through offers
        var visibleModels = {};
        for (i = 0; i < offers.length; i++) {
            if ((types[0] || types[offers[i].type]) &&
                (ranges[0] || ranges[offers[i].range]) &&
                (offers[i].price >= prices[0] && offers[i].price <= prices[1])) {
                if (!visibleModels[offers[i].model]) {
                    visibleModels[offers[i].model] = 0;
                }
                visibleModels[offers[i].model]++;
            }
        }

        // Update offer modules
        $('#contentArea .offerModule').each(function(i) {
            var $module = $(this);
            if (!$module.attr('id').match(/offerModule(\d+)/)) {
                return;
            }
            var model = RegExp.$1;
            if (visibleModels[model] > 0) {
                $module.find('.omCount').text(visibleModels[model]);
                $module.show();
            }
            else {
                $module.hide();
            }
        });

        if ($('#contentArea .offerModule:visible').length === 0) {
            $('#noOffers').show();
        }
        else {
            $('#noOffers').hide();
        }
    }

    function reset() {
        var $slider = $('#rangeslider');
        $slider.slider('values', 0, $slider.slider('option', 'min'));
        $slider.slider('values', 1, $slider.slider('option', 'max'));

        $('#fType0').attr('checked', true);
        checkAll('#fType', $('#fType0'));

        $('#fRange0').attr('checked', true);
        checkAll('#fRange', $('#fRange0'));

        filterOffers();
    }

// Save the filter values into a cookie for later use.
    function filterSave() {
        var price = $('#rangeslider').slider('option', 'values');
        $.cookie('filter_price', price[0] + '_' + price[1]);

        if ($('#fType0').attr('checked')) {
            $.cookie('filter_type', 'all');
        }
        else {
            var types = [];
            if ($('#fType1').attr('checked')) {
                types.push('pref_fin');
            }
            if ($('#fType2').attr('checked')) {
                types.push('hire_purch');
            }
            if ($('#fType3').attr('checked')) {
                types.push('con_hire');
            }
            if ($('#fType4').attr('checked')) {
                types.push('scrap');
            }
            if ($('#fType5').attr('checked')) {
                types.push('entry_price');
            }
            if ($('#fType6').attr('checked')) {
                types.push('fin_lease');
            }
            if ($('#fType7').attr('checked')) {
                types.push('free_serv');
            }
            if ($('#fType8').attr('checked')) {
                types.push('lcv_scrap');
            }
            $.cookie('filter_type', types.join(','));
        }
    }

    function checkAll(id, obj) {
        var $obj = $(obj);
        var i = 1;
        var $cb = $(id + i);

        // This is the "All" checkbox
        if ('#' + $obj.attr('id') == id + '0') {
            while ($cb.length) {
                $cb.attr('checked', $obj.attr('checked'));
                $cb = $(id + ++i);
            }
        }
        else {
            var checkem = true;
            while ($cb.length) {
                if (!$cb.attr('checked')) {
                    checkem = false;
                    break;
                }
                $cb = $(id + ++i);
            }
            $(id + '0').attr('checked', checkem);
        }
    }

    function format_currency(val, no_trim) {
        val += '';

        if (!no_trim) {
            val = val.replace(/\.0+$/, '');
        }

        var re = /^([-+]?\d+)(\d{3})/;
        while (re.test(val)) {
            val = val.replace(re, '$1,$2');
        }

        return val;
    }

    this.init = init;
    this.reset = reset;
}
