(function($) {
    
    $.fn.finder = function(o) {
        var $this = this;

        var _map;
        var _list;
        var _visible;
        
        var opts = {
            listurl: '/ecomaXL/modules/quickfinder/content/list.html',
            mapurl : '/ecomaXL/modules/quickfinder/content/loesungsfinder.json',
            fxDur: 300
        }
        
        if (o != null)
            $.extend(opts, o);

        load();

        return this;

        function load() {
            $this.append('<img src="/ecomaXL/modules/quickfinder/pics/loading.gif" class="loading" />');
            $.ajax({
                url: opts.listurl,
                dataType: 'html',
                success: function(data, status, XMLHttpRequest) {
                    $this.children('.loading').remove();
                    $this.append(data);
                    $this.children().hide(0);
                    $.ajax({
                        url: opts.mapurl,
                        dataType: 'json',
                        success: function(data, status, XMLHttpRequest) {
                            $this.children().show(0);
                            parseData(data);
                            registerHandlers();
                        },
                        error: ajaxError
                    })
                },
                error: ajaxError
            });
        }
        
        function ajaxError(XMLHttpRequest, status, error) {
            if (!console)
                return false;
            console.log('error thrown: ---------------------------------');
            console.log(XMLHttpRequest);
            console.log(status);
            console.log(error);
            console.log('-----------------------------------------------');
        }
        
        function parseData(data){
            _map = data;
            _list = [];
            _visible = [];
            for (var d in data) {
                _list.push(parseInt(d));
                _visible.push(parseInt(d));
            }
        }
        
        function registerHandlers() {
            var catListContainer = $this.find('.categoryList');
            var catList = $this.find('.categoryList li[id^=tag]');
            var productListContainer = $this.find('.productList .list');
            var productList = $this.find('.productList .list li');
            var noResult = $this.find('.productList .list .noresult');
            var eMaxDim = {
                width: productList.width(),
                height: productList.height(),
                fontSize: parseInt(productList.css('font-size'))
            }
            var lastInfoBubbleID = 0;
            $.finder.bc.e = $this.find('.productList .breadcrumb');
            
            $.infoBubble.setViewport($this);
            productListContainer.simplescroller();
            
            $this.find('.close').click(function() {
                $.infoBubble.hide();
                if ($.finder.bc.e.find('.removeAll').length > 0)
                    $.finder.bc.e.find('.removeAll').click();
                if ($this.find('.back:visible').length > 0)
                    $this.find('.back:visible').click();
            });
            
            $this.find('.showHelp').click(function() {
                var $this = $(this);
                $this.parents('.helpContainer').find('.description').stop().slideDown(opts.fxDur);
                $this.stop(true, true).fadeOut(opts.fxDur, function() {
                    $(this).siblings('.hideHelp').stop(true, true).fadeIn(opts.fxDur);
                });
            });
            
            $this.find('.hideHelp').click(function() {
                var $this = $(this);
                $this.parents('.helpContainer').find('.description').stop().slideUp(opts.fxDur);
                $this.stop(true, true).fadeOut(opts.fxDur, function() {
                    $(this).siblings('.showHelp').stop(true, true).fadeIn(opts.fxDur);
                });
            });
            
            $this.find('.open').parent('h4').click(function(e, callback) {
                var $this = $(this);
                $this.parents('ul:first').animate({left: -$this.parents('.categoryList').outerWidth()}, opts.fxDur);
                $this.siblings('ul, h4.sub').show(0);
                catListContainer.find('.back').fadeIn(opts.fxDur);
                e.preventDefault();
                return false;
            });
            
            $this.find('.back').click(function(e, callback) {
                var $this = $(this);
                catListContainer.children('ul:first').animate({left: 0}, {
                    duration: opts.fxDur,
                    complete: function(){
                        catListContainer.find('ul ul:visible, h4.sub:visible').hide(0);
                    }
                });
                $this.fadeOut(opts.fxDur);
                e.preventDefault();
                return false;
            });
            
            productList.hover(function(e) {
                var $t = $(this);
                var id = $(this).attr('id').split('-');
                id = id[id.length - 1];
                $.infoBubble.setOpt('offset', { x: ($t.width() / 2) - 10, y: -($t.height() / 5) });
                if (lastInfoBubbleID != id) {
                    $.infoBubble.show('<img src="/ecomaXL/modules/quickfinder/pics/loading.gif" class="loading" />', { x: $t.offset().left + ($t.width() / 2), y: $t.offset().top + ($t.height() / 2) });
                    $.ajax({
                        url: '/ecomaXL/modules/quickfinder/content/info/product_' + id + '.html',
                        dataType: 'html',
                        success: function(data, status, XMLHttpRequest){
                            $.infoBubble.changeContent(data);
                        },
                        error: ajaxError
                    });
                }
                else {
                    $.infoBubble.show();
                }
                lastInfoBubbleID = id;
            }, function(e) {
                $.infoBubble.hide();
            });
            
            $.infoBubble.setHover(function() {
                $.infoBubble.show();
            });
            
            $.finder.bc.e.find('div').live('click', function() {
                var id = $(this).attr('id').split('-');
                id = id[id.length - 1];
                catList.filter('#tag-' + id).trigger('click');
            });
            
            catList.hover(function() {
                if ($(this).hasClass('active')) return false;
                var id = $(this).attr('id').split('-');
                id = id[id.length - 1];
                var affected = $.finder.getElementsByMap(productList, $.finder.subtractMaps(_visible, _map[id]), true);
                if (!jQuery.support.opacity)
                    affected.css({ opacity: 0.3 });
                else 
                    affected.stop(true, false).fadeTo(opts.fxDur, 0.3);
            }, function() {
                if (!jQuery.support.opacity)
                    productList.filter(':not(.hidden)').css({ opacity: 1 }).show(0).css('filter', '');
                else 
                    productList.filter(':not(.hidden)').stop(true, false).fadeTo(opts.fxDur, 1);
            });
            
            catList.click(function() {
                var $t = $(this);
                var bc = $t.parents('#quickfinder').find('.productList .breadcrumb');
                var id = $t.attr('id').split('-');
                    id = id[id.length - 1];
                var category = $t.parents('li[class^=cat]').attr('class');
                if ($t.hasClass('active')) {
                    $t.removeClass('active');
                    $.finder.bc.remove(id);
                    var v = _list;
                    catList.filter('.active').each(function() {
                        var id = $(this).attr('id').split('-');
                        id = id[id.length - 1];
                        v = $.finder.intersectMaps(v, _map[id]);
                    });
                    var affected = $.finder.getElementsByMap(productList, $.finder.subtractMaps(v, _visible));
                    _visible = v;
                    if (affected.length > 0) {
                        affected.removeClass('hidden').show(0).css('filter', '');
                    }
                    if (_visible.length == 0)
                        noResult.show(0);
                    else
                        noResult.hide(0);
                }
                else {
                    $t.addClass('active');
                    $.finder.bc.add($t.text(), id, category);
                    var affected = $.finder.getElementsByMap(productList, $.finder.subtractMaps(_visible, _map[id]));
                    _visible = $.finder.intersectMaps(_visible, _map[id]);
                    if (affected.length > 0) {
                        affected.fadeTo(0, 1).addClass('hidden').hide(0);
                    }
                    if (_visible.length == 0)
                        noResult.show(0);
                    else
                        noResult.hide(0);
                }
                productListContainer.simplescroller();
            });
        }

    }
    
    $.finder = {
        
        intersectMaps : function(map1, map2) {
            if (!map1 || !map2)
                return false;
                
            var tmp = [];
            
            for (var i = 0; i < map1.length; i++) {
                if ($.inArray(map1[i], map2) != -1)
                    tmp.push(map1[i]);
            }
            
            return tmp;
        },
        
        subtractMaps : function(map1, map2) {
            if (!map1 || !map2)
                return false;
                
            var tmp = [];
            
            for (var i = 0; i < map1.length; i++) {
                if ($.inArray(map1[i], map2) == -1)
                    tmp.push(map1[i]);
            }
            return tmp;
        },
        
        getElementsByMap : function(list, map) {
            if (map.length == 0)
                return [];
            var f = '';
            for (var i = 0; i < map.length; i++) {
                if (i != 0) f += ', ';
                f += '#product-' + map[i];
            }
            return list.filter(f);
        },
        
        fitElementsToContainer : function($container, elements, maxDim) {
            if (!$container || !elements)
                return;
            var NRe = elements.length;
            var m, n;
            var Wc = $container.width(); var Hc = $container.height();
            var oWe = elements.outerWidth(true); var oHe = elements.outerHeight(true);
            var We = elements.width(); var He = elements.height();
            var FSe = parseInt(elements.css('font-size'));
            var diffW = oWe - We; var diffH = oHe - He;
            
            var r = (Wc / oWe) / (Hc / oHe);
            n = Math.ceil(Math.sqrt(NRe * r));
            m = Math.ceil((n / r));
            
            var ratio = (Wc / n) / oWe;
            var nW = Math.floor((ratio * oWe) - diffW);
            var nH = Math.floor((ratio * oHe) - diffH);
            var nFS = Math.floor(ratio * FSe);
            if (ratio * oHe * m > Hc) {
                ratio = (Hc / m) / oHe;
                nW = Math.floor((ratio * oWe) - diffW);
                nH = Math.floor((ratio * oHe) - diffH);
            }
            if(maxDim != null && (nW > maxDim.width || nH > maxDim.height)) {
                nW = maxDim.width;
                nH = maxDim.height;
                if(nFS > maxDim.fontSize)
                    nFS = maxDim.fontSize;
            }
            
            elements.width(nW);
            elements.height(nH);
            elements.css('font-size',  nFS + 'px');
        },
        
        bc: {
            e: null,
            add: function(txt, id, category) {
                if ($.finder.bc.e == null)
                    return false;
                if ($.finder.bc.e.find('em').length > 0) {
                    var bcem = $.finder.bc.e.find('em');
                    $.finder.bc.e.attr('txtAll', bcem.text());
                    bcem.remove();
                    $.finder.bc.e.append($('<div class="removeAll"><span>zurücksetzen</span></div>').click(function() { $(this).siblings('div').click(); }));
                }
                $.finder.bc.e.find('.removeAll').before('<div id="bc-' + id + '" class="' + category + '"><span>' + txt + '</span></div>')
            },
            remove: function(id) {
                if ($.finder.bc.e == null)
                    return false;
                
                $.finder.bc.e.find('#bc-' + id).remove();
                
                if ($.finder.bc.e.find('div:not(.removeAll)').length == 0) {
                    $.finder.bc.e.find('.removeAll').remove();
                    $.finder.bc.e.append('<em>' + $.finder.bc.e.attr('txtAll') + '</em>');
                }
            }
        }
        
    }
    
    $.infoBubble = new function() {
        var container = null;
        var viewport = null;
        var hoverFunc = function() {};
        var lastPos = null;
        
        var tOut = null;
        
        var opts = {
            fxDur: 300,
            offset: {
                x: 20,
                y: 20
            }
        }
        
        this.show = function(content, pos) {
            this.changeContent(content);
            this.moveTo(pos);
            if (viewport.is(':animated') || viewport.is(':hidden') || viewport.parents().is(':animated'))
                return false;
            if (tOut != null) window.clearTimeout(tOut);
            if (!jQuery.support.opacity)
                container.show(0);
            else
                container.stop(true, false).fadeTo(opts.fxDur, 1);
            
        };
        
        this.hide = function() {
            if (!jQuery.support.opacity)
                tOut = window.setTimeout(function(){
                    container.hide(0)
                }, 100);
            else
                container.stop(true, false).fadeTo(opts.fxDur, 0, function() { $(this).hide(0); });
        };
        
        this.changeContent = function(content) {
            if (content == null) return;
            if (!inDOM())
                injectInDOM();
            container.find('.content').html(content);
            if (lastPos != null)
                $.infoBubble.moveTo(lastPos);
        }
        
        this.moveTo = function(pos) {
            if (pos == null) return;
            var _l = pos.x + opts.offset.x;
            var _t = pos.y + opts.offset.y;
            var _c = '';
            lastPos = pos;
            if (pos.x + opts.offset.x + container.outerWidth() > viewport.width() + viewport.offset().left) {
                _l = pos.x - opts.offset.x - container.outerWidth();
                _c += 'l';
            }
                
            if (pos.y + opts.offset.y + container.outerHeight() > viewport.height() + viewport.offset().top) {
                _t = pos.y - opts.offset.y - container.outerHeight();
                _c += 't';
            }
            
            container.children().removeAttr('class').addClass(_c);
            
            container.css({
                left: _l,
                top: _t
            });
        }
        
        this.setOpt = function(optName, optValue) {
            opts[optName] = optValue;
        }
        
        this.setHover = function(func) {
            hoverFunc = func;
        }
        
        this.setViewport = function(el) {
            viewport = el;
        }
        
        function inDOM() {
            return !(container == null);
        }
        
        function injectInDOM() {
            var t = this;
            var b = $('body');
            b.append('<div id="infoBubble" style="position: absolute"><div><div class="spike"></div><div class="top"><div class="bottom"><div class="content"></div></div></div></div></div>');
            container = b.find('#infoBubble');
            container.hover(hoverFunc, function() { $.infoBubble.hide(); });
            if (container.mousewheel) {
                container.mousewheel(function(e, delta){
                    e.preventDefault();
                    return false;
                });
            }
        }
    }

    $.fn.simplescroller = function() {
        var $this = this;
        var scrollbar = $this.find('div.scrollbar');
        var found = true;
        if (scrollbar.length == 0) {
            scrollbar = $('<div class="scrollbar"><div class="prev"></div><div class="scroller"><div class="bar"><div class="top"><div class="center"></div></div></div></div><div class="next"></div></div>');
            $this.append(scrollbar);
            found = false;
        }
        var scroller = scrollbar.find('div.scroller');
        var bar = scrollbar.find('div.bar');
        var next = scrollbar.find('div.next');
        var prev = scrollbar.find('div.prev');
        
        var uli = $this.find('ul');
        
        if (uli.outerHeight() < $this.height()) {
            scrollbar.hide(0);
        }
        else {
            scrollbar.show(0);
        }
        
        var h = scroller.height() * $this.height() / uli.outerHeight();
        if (h < 10) h = 10;
        if (h > scroller.height()) h = scroller.height();
        
        $this.css('position', 'relative');
        uli.css({ position: 'relative', top: 0 });
        bar.height(h).css({
            position: 'absolute',
            top: 0
        }).attr('sa', 0);
        
        if (found) return this;
        
        $this.bind('selectstart', function(){ return false; }); // ie: disable text selection
        if ($this.mousewheel) {
            $this.mousewheel(function(e, delta){
                if (delta < 0) {
                    next.trigger('click', [Math.abs(delta) * 15]);
                }
                else {
                    prev.trigger('click', [delta * 15]);
                }
                e.preventDefault();
                return false;
            });
        }
        next.click(function(e, step) {
            step = (step == null) ? 35 : step;
            var ntop = parseInt(bar.css('top')) + step;

            if (ntop + bar.height() > scroller.height())
                ntop = scroller.height() - bar.height();

            uli.css('top', -ntop * (uli.outerHeight() / scroller.height()));
            
            bar.css({
                top: ntop
            });
        });
        prev.click(function(e, step) {
            step = (step == null) ? 35 : step;
            var ntop = parseInt(bar.css('top')) - step;

            if (ntop < 0)
                    ntop = 0;
            
            uli.css('top', -ntop * (uli.outerHeight() / scroller.height()));
            
            bar.css({
                top: ntop
            });
        });
        bar.mousedown(function(e) {
            bar.attr('sa', e.pageY - bar.offset().top);
            $(document).mouseup(function() {
                $(document).unbind('mousemove').unbind('mouseup');
                return false;
            }).mousemove(function(e) {
                var ntop = 0;
                
                ntop = e.pageY - parseInt(bar.attr('sa')) - scroller.offset().top;
                if (ntop < 0)
                    ntop = 0;
                if (ntop + bar.height() > scroller.height())
                    ntop = scroller.height() - bar.height();
                
                uli.css('top', -ntop * (uli.outerHeight() / scroller.height()));
                
                bar.css({
                    top: ntop
                });
            });
            return false;
        });
        bar.click(function(e) { e.stopPropagation(); return false; });
        
        return this;
    }


})(jQuery)
