// depends on octopart.numbers.js, octopart-api.js

(function(){
    // add library to window
    if (window.search) return;
    else window.search = {};


    // variables
    var g_searchclient   = null;
    var g_filterwidgets  = {};
    var g_parametricbox  = null;
    var g_attrcache      = {};
    var g_searchsettings = {};

    
    // ------- overload submit method of search client ---------
    octopart.SearchClient.prototype._submit = octopart.SearchClient.prototype.submit;

    octopart.SearchClient.prototype.submit = function(args,callback){
	args = args || {};

	// save view
	args.view = $('li.ui-tabs-selected').children('a').text();

	if (args.history == false) {
	    delete args['history'];
	    do_search({'request':this.request,'params':args},callback);
	    return;
	}
	$.history.load('search2/'+JSON.stringify({'request':this.request,'params':args}));
    };


    // separate search function for history
    function do_search(args,callback) {
	args = args || {};

	request = args.request || {};
	params  = args.params || {};

	// default view
	if (!('view' in params))
	    params.view = 'list view';

	// results per page
	if (!('limit' in params))
	    params.limit = g_searchsettings['rpp_'+params.view.replace(' ','_')];

	// deal with pagenum argument
	if ('pagenum' in params) {
	    params.start = (request.start || 0) + params.limit * (params.pagenum-1);
	    delete params['pagenum'];
	}
	
	// default url arguments
	params['drilldown.facets.limit'] = 11;
	if (!('drilldown.include' in params)) params['drilldown.include'] = 1;

	
	// loading graphic
	if (params['limit'] != 0) {
            var tab = $('#tabs_wrapper');
            var overlay = $('<div/>')
            overlay.css({
		'background-color':'white',
		'position':'absolute',
		'top':tab.position().top,
		'left':tab.position().left-10,
		'height':tab.height(),
		'width':tab.width()+10,
		'z-index':100,
		'opacity':.9
            });
	    
	    
            var message = $('<div><img src="/static/parts/ajax-loader.gif" style="position:relative;top:3px;margin-right:10px;">Loadin\
g ...</div>');
            message.css({
		'background-color':'white',
		'position':'fixed',
		'top':tab.position().top+75,
		'left':tab.position().left+tab.width()/2-75,
		'color':'darkgray',
		'font-size':'15pt',
		'border':'2px solid darkgray',
		'text-align':'center',
		'padding':'15px 30px 18px 30px',
		'z-index':101
            });
	    
            if ($.browser.msie && parseInt($.browser.version) == 6) {
		message.css({
                    'position':'absolute',
                    'top':$(window).scrollTop()+tab.position().top+75
		});
            }
	    
	    $('body').append(overlay).append(message);
	}


	// handle callback
	callback = callback || function(response){updateUI(response,params.view);};

	// submit search
	g_searchclient.reset();
	g_searchclient.request = request;
	g_searchclient._submit(params,function(response){
	    callback(response);
	    if (overlay) overlay.remove();
	    if (message) message.remove();
	    $(window).scrollTop(0);
	});
	
    };


    // -------- utility functions ------
    function setupVendorpartToggle() {
	$('.vp_table').each(function() {
            var self = $(this);
            if ( self.find('tr').size() > 5 ) {
                self.find('tr:gt(4)').css('display','none');
                var more = $('<a style="text-decoration:underline;color:darkgreen;cursor:pointer;">more...</a>');
		more.toggle(function(){self.find('tr:gt(4)').show();$(this).text('less...');},
                            function(){self.find('tr:gt(4)').not('.donthide').hide();$(this).text('more...');}
                           );
                self.append($('<tr class="donthide"></tr>')
                            .append('<td>&nbsp;</td>')
                            .append($('<td colspan="4"></td>').append(more))
                           );
	    }
        });
    };

    
    function setupFacetToggle() {
        $('table.filterwidget').each(function(){
            var self = $(this);
            if (self.find('tr').size()>6) {
		$('tr:gt(5)',self).css('display','none');
                more = $('<a style="text-decoration:underline;cursor:pointer;">more...</a>');
                more.toggle(function(){$('tr:gt(5)',self).show();$(this).text('less...');},
                            function(){$('tr:gt(5)',self).not('.donthide').hide();$(this).text('more...');}
                           );
                self.append($('<tr class="donthide"></tr>')
                            .append('<td>&nbsp;</td>')
                            .append($('<td colspan="2"></td>').append(more))
                           );
            }
        });
    };



    // ---------------------------- DEFINE FilterWidgets ------------------------------------
    FilterWidget = function(args){
        args = args || {};
	this.attribute = args.attribute;
        this.missing   = args.missing;
        this.facets    = args.facets;

	// add to cache
	g_attrcache[args.attribute.fieldname] = args.attribute;

        // render
        this._table = this.build_table();
        $('div.filterwrapper div.body ul:first').append($('<li/>').append(this._table));
    };


    FilterWidget.prototype.build_table = function(){
        var table = $('<table class="filterwidget" border="0" cellspacing="0" cellpadding="0"></table>');

	var headerrow = $('<tr class="header"></tr>').appendTo(table);

        if ($.browser.msie)
            headerrow.append('<th style="padding-left:3px;width:13px;font-size:7pt;">▼</th>');
	else
            headerrow.append('<th style="padding-left:3px;width:13px;">▾</th>');

        headerrow.append('<th class="left">'+this.attribute.displayname+'</th>');
        headerrow.append('<th class="right">&nbsp;</th>');

        var max_count = 0
        for (x in this.facets) max_count = Math.max(this.facets[x].count,max_count);
        var max_count_length = max_count.toString().length

        var self = this;
        $.each(this.facets,function(i,facet){
            if (i>=10) return false;
            var row = $('<tr class="facet"/>');
            row.click(function(){
                if ($.browser.msie) {
                    row.unbind('mouseover');
                    $(this).css({'background-color':'','color':''});
                }

                self.select(facet.value);
                $('tr.facet:not(.selected)').css('color','#AAAAAA');
                $('tr.donthide').css('color','#AAAAAA');

                g_searchclient.addFilter(self.attribute.fieldname,facet.value);
                g_searchclient.submit({'drilldown.include':1})
            });
            row.append('<td>&nbsp;</td>');

            if (facet.value.length+max_count_length>22)
                row.append('<td class="left" value="'+facet.value+'" title="'+facet.value+'">'+facet.value.substring(0,22-max_count_length)+'...</td>');
            else
                row.append('<td class="left" value="'+facet.value+'">'+facet.value+'</td>');
            row.append('<td class="right">('+facet.count.format(',')+')</td>');

            // fix ie hover property
            if ($.browser.msie) row.hover(function(){$(this).css({'background-color':'yellowgreen','color':'white'});},function(){$(this).css({'background-color':'','color':''});});

            row.appendTo(table);
        });

        // hide 5-10
        if (this.facets.length>5) {
            $('tr:gt(5)',table).css('display','none');
            more = $('<a style="text-decoration:underline;cursor:pointer;">more...</a>');
            more.toggle(function(){$('tr:gt(5)',table).show();$(this).text('less...');},
                        function(){$('tr:gt(5)',table).not('.donthide').hide();$(this).text('more...');}
                       );
            table.append($('<tr class="donthide"></tr>')
                         .append('<td>&nbsp;</td>')
                         .append($('<td colspan="2"></td>').append(more))
                        );
        }

        return table;
    };


    FilterWidget.prototype.select = function(value){
        this._table.fadeIn('slow');

        $('tr.selected',this._table).removeClass('selected');
	$('td.left',this._table).each(function(i,val){
	    val = $(val);
	    if (val.attr('value') == value) val.parent().addClass('selected');
	});

        var row = $('tr.selected',this._table).addClass('selected');

        // hide other facets
        $('tr:not(.header):not(.selected)',this._table).remove();

        // make unclickable
        row.unbind('click');

        // add undo option
        var self = this;
        $('td.right',row).unbind('click').html($('<a style="font-size:8pt;">undo</a>').click(function(){
            g_searchclient.deleteFilter(self.attribute.fieldname,value);
            g_searchclient.submit();
            row.removeClass('selected').removeClass('facet');
        }));
    };


    FilterWidget.prototype.update = function(data){
        this._table.fadeIn('slow');

        this.facets  = data.facets;
        this.missing = data.missing;

        new_table = this.build_table();
        this._table.parent().append(new_table);
        this._table.remove();
        this._table = new_table;
    };


    FilterWidget.prototype.remove = function(data){
	this._table.parent().remove();
    };



    RangedFilterWidget = function(args){
        args = args || {};
	this.attribute = args.attribute;
        this.missing   = args.missing;
        this.min       = args.min;
        this.max       = args.max;
        this.count     = args.count;

	// add to cache
	g_attrcache[args.attribute.fieldname] = args.attribute;

        // render
        this._table = this.build_table();
        g_parametricbox.add_widget(this);
    };



    RangedFilterWidget.prototype.build_table = function(){
        var self = this;

        var div = $('<div class="rangedfilterwidget available"/>');
	div.data('attribute',this.attribute);

        var wrapper = $('<div/>').appendTo(div);

        var checkbox = $('<input class="checkbox" type="checkbox">').click(function(){
            if (this.checked) {
                div.addClass('active');
		g_searchclient.deleteFilter(self.attribute.fieldname);
		g_searchclient.addRangedFilter(self.attribute.fieldname,self.min,self.max);
            } else {
                div.removeClass('active');
                g_searchclient.deleteFilter(self.attribute.fieldname);
            }
            g_searchclient.submit();
        });
        wrapper.append( $('<span style="float:left;">&nbsp;<span class="label">'+this.attribute.displayname+' ('+this.count.format(',')+')</span></span>').prepend(checkbox) );
        wrapper.append( $('<span style="float:right;"/>').append( $('<a style="font-size:8pt;color:#003399;text-decoration:underline;cursor:pointer;">hide</a>').click(function(){
            if (checkbox.is(':checked')) {
                checkbox.attr('checked',false);
                checkbox.click();
                checkbox.attr('checked',false);
            }
            g_filterwidgets[self.attribute.fieldname].remove();
        })));
        wrapper.append('<div class="g_clear"/>');


	if (!self.attribute.metadata.unit)
	    wrapper.append('<div class="values">'+self.min+' - '+self.max+'</div>');
        else if (self.attribute.metadata.unit.symbol == '$')
	    wrapper.append('<div class="values">'+self.min.format('$0,000.00')+' - '+self.max.format('$0,000.00')+'</div>');
        else
	    wrapper.append('<div class="values">'+self.min.format_unit(self.attribute.metadata.unit.symbol)+' - '+self.max.format_unit(self.attribute.metadata.unit.symbol)+'</div>');



	if (self.attribute.metadata.datatype == 'integer') {
	    var slider = $('<div style="margin:2px 12px 10px 10px;background-color:#EEEEEE;"/>').slider({
		range: true,
		min: self.min,
		max: self.max,
		values: [self.min,self.max],
		step: Math.max(1, parseInt((self.max-self.min)/100)),
		slide: function(event,ui) {
                    // using log scale
                    var min = ui.values[0];
                    var max = ui.values[1];
		    $('.values',div).text(min+' - '+max);
		},
		change: function(event,ui) {
                    checkbox.attr('checked',true);
                    self._table.addClass('active');
                    var min = ui.values[0];
                    var max = ui.values[1];
		    g_searchclient.deleteFilter(self.attribute.fieldname);
                    g_searchclient.addRangedFilter(self.attribute.fieldname,min,max);
                    g_searchclient.submit();
		}
            });

	} else {
	    function sliderVal(val) {
		if (self.min==0 && (self.max-self.min)>1000) {
                    // use log scale but handle 0 as special case
                    if (val==0) return 0;

                    var minv = Math.log( Math.pow(10,-6) );
                    var maxv = Math.log(self.max);
                    var scale = (maxv-minv)/100;
                    return Math.exp( minv + scale*val );
		}
		else if (self.min > 0) {
                    var minv = Math.log(self.min);
                    var maxv = Math.log(self.max);
                    var scale = (maxv-minv)/100;
                    return Math.exp( minv + scale*val );
		}
		else {
                    // use linear scale
                    var minv = self.min;
                    var maxv = self.max;
                    var scale = (maxv-minv)/100;
                    return minv+scale*val;
		}

            };


	    function round(val) {
		var exponent = 3*Math.floor(Math.log(Math.abs(val))/Math.log(10)/3);
		if (val==0) exponent=0;
		var reduced = Math.round(val/Math.pow(10,exponent)*10)/10;
		return reduced*Math.pow(10,exponent);
	    };


	    var slider = $('<div style="margin:2px 12px 10px 10px;background-color:#EEEEEE;"/>').slider({
		range: true,
		min: 0,
		max: 100,
		values: [0,100],
		step: .1,
		slide: function(event,ui) {
                    // using log scale
                    var min = sliderVal(ui.values[0]);
                    var max = sliderVal(ui.values[1]);
                    if (self.attribute.metadata.unit.symbol == '$')
			$('.values',div).text(min.format('$0,000.00')+' - '+max.format('$0,000.00'));
                    else
			$('.values',div).text(min.format_unit(self.attribute.metadata.unit.symbol)+' - '+max.format_unit(self.attribute.metadata.unit.symbol));
		    
		},
		change: function(event,ui) {
                    checkbox.attr('checked',true);
                    self._table.addClass('active');
                    // using log scale
                    var min = round(sliderVal(ui.values[0]));
                    var max = round(sliderVal(ui.values[1]));
		    g_searchclient.deleteFilter(self.attribute.fieldname);
                    g_searchclient.addRangedFilter(self.attribute.fieldname,min,max);
                    g_searchclient.submit();
		}
            });
	} // endif
	
	
        div.append(slider);
        return div
    };


    RangedFilterWidget.prototype.apply = function(){
        this._table.fadeIn('slow');
        var checkbox = $('input.checkbox',this._table);
        if (!checkbox.is(':checked')) {
            checkbox.attr('checked',true);
            checkbox.click();
            checkbox.attr('checked',true);
        }
    };


    RangedFilterWidget.prototype.select = function(data,hits){
        this._table.addClass('available');
        this._table.fadeIn('slow');

        this.count   = hits;
        $('span.label',this._table).html(this.attribute.displayname+' ('+hits.format(',')+')');

        $('input.checkbox',this._table).attr('checked',true);
    };


    RangedFilterWidget.prototype.update = function(data){
        this._table.addClass('available');

        this.missing = 1-data.coverage;
        this.count   = data.bins[0].count;
        $('span.label',this._table).html(this.attribute.displayname+' ('+this.count.format(',')+')');

        this.min = data.bins[0].min;
        this.max = data.bins[0].max;

        if (!this.attribute.metadata.unit)
	    $('div.values',this._table).text(this.min+' - '+this.max);
	else if (this.attribute.metadata.unit.symbol == '$')
	    $('div.values',this._table).text(this.min.format('$0,000.00')+' - '+this.max.format('$0,000.00'));
        else
            $('div.values',this._table).text(this.min.format_unit(this.attribute.metadata.unit.symbol)+' - '+this.max.format_unit(this.attribute.metadata.unit.symbol));

    };


    RangedFilterWidget.prototype.remove = function(){
	this._table.remove();
    };


    RangedFilterWidget.prototype.show = function(){
        this._table.fadeIn('slow');
    };



    // ----------------------------- define ParametricSearchBox ------------------------------
    function ParametricSearchBox() {
        this._body      = null;
        this._select    = null;
        this._options   = [];
        this._is_virgin = true;

        var div = $('<div class="filterwrapper"></div>');

        var self = this;
        this._select = $('<select><option>apply filter...</option></select>').change(function(){
            self.apply_filter($(this).find(':selected').val());
            $('option:first',self._select).attr('selected',true);
        });

        var span = $('<span><span style="float:left;">Parametric Search</span><span style="float:left;font-size:7pt;margin-left:3px;">►</span></span>').css('cursor','pointer');

        div.append( $('<div class="header"/>').append(span).append( $('<span style="float:right;"/>').append(this._select) ).append('<div class="g_clear"/>') );

        this._body = $('<div class="body"><div class="top"/><div class="allfilters"/></div>').css('display','none').appendTo(div);

        $('div.allfilters',this._body).append( $('<a>show all »</a>').toggle(
            function(){
                $('div.rangedfilterwidget.available:hidden',self._body).addClass('showmore').show();
                $(this).text('« undo');
            },
            function(){
                $('div.rangedfilterwidget.available.showmore:not(.active)',self._body).hide();
                $('div.rangedfilterwidget').removeClass('showmore');
                $(this).text('show all »');
		$(window).scrollTop(0);
            }
        ));


        span.toggle(
            function(){
                $('span:last',this).text('▼');
                self._body.show();
            },function(){
                $('span:last',this).text('►');
                self._body.hide();
            });
        this._span = span;

        $('td.resultscolumn div.wrapper').prepend(div);
    };


    ParametricSearchBox.prototype.apply_filter = function(fieldname){
        this._is_virgin = false;
        if ($(this._body).is(':visible') == false) this._span.click();
        g_filterwidgets[fieldname].apply();
    };


    ParametricSearchBox.prototype.add_widget = function(widget){
        $('div.top',this._body).append( widget._table.hide() );
    };


    ParametricSearchBox.prototype.update_select = function(){
        // update select box
        var self = this;
        var navail = 0;
        $('option:not(:first)',self._select).remove();
        $('div.rangedfilterwidget.available').each(function(i,obj){
            obj = $(obj);
            self._select.append('<option value="'+obj.data('attribute').fieldname+'">'+obj.data('attribute').displayname+'</option>');
            navail++;
        });

        // update "show all"
        if (navail) $('div.allfilters').show();
        else $('div.allfilters').hide();

        if ($('div.rangedfilterwidget.available input:checked').length && this._body.is(':hidden') && this._is_virgin) {
            this._span.click();
            this._is_virgin = false;
        }
    };



    // -------------------------------- HTML generators ---------------------------
    function generateNoResultsHTML(response){
	var div = $('<div class="noresults"/>');
	
	div.append("<p>Sorry, we didn't find any matches.</p><p>Search Suggestions:</p>");

	var ul = $('<ul/>');
	if (response.request.filters.length || response.request.rangedfilters.length)
	    $('<li>Try </li>').append($('<a class="removefilters">removing filters</a>').click(function(){
		g_searchclient.reset();
		g_searchclient.setQueryString(response.request.q);
		g_searchclient.submit();

		// remove category div
		$('div.category_header').remove();
	    })).appendTo(ul);
	$('<li>Wildcards(*) for part number searches</li>').appendTo(ul);
	$('<li>Try a different <a href="/searchsyntax" class="unvisited">search syntax</a></li>').appendTo(ul);
	$('<li>Search on <a href="http://google.com/search?q='+encodeURI(response.request.q)+'" class="unvisited">Google</a>, <a href="http://www.findchips.com/avail?part='+encodeURI(response.request.q)+'" class="unvisited">FindChips</a>, <a href="http://www.alldatasheet.com/view.jsp?Searchword='+encodeURI(response.request.q)+'" class="unvisited">AllDataSheet</a>, <a href="http://www.oemstrade.com/results.cfm?searchText='+encodeURI(response.request.q)+'" class="unvisited">OEMsTrade</a></li>').appendTo(ul);
	$('<li><a href="mailto:contact@octopart.com">Help us</a> find the manufacturer of this product</a></li>').appendTo(ul);
	ul.appendTo(div);

	return div;
    };



    function generateListViewHTML(response){
        wrapper = $('<div/>');

        sortbydiv = $('<div class="sortby"/>').appendTo(wrapper);

        fields = $('<div class="fields">sort by: </div>').appendTo(sortbydiv);
        if (response.request.sortby[0][0] == 'score') fields.append('<b>relevance</b>');
        else fields.append($('<a title="sort by relevance">relevance</a>').click(function(){
            g_searchclient.setSortBy([['score','desc']]);
            g_searchclient.submit();
        }));
        fields.append(' | ');
        if (response.request.sortby[0][0] == 'avg_price') fields.append('<b>price</b>');
        else fields.append($('<a title="sort by average price">price</a>').click(function(){
            g_searchclient.setSortBy([['avg_price','asc']]);
            g_searchclient.submit();
        }));
        fields.append(' | ');
        if (response.request.sortby[0][0] == 'avg_avail') fields.append('<b>avail</b>');
        else fields.append($('<a title="sort by average availability">avail</a>').click(function(){
            g_searchclient.setSortBy([['avg_avail','desc']]);
            g_searchclient.submit();
        }));

	
        $.each(response.request.rangedfilters, function(i,filter){
	    var fieldname = filter[0];
	    var value = filter[1][0];

            fields.append(' | ');
            if (response.request.sortby[0][0] == fieldname) fields.append('<b>'+g_attrcache[fieldname].displayname+'</b>');
            else if ($.inArray(fieldname,['avg_price','avg_avail']) == -1) {
                fields.append($('<a title="sort by '+g_attrcache[fieldname].displayname+'">'+g_attrcache[fieldname].displayname.toLowerCase()+'</a>').click(function(){
                    g_searchclient.setSortBy([[fieldname,'desc']]);
                    g_searchclient.submit();
                }));
            }
        });

        order = $('<div class="order"/>').appendTo(sortbydiv);
        if (response.request.sortby.length && response.request.sortby[0][0] != 'score') {
            order.append('( ');
            if (response.request.sortby[0][1] == 'asc') {
                order.append('<b>ascending</b> | ');
                order.append($('<a title="order by descending">descending</a>').click(function(){
                    g_searchclient.setSortBy([[response.request.sortby[0][0],'desc']]);
                    g_searchclient.submit();
                }));
            } else {
                order.append($('<a title="order by ascending">ascending</a>').click(function(){
                    g_searchclient.setSortBy([[response.request.sortby[0][0],'asc']]);
                    g_searchclient.submit();
                }));
                order.append(' | <b>descending</b>');
            }
            order.append(' )');
        }

        sortbydiv.append('<div class="g_clear"/>');

	// add parts
	for (x in response.results) {
	    var part = response.results[x].item;
	    var highlight = response.results[x].highlight;

	    var div = $('<div class="part"/>');
	    var table = $('<table cellspacing="0" cellpadding="0" border="0"><tr valign="top"></tr></table>');

	    // --- left_col ---
	    var left_col = $('<td class="left_col" nowrap/>');
	    
	    // -- title --
	    var title = $('<div><a class="title" href="'+part.detail_url+'">'+part.manufacturer.displayname+' - '+part.mpn+'</a></div>');
	    if (part.datasheets.length) title.append('<a class="datasheet" href="'+part.datasheets[0].url+'" target="_blank">datasheet: pdf<img src="http://cdn.static.octopart.com/global/pdf_small.f664c524810d201b1eb2ff1b29c2f53b.jpg"></a>');
	    title.appendTo(left_col);

	    // -- highlight --
	    if (highlight) $('<div class="snippet"/>').html(highlight).appendTo(left_col);
	    
	    // -- vendorpart table --
	    var vp_table = $('<table class="vp_table" cellspacing="0" cellpadding="0" border="0">');
	    for (i in part.offers) {
		var offer = part.offers[i];
		var row = $('<tr/>');
		
		// auth
		var td_auth = $('<td class="auth"/>').appendTo(row);
		if (offer.is_authorized) td_auth.text('*');
		
		// vendor
		$('<td class="vendor"><a rel="nofollow" href="'+offer.supplier.homepage_url+'">'+offer.supplier.displayname+'</a></td>').appendTo(row);
		
		// sku
		$('<td class="sku"><a rel="nofollow" href="'+offer.clickthrough_url+'">'+offer.sku+'</a></td>').appendTo(row);

		// price
		var td_price = $('<td class="price"/>');
		if (offer.prices.length) {
		    if (offer.prices[0][1] < .01)
			td_price.text(offer.prices[0][1].format('$0,000.000'));
		    else
			td_price.text(offer.prices[0][1].format('$0,000.00'));
		}
		td_price.appendTo(row)

		// avail
		var td_avail = $('<td class="avail">avail:</td>');
		if (offer.avail == -4) td_avail.append('RFQ');
		else if (offer.avail == -3 || offer.avail == -1) td_avail.append('n/s');
		else if (offer.avail == -2) td_avail.append('yes');
		else td_avail.append(offer.avail);
		td_avail.appendTo(row)

		// buynow
		var td_purchase = $('<td class="purchase" nowrap/>');
		if (offer.buynow_url) td_purchase.append('<a rel="nofollow" href="'+offer.buynow_url+'">Buy Now</a>');
		else if (offer.sendrfq_url) td_purchase.append('<a href="'+offer.sendrfq_url+'">Send RFQ</a>');
		else td_purchase.append('&nbsp;');
		td_purchase.appendTo(row)

		row.appendTo(vp_table);
	    }
	    vp_table.appendTo(left_col);

	    // -- part actions --
	    $('<div class="actions" ppid="'+part.id+'"><span>*</span><a href="/authorized">authorized</a></div>').appendTo(left_col);


	    // --- right_col ---
	    var right_col = $('<td class="right_col"/>');
	    
	    if (part.images.length) {
		var image = part.images[0];
		$('<div class="imagewrapper"><a target="_blank" href="'+image.url+'"><img src="'+image.url_55px+'"></a></div>').appendTo(right_col);
		$('<div><a class="imagecredit" rel="nofollow" href="'+image.credit_url+'">'+image.credit_domain+'</a></div>').appendTo(right_col);
	    }

	    $('tr',table).append(left_col).append(right_col);
	    table.appendTo(div);
	    div.appendTo(wrapper);
	}

        return wrapper;
    };



    function generateMatrixViewHTML(response){
        wrapper = $('<div/>');

        table = $('<table class="partmatrix" border="0" cellspacing="0" cellpadding="0"/>').appendTo(wrapper);
        thead = $('<thead/>').appendTo(table);

        row = $('<tr/>').appendTo(thead);


        th = $('<th style="text-align:center;"/>').appendTo(row);
        if (response.request.sortby[0][0] == 'score') {
            th.addClass('selected').append('part');
        } else {
            th.append($('<a>part</a>').click(function(){
                g_searchclient.setSortBy([['score','desc']]);
                g_searchclient.submit();
            }));
        }


	function compareArrays(array1,array2) {
	    if (array1.length != array2.length) return false
	    for (i in array1) if (array1[i] != array2[i]) return false
	    return true
	};


        th = $('<th class="header" title="sort by average price"><a>price</a></th>').appendTo(row);
        if (compareArrays(response.request.sortby[0],['avg_price','asc'])) {
            th.addClass('selected headerSortUp').children('a').click(function(){
                g_searchclient.setSortBy([['avg_price','desc']]);
                g_searchclient.submit();
            });
        } else if (compareArrays(response.request.sortby[0],['avg_price','desc'])) {
            th.addClass('selected headerSortDown').children('a').click(function(){
                g_searchclient.setSortBy([['avg_price','asc']]);
                g_searchclient.submit();
            });
        } else {
            th.children('a').click(function(){
                g_searchclient.setSortBy([['avg_price','asc']]);
                g_searchclient.submit();
            });
        }

        th = $('<th class="header" title="sort by average availability"><a>avail</a></th>').appendTo(row);
        if (compareArrays(response.request.sortby[0],['avg_avail','asc'])) {
            th.addClass('selected headerSortUp').children('a').click(function(){
                g_searchclient.setSortBy([['avg_avail','desc']]);
                g_searchclient.submit();
            });
        } else if (compareArrays(response.request.sortby[0],['avg_avail','desc'])) {
            th.addClass('selected headerSortDown').children('a').click(function(){
                g_searchclient.setSortBy([['avg_avail','asc']]);
                g_searchclient.submit();
            });
        } else {
            th.children('a').click(function(){
                g_searchclient.setSortBy([['avg_avail','desc']]);
                g_searchclient.submit();
            });
        }

        th = $('<th class="header" title="sort by number of suppliers"><a>suppliers</a></th>').appendTo(row);
        if (compareArrays(response.request.sortby[0],['num_suppliers','asc'])) {
            th.addClass('selected headerSortUp').children('a').click(function(){
                g_searchclient.setSortBy([['num_suppliers','desc']]);
                g_searchclient.submit();
            });
        } else if (compareArrays(response.request.sortby[0],['num_suppliers','desc'])) {
            th.addClass('selected headerSortDown').children('a').click(function(){
                g_searchclient.setSortBy([['num_suppliers','asc']]);
                g_searchclient.submit();
            });
        } else {
            th.children('a').click(function(){
                g_searchclient.setSortBy([['num_suppliers','desc']]);
                g_searchclient.submit();
            });
        }

        $.each(response.request.rangedfilters,function(i,filter){
	    var fieldname = filter[0];
	    var value = filter[1][0];
	    
            if ($.inArray(fieldname, ['avg_price','avg_avail','num_suppliers']) != -1) return true;

            th = $('<th class="header" title="sort by '+g_attrcache[fieldname].displayname+'"><a>'+g_attrcache[fieldname].displayname.toLowerCase()+'</a></th>').appendTo(row);

            if (compareArrays(response.request.sortby[0],[fieldname,'asc'])) {
                th.addClass('selected headerSortUp').children('a').click(function(){
                    g_searchclient.setSortBy([[fieldname,'desc']]);
                    g_searchclient.submit();
                });
            } else if (compareArrays(response.request.sortby[0],[fieldname,'desc'])) {
                th.addClass('selected headerSortDown').children('a').click(function(){
                    g_searchclient.setSortBy([[fieldname,'asc']]);
                    g_searchclient.submit();
                });
            } else {
                th.children('a').click(function(){
                    g_searchclient.setSortBy([[fieldname,'desc']]);
                    g_searchclient.submit();
                });
            }
        });


        tbody = $('<tbody/>').appendTo(table);	


	// add parts html
	for (x in response.results) {
	    var part = response.results[x].item;
	    
	    var row = $('<tr valign="top"/>').appendTo(tbody);

	    // info
	    var col = $('<td class="part"/>').appendTo(row);
	    var info_table = $('<table cellspacing="0" cellpadding="0" border="0"><tr valign="top"></tr></table>').appendTo(col);

	    var info_col1 = $('<td class="image_col"><div class="imagewrapper"></div></td>');
	    if (part.images.length)
		$('div.imagewrapper',info_col1).append('<a href="'+part.images[0].url+'" target="_blank"><img src="'+part.images[0].url_30px+'"></a>');
	    
	    var info_col2 = $('<td nowrap/>');
	    info_col2.append('<div class="title"><a href="'+part.detail_url+'" target="_blank">'+part.manufacturer.displayname+' - '+part.mpn+'</a></div>');
	    info_col2.append('<div class="actions" ppid="'+part.id+'"></div>')
	    if (part.datasheets.length)
		$('div.actions',info_col2).append('<a href="'+part.datasheets[0].url+'" target="_blank">datasheeet: pdf</a>');
	    
	    $('tr',info_table).append(info_col1).append(info_col2);

	    // avg_price
	    var data_col = $('<td class="data"/>').appendTo(row);
	    if (part.avg_price) {
		if (part.avg_price[0]<.01) data_col.text(part.avg_price[0].format('$0,000.000'));
		else data_col.text(part.avg_price[0].format('$0,000.00'));
	    }
	    if (response.request.sortby[0][0] == 'avg_price') data_col.addClass('sorted');

	    // avg_avail
	    var data_col = $('<td class="data"/>').appendTo(row);
	    if (part.avg_avail != null) data_col.text(part.avg_avail.format('0,000'));
	    if (response.request.sortby[0][0] == 'avg_avail') data_col.addClass('sorted');

	    // num_suppliers
	    var data_col = $('<td class="data"/>').text(part.num_suppliers).appendTo(row);
	    if (response.request.sortby[0][0] == 'num_suppliers') data_col.addClass('sorted');

	    // other attributes
	    $.each(response.request.rangedfilters, function(i,filter){
		var fieldname = filter[0];
		var attr = g_attrcache[fieldname];

		var data_col = $('<td class="data"/>').appendTo(row);
		
		values = part.getAttributeValues(fieldname);
		if (values) {
		    var value = values[0];
		    if (attr.metadata.datatype == 'float') value = value.value;
		    
		    if (attr.metadata.unit)
			data_col.append( value.format_unit(attr.metadata.unit.symbol) );
		    else
			data_col.append( value );
		}

		if (response.request.sortby[0][0] == fieldname) data_col.addClass('sorted');
	    });

	}

        return wrapper;
    };

    

    // -------------------------------- UI initialization functions ------------------------------------------
    function setupUI(querystring) {
	// make sure searchbox has querystring
        $('div.search form input.searchbox').val(querystring);

        // overload searchbox
        $('div.search form').submit(function(){
	    g_searchclient.setQueryString($('input.searchbox',$(this)).val());
	    g_searchclient.submit();
            return false;
        });

	// get rid of "search within option"
	$('#searchform div.within').remove();

        // overload tab selectors
        $('ul.ui-tabs-nav li').click(function(){
            var self = $(this);
            if (self.hasClass('ui-tabs-selected')) return false;
            $('li.ui-tabs-selected',self.parent()).removeClass('ui-tabs-selected');
            self.addClass('ui-tabs-selected');

            g_searchclient.submit();
            return false;
        });

        // overload sorting actions
        var do_sort = function(url){
            var sort = url.split('s=')[1];
            if (sort) {
		if (sort.indexOf('_asc') != -1) sort = [[sort.split('_asc')[0],'asc']]
		else sort = [[sort.split('_desc')[0],'desc']]
	    } else
		sort = [['score','desc']];
            g_searchclient.setSortBy(sort);
            g_searchclient.submit();
        }
        $('div.sortby a').click(function(){do_sort(this.href);return false;});
        $('table.partmatrix th a').click(function(){do_sort(this.href);return false;});

        // overload page selectors
        var do_page = function(url){
            g_searchclient.submit({
		'pagenum' : parseInt(url.split('p=')[1])
            });
        }
        $('#tabs_div td.pages a').click(function(){do_page(this.href);return false;});
        $('#pagerange a').click(function(){do_page(this.href);return false;});

        // add parametric search box
        g_parametricbox = new ParametricSearchBox();
    };


    function updateUI(response,view) {

	// only update part data if parts were requested
	if (response.request.limit) {
            // update title
            if (response.request.q.length==0)
		document.title = 'Part Search - Octopart';
            else
		document.title = response.request.q+' - Octopart';
	    
            // update searchbox
            $('div.search form input.searchbox').val(response.request.q);
	    
            // update search summary
            div = $('div.search_summary .hits');
            n_start = 0; n_end = 0;
            if (response.hits>0) {
		n_start = response.request.start+1;
		n_end = response.request.start+response.results.length;
            }
            summary = 'Results '+n_start+'-'+n_end+' of '+response.hits.format(',');
            if (response.request.q.length==0) div.text(summary+':');
            else div.html(summary+' for ').append($('<b/>').text(response.request.q)).append(':');
	    
            $('div.search_summary .totaltime').html('('+response.time.toFixed(2)+' sec.)');
	    
            // select tab
            $('ul.ui-tabs-nav li').removeClass('ui-tabs-selected');
            $('ul.ui-tabs-nav li:contains("'+view+'")').addClass('ui-tabs-selected');
	    
            // update search results
            $('div.tab').removeClass('selected');
            tab = $('#'+view.replace(' ','_')).addClass('selected');
	    
            // update no filters message
            if (response.numresults==0) {
		if (!$('div.nofilters').length)
                    $('div.filterwrapper div.body').prepend('<div class="nofilters"><i>No filters</i></div>');
            } else {
		$('div.nofilters').remove();
            }
	    

	    if (response.hits==0) tab.html(generateNoResultsHTML(response));
            else if (view == 'list view') tab.html(generateListViewHTML(response));
            else if (view == 'matrix view') tab.html(generateMatrixViewHTML(response));
	    
	    
            setupVendorpartToggle();
	    OJ_.setup_techchat();
	    
	    // TODO: saved parts
            $('div.actions').projectmanager_search_markup([]);
	    
	    
            // update page selectors
            var div = $('#pagerange').html('');
            var td = $('#tabs_div td.pages');
            $('a',td).remove();
	    
            if (response.request.start>=response.request.limit) {
		div.append($('<a class="g_unvisited prev">&lt; Previous</a>').click(function(){
                    g_searchclient.submit({'start': response.request.start-response.request.limit});
		}));
		td.append($('<a class="g_unvisited">&lt; Previous</a>').click(function(){
                    g_searchclient.submit({'start':response.request.start-response.request.limit});
		}));
            }
	    
	    // function to build buffered range list
	    function brange(main_num, max_val, len_buffer) {
		var num_max = Math.min( max_val, Math.max(len_buffer,main_num+len_buffer/2-1));
		var num_min = Math.max( 1, main_num-len_buffer, num_max-len_buffer+1);
		
		var brange = []
		for (var i=num_min;i<=num_max;i++) brange.push(i); 
		return brange.slice(0,len_buffer);
	    };
	    
	    var this_pagenum = (parseInt(response.request.start/response.request.limit))+1;
	    var max_pagenum  = parseInt(response.hits/response.request.limit);
	    if (response.hits%response.request.limit>0) max_pagenum++;
	    
	    // handle search engine limit (1000 max results)
	    if (response.request.limit>0 && max_pagenum*response.request.limit>1000)
		max_pagenum = parseInt(1000/response.request.limit);

            $.each(brange(this_pagenum,max_pagenum,10), function(i,val){
		if (val == this_pagenum) {
                    div.append($('<span/>').text(val));
		} else {
                    div.append($('<a class="g_unvisited"/>').text(val).click(function(){
			g_searchclient.submit({'start':response.request.limit*(val-1)});
                    }));
		}
		div.append(' ');
            });
	    
            if (this_pagenum < max_pagenum) {
		div.append($('<a class="g_unvisited next">Next &gt;</a>').click(function(){
                    g_searchclient.submit({'start':response.request.start+response.request.limit});
		}));
		td.append('&nbsp;&nbsp;&nbsp;').append($('<a class="g_unvisited">Next &gt;</a>').click(function(){
                    g_searchclient.submit({'start':response.request.start+response.request.limit});
		}));
            }
	}
	

        // only update filters if drilldown was sent back
        if (response.drilldown) {
            // update filter widgets
            // set all ranged filters to unavailable
            $('div.rangedfilterwidget').removeClass('available');

            // iterate through selected filters
            var selected_keys = new Array;
            $.each(response.request.filters, function(i,filter){
		var fieldname = filter[0];
		var value = filter[1][0];
                if (!(fieldname in g_filterwidgets)) return;
                g_filterwidgets[fieldname].select(value);
                selected_keys.push(fieldname);
            });
            $.each(response.request.rangedfilters,function(i,filter){
		var fieldname = filter[0];
		var value = filter[1][0];
                if (!(fieldname in g_filterwidgets)) return;
                g_filterwidgets[fieldname].select(value,response.hits);
                selected_keys.push(fieldname);
            });


            // get available filters
            var available_filters = {};
            $.each(response.drilldown, function(i,data){available_filters[data.attribute.fieldname]=data;});

            // get rid of obsolete filters
            $.each(g_filterwidgets, function(fieldname,widget){
                //if (fieldname in available_filters) return;
                if ($.inArray(fieldname, selected_keys) != -1) return;
		widget.remove();
		delete g_filterwidgets[fieldname];
            });
	    

            // sort available filters
            var sorted_filter_array = new Array;
            for (x in available_filters) sorted_filter_array.push(available_filters[x]);
            sorted_filter_array.sort(function(x,y){return y.coverage-x.coverage;});

            // add new widgets / update existing
            $.each(sorted_filter_array,function(i,data){
                if ($.inArray(data.attribute.fieldname, selected_keys) != -1) return;

                if (data.attribute.fieldname in g_filterwidgets) {
                    g_filterwidgets[data.attribute.fieldname].update(data);
                } else {
                    if (data.facets) g_filterwidgets[data.attribute.fieldname] = new FilterWidget({'attribute':data.attribute, 'missing':1-data.coverage, 'facets':data.facets});
                    else g_filterwidgets[data.attribute.fieldname] = new RangedFilterWidget({'attribute':data.attribute, 'missing':1-data.coverage, 'min':data.bins[0].min, 'max':data.bins[0].max, 'count':data.bins[0].count});

                    if ( $.inArray(data.attribute.fieldname, selected_keys) != -1 )
                        g_filterwidgets[data.attribute.fieldname].select(data,response.hits)
                }
            });
        }

        // update parametric search box
        g_parametricbox.update_select();

        OJ_.flash({'msg':'results updated','timeout':1500});
    }



    function renderInitialFilters(){
        // get and render filters
        $('div.filterwrapper div.body').append('<div class="loading" style="text-align:center;color:dimgray;font-size:11pt;padding:30px 0px;"><img src="/static/parts/ajax-loader-2.gif">&nbsp;&nbsp;Loading...</div>');
	
        g_searchclient.submit({'limit':0,'drilldown.include':1,'history':false}, function(response){
            $('div.filterwrapper div.body div.loading').remove();
	    
	    var widgetdata = new Array();
	    for (x in response.drilldown) {
		var data = response.drilldown[x];
		if (data.facets) widgetdata.push( {'attribute':data.attribute, 'missing':1-data.coverage, 'facets':data.facets} );
		else if (data.bins && data.bins.length) widgetdata.push( {'attribute':data.attribute, 'missing':1-data.coverage, 'min':data.bins[0].min, 'max':data.bins[0].max, 'count':data.bins[0].count} );
	    }
            widgetdata.sort(function(x,y){return x.missing-y.missing;});
	    
	    var applied_filters = {};
	    for (x in response.request.filters) applied_filters[response.request.filters[x][0]] = response.request.filters[x][1];

            $.each(widgetdata,function(i,data){
                if (data.facets) var f = new FilterWidget(data);
                else var f = new RangedFilterWidget(data);
                if (data.attribute.fieldname in applied_filters) f.select(applied_filters[data.attribute.fieldname],response.hits);
                g_filterwidgets[data.attribute.fieldname] = f;
            });
            g_parametricbox.update_select();
        });
    };



    function overloadExistingFilters(){
	// iterate through filters
        $('table.filterwidget').each(function(n,this_table){
            var self = $(this);
            if (self.attr('filter_type') == 'facet') {
                // todo: handle selected
                var data = {};
		data['attribute'] = eval('('+self.attr('attribute')+')');
                data['missing']     = self.attr('missing');
                data['facets'] = [];
                $('tr:not(.header):not(.donthide)',self).each(function(i,this_row){
                    this_row = $(this_row);
                    var facet = {}
                    facet.value = $.trim($('td.left',this_row).text());
		    var count   = $('td.right',this_row).text();
                    facet.count = parseInt(count.replace(',','').substring(1,count.length-1));
                    data['facets'].push(facet);
                });
                g_filterwidgets[data.attribute.fieldname] = new FilterWidget(data);
                var selected = $('tr.selected',self);
                if (selected.length) g_filterwidgets[data.attribute.fieldname].select(data['facets'][0].value);
                $(this_table).parent().remove();
            } else if (self.attr('filter_type') == 'ranged') {
		var data = {}
		data['attribute']   = eval('('+self.attr('attribute')+')');
                data['missing']     = parseInt(self.attr('missing'));
                data['min']         = parseFloat(self.attr('min'));
                data['max']         = parseFloat(self.attr('max'));
                data['count']       = parseInt(self.attr('count'));
                g_filterwidgets[data.attribute.fieldname] = new RangedFilterWidget(data);
		var selected = $('tr.selected',self);
		if (selected.length) g_filterwidgets[data.attribute.fieldname].select([data['min'],data['max']],data.count);
		
                $(this_table).parent().remove();
            }
        });
        g_parametricbox.update_select();
    };


    
    // ------------------------------------- INITIALIZATION ROUTINE ----------------------------------------
    function init(args) {
	args = args || {};
	g_searchclient = new octopart.SearchClient(args.request);

	var original_request = $.extend({},args.request);

	setupUI(original_request.q);
	
	var initial_request = true;

	if (args.js == 'on') renderInitialFilters();
	else overloadExistingFilters();

	g_searchsettings = args.search_settings;

	// handle back-forward-reload events
	function callback(hash){
	    if ( !hash && !initial_request ) {
                $('div.filterwrapper div.body div.loading').remove();
                g_searchclient.reset();
		g_searchclient.request = original_request;
                g_searchclient.submit({'start':original_request.start,'limit':original_request.limit,'history':false});
            } else if (hash.substring(0,8) == "search2/") {
                do_search(JSON.parse(hash.substring(8,hash.length)));
            } else if (hash.substring(0,7) == "search/") {
		// handle old fragments -- do redirect to original search
		window.location.href = window.location.href.split('#')[0];
            }

            initial_request = false;
        };

        $.history.init(callback);

    };

    
    // attach classes and methods to search library
    search.init                  = init;
    search.setupVendorpartToggle = setupVendorpartToggle;
    search.setupFacetToggle      = setupFacetToggle;
})();
