/*
 * search.js
 *
 * Apache Lucene Solr client
 */

if( typeof CAYMANCHEM === 'undefined') CAYMANCHEM = {};

CAYMANCHEM.search = {
    /** serial number for AJAX queries */
    serial: 0,

    /** placeholders for blank fields */
    blank: {
        text: '-',
        image: 'images/fish.png',
        dummy: true
    },

    /** Character for score indicator bar */
    score: '\u2589',
    
    /**  */
    fields : [
        {
            name: 'catalog_num',
            prefix: true,
            suffix: false
        },
        {
            name: 'supplier_catalog_num',
            prefix: true,
            suffix: false
        },
        {
            name: 'discontinued_id',
            prefix: true,
            suffix: false
        },
        {
            name: 'cas_no',
            prefix: true,
            suffix: false
        },
        {
            name: 'name',
            prefix: true,
            suffix: false,
            boost: 2
        },
        {
            name: 'tagline',
            prefix: false,
            suffix: false
        },
        {
            name: 'marketing_notes',
            prefix: false,
            suffix: false
        },
        {
            name: 'synonym',
            prefix: false,
            suffix: false
        },
        {
            name: 'keywords',
            prefix: false,
            suffix: false
        },
        {
            name: 'product_qualifier_id',
            prefix: false,
            suffix: false
        },
        {
            name: 'sku',
            prefix: false,
            suffix: false
        }
    ],

    /** default data parameters for Apache Solr search */
    defaults: {
        bf:'recip(rord(introduction_date),1,1000,1000)',
        defType:'dismax',
        enableElevation: 'true',
        fl:'catalog_num,cas_no,name,synonym,tagline,marketing_notes,keywords,introduction_date,mojo,score,size,website_not_searchable_flag,sku,library',
        forceElevation: 'true',
        q: 'kit',
        qf:'catalog_num^10 supplier_catalog_num discontinued_id cas_no^10 name^100 synonym^100 tagline^0.01 marketing_notes^0.01 keywords product_qualifier_id^0.01 ngram_name^100 ngram_general^0.01 sku library^0.01',
        queryDebug:'true',
        rows: 5000,
        spellcheck:'true',
        'spellcheck.collate':'true',
        'spellcheck.count':10,
        'spellcheck.extendedResults':'true',
        'spellcheck.onlyMorePopular':'false',
        start: 0,
        serial: 0,
        version: 2.2,
        dummy: true
    },

    limits: {
        quick: 10,  //  Mamimum number of quick search results to display
        dummy: true
    },

    /** catalog number scrubber: strip catalog number extensions, e.g., 14010-1 --> 14010 */
    item: {
        pattern: /^(\d{5,8}(?:\.1)?)(?:-\d+)?$/,
        strip: function(searchTerm) {
            var result = CAYMANCHEM.search.item.pattern.exec(searchTerm);

            if( result === null )
                return searchTerm;
            else
                return result[1];
        }
    },

    /** Compose search term in Lucene Solr query language */
    compose: function(searchTerm) {
        var searchTerms = searchTerm.split(/\s/);
        return searchTerms.map(function(t){
            return '(' +
            CAYMANCHEM.search.fields.map(function(s){return s.name + ':' + (s.suffix?'*':'')+ t + (s.prefix?'*':'') + (typeof s.boost === 'number' ? '^' + s.boost : '')}).join(' OR ') +
            ')';
        }).join(' AND ');
    },

    /** Starting from the beginning, mark as elevated any products that preceed the product with the max score */
    elevate: function(response) {
        var products = response.docs;
        
        if(products.length === 0) return products;

        var i = 0;
        while( products[i].score < response.maxScore ) {
            products[i].elevated = true;
            i++;
        }
        

        return products;
    },

    /** calculate combined score for each product and add as property */
    combine: function(products) {
        $.each(products,function(i,product){
            // Calculate combined mojo and score
            product.combined = (product.elevated ? 100000.0 : 0) + Math.max(1.0,product.mojo) * product.score;
        });

        return products;
    },

    /** strip products not searchable by keyword */
    strip: function(products,searchTerm){
        return $.grep(products,function(product,index){
            if(typeof console === 'object') console.log(product.website_not_searchable_flag);
            return product.website_not_searchable_flag ? (product.catalog_num === searchTerm) : true;
        });
    },

    repeat: function(c, f, x){
        var n = f(x);

        for( var s = '', i = 0; i < n; s = s + c, i++ ) {}

        return s;
    },

    results: {
        /** Generate a list-like table of quicksearch produdct matches */
        list: function(baseUrl,products,serial) {
            var list = $('<table id="searchResults" class=""/>').attr('rel',serial);
            $.each(products.slice(0,CAYMANCHEM.search.limits.quick),function(i,product){
                var productUrl = baseUrl + '/template/Product.vm/catalog/' + product.catalog_num;
                var a;

                var tr = $('<tr/>');
                
                var td = $('<td/>').addClass('catalogNumber');
                a = $("<a/>").attr('rel',serial).attr('href',productUrl).text(product.catalog_num);
                td.append(a);
                tr.append(td);

                
                a = $("<a/>").attr('rel',serial).attr('href',productUrl);
                a.text(product.name);
                if(typeof product.cas_no === 'string' && product.cas_no.length > 0) {
                    a.append(' (');
                    a.append($('<em/>').text(product.cas_no));
                    a.append(')');
                }
                tr.append($('<td/>').addClass('product').append(a));

                list.append(tr);
            });
            return list;
        },
    /** Generate a table of search results to be managed by DataTables */
        table: function(baseUrl,products){
            var format = function(x) {
                var scale = 6;
                var factor = Math.pow(10.0, scale);
                var rounded = Math.floor(x * factor);
                var s = rounded + '';
                var units = s.substring(0, s.length - scale);
                var decimals = s.substring(s.length - scale);
                var result = (units.length > 0 ? units : '0') + '.' + decimals;
                return result;
            };

            var table = $('<table id="searchResults" class="productTable"><thead><tr><th class="catalogNumber">Item Number</th><th class="product">Product</th><th class="casNumber">CAS</th><th class="image">Image</th><th class="urlname">Plain Text Name</th><th class="pricing">Pricing</th><th class="keywords">Keywords</th><th class="popularity">Popularity</th><th class="score">Relevance</th><th class="combined">Combined</th><th class="popularitySort">Popularity</th><th class="scoreSort">Relevance</th><th class="combinedSort">Search Score</th></tr></thead><tbody/></table>');

            var tbody = $(table).find('tbody');

            $.each(products,function(i,product){
				//	Local variable for table cells created below
				var td;

                //  Create row, attaching catalog number
                var tr = $("<tr/>").attr('rel',product.catalog_num);

                //  Insert cells in row

                tr.append($('<td/>').addClass('catalogNumber').text(product.catalog_num));

                td = $('<td/>').addClass('product');
                td.append($('<a/>').attr('href',baseUrl + '/template/Product.vm/catalog/' + product.catalog_num).text(product.name));
                if( typeof product.synonym === 'object' && product.synonym.length ) {
                    $.each(product.synonym,function(i,synonym){
                        td.append($('<span/>').addClass('synonym').text(synonym));
                        td.append($('<span/>').addClass('separator').text(', '));
                    });
                    td.find(':last-child').remove();
                }
                tr.append(td);

                tr.append($('<td/>').addClass('casNumber').text(product.cas_no || CAYMANCHEM.search.blank.text));

                tr.append($('<td/>').addClass('image'));

                tr.append($('<td/>').addClass('urlname').text(product.name));


                var table = $('<table/>');
                if(typeof product.size === 'object' && product.size.length > 0) {
                    $.each(product.size,function(i,size){
                        var sizeFields = size.split(' $');

                        table.append($('<tr/>').append($('<td/>').text(sizeFields[0])).append($('<td/>').text('$' + sizeFields[1])));
                    });
                } else {
                    table.append($('<tr/>').append($('<td>').attr('colspan',2).text(CAYMANCHEM.search.blank.text)));
                }
                tr.append($('<td/>').addClass('pricing').append(table));

                tr.append($('<td/>').addClass('keywords').text(product.keywords || ''));

                tr.append($('<td/>').addClass('popularity').append(
                    $('<span>').addClass('score').attr('rel',format(product.mojo)).text(CAYMANCHEM.search.repeat(CAYMANCHEM.search.score,function(x){return Math.max(1.0,Math.min(5.0,Math.log(x) / 2.0))},product.mojo)))
                );

                tr.append($('<td/>').addClass('scoreSort').append(
                    $('<span>').addClass('score').attr('rel',format(product.score)).text(CAYMANCHEM.search.repeat(CAYMANCHEM.search.score,function(x){return Math.max(1.0,Math.min(5.0,x))},product.score)))
                );

                tr.append($('<td/>').addClass('combinedSort').append(
                    $('<span>').addClass('score').attr('rel',format(product.combined)).text(CAYMANCHEM.search.repeat(CAYMANCHEM.search.score,function(x){return Math.max(1.0,Math.min(5.0,Math.log(x) / 2.0))},product.combined)))
                );

                tr.append($('<td/>').addClass('popularitySort').text(product.mojo));
                tr.append($('<td/>').addClass('scoreSort').text(product.score));
                tr.append($('<td/>').addClass('combinedSort').text(product.combined));

                //  Insert row in table
                tbody.append(tr);
            });

            return table;
        }
    },

    /** spelling */
    spelling: function(baseUrl,searchTerm,spellcheck){
        var collationPosition = 0;
        if( typeof spellcheck === 'object' ) {
            collationPosition = $.inArray('collation',spellcheck.suggestions) + 1; /* collation key may or may not exist in array */
            if(  collationPosition > 0 ) {
                var collation = spellcheck.suggestions[collationPosition];
                if( searchTerm.toLowerCase() !== collation.toLowerCase() ) {
                    var link = $('<a/>').attr('href','#').text(collation).click(function(){
                        $('#q').attr('value',$(this).text());
                        $('#query').submit();
                    });
                    return $('<p>').attr('id','didYouMean').text('Did you mean ').append(link).append('?');
                } else {
                    return $('<!-- collation differs solely by case -->');
                }
            } else {
                return $('<!-- no collation -->');
            }
        } else {
            return $('<!-- no spellcheck -->');
        }
    },

    /** Activate search results table */
    activate: function(table,baseUrl){
        //  Manage with DataTables
        $(table).dataTable({
            aaSorting:[[9,'desc'],[1,'asc']],
            aoColumns: [
                {
                    "bSortable":true,
                    "sTitle":"Item &#x2116;",
                    "sType":"numeric",
                    "sWidth":"100px"
                },
                {
                    "bSortable":true,
                    "iDataSort":4,
                    "sTitle":"Product"
                },
                {
                    "bSortable":true,
                    "bVisible":true,
                    "sTitle":"CAS",
                    "sWidth":"100px"
                },
                {
                    "bSortable":false,
                    "bVisible":true,
                    "sTitle":"Image",
                    "sWidth":"100px"
                },
                {
                    "bSortable":false,
                    "bVisible":false,
                    "sTitle":"Plain Text Name"
                },
                {
                    "bSortable":false,
                    "bVisible":true,
                    "sTitle":"Pricing"
                },
                {
                    "bSortable":false,
                    "bVisible":false,
                    "sTitle":"Keywords"
                },
                {
                    "bSortable":true,
                    "bVisible":false,
                    "iDataSort":10,
                    "sTitle":"Pop",
                    "sType":"String"
                },
                {
                    "bSortable":true,
                    "bVisible":false,
                    "iDataSort":11,
                    "sTitle":"Rel",
                    "sType":"String"
                },
                {
                    "bSortable":true,
                    "bVisible":true,
                    "iDataSort":12,
                    "sTitle":"Search Score",
                    "sType":"String"
                },
                {
                    "bSortable":true,
                    "bVisible":false,
                    "sTitle":"Pop",
                    "sType":"numeric"
                },
                {
                    "bSortable":true,
                    "bVisible":false,
                    "sTitle":"Rel",
                    "sType":"numeric"
                },
                {
                    "bSortable":true,
                    "bVisible":false,
                    "sTitle":"Com",
                    "sType":"numeric"
                }
            ],
            bSort: true,
            bPaginate: true,
            bStateSave: true,
            fnRowCallback: function( nRow, aData, iDisplayIndex ) {
                var catalogNumber = $(nRow).attr('rel');
                var td = $(nRow).find('.image').first();

                if(!$(td).hasClass('populated')) {
                    $(td).addClass('populated');

                    var tinyUrl = baseUrl + '/../images/catalog/tiny/' + catalogNumber + '.png';
                    var pageUrl = baseUrl + '/../images/catalog/' + catalogNumber + '.png';
                    var blankUrl = baseUrl + '/../' + CAYMANCHEM.search.blank.image;

                    $.ajax({
                        error: function(XMLHttpRequest, textStatus, errorThrown){
                            $(this).append($('<img/>').attr('src',blankUrl));
                        },
                        type: 'GET',
                        url: tinyUrl,
                        context: td,
                        success: function(data, textStatus, xmlHttpRequest){
                                var tooltip = $('<img/>').addClass('tooltip').attr('src',pageUrl);
                                var tiny = $('<img/>').attr('src',tinyUrl);
                                $(this).append(tiny).append(tooltip);
                                tiny.tooltip({
                                    effect: 'fade',
                                    fadeInSpeed: 200,
                                    fadeOutSpeed: 200,
                                    offset: [0,-5],
                                    lazy: true,
                                    predelay: 333,
                                    position: 'center left',
                                    dummy: true
                                });
                        },
                        dummy: true
                    });
                }

                //  Product badges
                if( $(nRow).attr('data-badges') !== 'true' ) {
                    $(nRow).attr('data-badges','true');
                    
                    $.ajax({
                        data: {'catalog':catalogNumber},
                        type: 'GET',
                        url: baseUrl + '/template/Badges.vm',
                        context: $(nRow).find("td.product a"),
                        success: function(data, textStatus, xmlHttpRequest){
                            $(this).prepend(data);
                        },
                        dummy: true
                    })
                };
                
                return nRow;
            },
            iCookieDuration: 3600,
            iDisplayLength: 10,
            iDisplayStart: 0,
            oLanguage: {
                sLengthMenu: 'Display <select><option value="5">5</option><option value="10">10</option><option value="20">20</option><option value="50">50</option><option value="-1">All</option></select> Results',
                oPaginate: {
                    sFirst: "",
                    sLast: "",
                    sNext: "",
                    sPrevious: ""
                },
                sSearch: 'Filter product list by keyword ',
                dummy: true
            },
            sCookiePrefix: "datatables_",
            sDom: "<'a'fi><'b'lp>rt<'a'fi><'b'lp>",
            sPaginationType: "full_numbers",
            dummy: true
        });

        //  Show
        $(table).css('width','100%').fadeIn();

        //  Fade out any loading throbber
        $('#loading').fadeOut();
    },

    /** Perform search */
    main: function(options){
        var baseUrl = options.baseUrl;
        var resultsContainerSelector = options.resultsContainerSelector;
        var searchTerm = $.trim(options.searchTerm);
        var solrUrl = options.solrUrl;

        searchTerm = CAYMANCHEM.search.item.strip(searchTerm);

        $('#go').attr('disabled','disabled').attr('value','Searching\u2026');

        //  Perform search
        $.ajax({
            complete: function() {
                $('#go').attr('disabled','').attr('value','Full Search');
            },
            data: $.extend({}, CAYMANCHEM.search.defaults, {
                q: searchTerm,
                rows: 5000,
                start: 0,
                dummy: true
            }),
            dataType: 'json',
            success: function(data, textStatus, XMLHttpRequest) {
                /* Remove any remaining quicksearch results */
                $(resultsContainerSelector).remove('.productMatches');

                /* Mark elevated products */
                var products = CAYMANCHEM.search.elevate(data.response);

                /* Strip out products not keyword searchable unless they match catalog number */
                products = CAYMANCHEM.search.strip(products,data.responseHeader.params.q);

                /* Analytics */
                CAYMANCHEM.track.event('Search','Full',data.responseHeader.params.q,products.length);

                /* Calculated combined score */
                products = CAYMANCHEM.search.combine(products);

                var table = CAYMANCHEM.search.results.table(baseUrl,products);
                $(resultsContainerSelector).prepend(table);
                CAYMANCHEM.search.activate(table,baseUrl);

                /* spelling */
                var spelling = CAYMANCHEM.search.spelling(baseUrl,searchTerm,data.spellcheck);
                if( typeof spelling === 'object' )
                    $(resultsContainerSelector).prepend(spelling);
            },
            url: solrUrl
        });
    },

    quick: function(options) {
        var baseUrl = options.baseUrl;
        var resultsContainerSelector = options.resultsContainerSelector;
        var more = options.more;
        var searchTerm = $.trim(options.searchTerm);
        var solrUrl = options.solrUrl;

        var serial = ++CAYMANCHEM.search.serial;

        searchTerm = CAYMANCHEM.search.item.strip(searchTerm);

        //  Perform quick search
        if( typeof searchTerm === 'string' && searchTerm.length > 0 )
            $.ajax({
                complete: function() {
                    //  Remove any existing product quicksearch results not from the latest
                    $(resultsContainerSelector).remove('.productMatches[rel!='+CAYMANCHEM.search.serial+']');
                },
                data: $.extend({}, CAYMANCHEM.search.defaults, {
                    fl:'catalog_num,name,cas_no,mojo,score,website_not_searchable_flag',
                    q: searchTerm,
                    rows: 5000,
                    serial: serial,
                    start: 0,
                    dummy: true
                }),
                dataType: 'json',
                success: function(data, textStatus, XMLHttpRequest) {
                    var serial = data.responseHeader.params.serial;
                    
                    /** Abort if we're not the latest AJAX request */
                    if( serial < CAYMANCHEM.search.serial )
                        return;

                    /* Mark elevated products */
                    var products = CAYMANCHEM.search.elevate(data.response);

                    products = CAYMANCHEM.search.strip(products,searchTerm);

                    /* Calculated combined score */
                    products = CAYMANCHEM.search.combine(products);

                    CAYMANCHEM.track.event('Search','Quick',data.responseHeader.params.q,products.length);

                    if( products.length > 0 ) {
                        /* Sort in place by combined score then relevance */
                        products.sort(function(a,b){
                            return a.combined === b.combined ?
                                    (a.name < b.name ? -1 : 1)
                                :
                                    b.combined - a.combined
                                ;
                        });

                        var list = CAYMANCHEM.search.results.list(baseUrl,products,serial);
                        if( data.response.numFound > CAYMANCHEM.search.limits.quick ) {
                            list.append($('<td/>').addClass('catalogNumber').append($("<a/>").addClass('productMatches').attr('id','productMatchesMore').text("\u2026").attr('rel',serial).attr('href','#').click(function(){more()})));
                        }
                        
                        $(resultsContainerSelector).remove('.productMatches').prepend(list.addClass('productMatches')).prepend($("<h3/>").addClass('productMatches').attr('rel',serial).text("Product Matches"));
                   }
                },
                url: solrUrl
            });
    },
    
    dummy: true
}

