/*
 * suggest.js
 *
 * In-memory substring searching of JavaScript objects found in data.js
 *
 */

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

CAYMANCHEM.suggest = {
    searchTerm:'',
    
    tests: {
        equals: function(searchTerm,testTerm) {
            return searchTerm === testTerm;
        },
        startsWith: function(searchTerm,testTerm) {
            return testTerm.indexOf(searchTerm) === 0;
        },
        matches: function(searchTerm,testTerm) {
            return testTerm.indexOf(searchTerm) !== -1;
        }
    },

    reduce: {
        and: {
            initial: true,
            lambda: function(a,b) {
                return a && b;
            }
        },
        or: {
            initial: false,
            lambda: function(a,b) {
                return a || b;
            }
        }
    },

    modes: {
        categories: {
            reduce: 'or',
            label: 'Category Matches',
            limit: Infinity,
            link: function(baseUrl,category) {
                var a = $("<a>");

                a.attr('href',baseUrl + '/template/productQualifiers,ProductQualifier.vm/productqualifier/' + category.urlname);
                a.text('See all ' + category.name + ' products (' + category.count + ')');

                return a;
            },
            minLength: 3,
            more: function(baseUrl, searchTerm) {
                return baseUrl + '/template/productQualifiers,Home.vm';
            },
            tests: [
                {test:'matches', field:'name'}
            ],
            dummy: true
        },     
        countries: {
            reduce: 'or',
            label: 'Distributor Matches',
            limit: Infinity,
            link:function(baseUrl, country) {
                var a = $("<a>");

                a.attr('href',baseUrl + '/template/globalBuyersGuide,Territory.vm/territory/' + country.id);
                a.text('See distributors in ' + country.name);

                return a;
            },
            minLength: 2,
            more: function(baseUrl, searchTerm) {
                return baseUrl + '/template/globalBuyersGuide,Home.vm';
            },
            tests: [
                {test:'equals', field:'id'},
                {test:'startsWith', field:'name'}
            ],
            dummy: true
        },
        shortcuts: {
            reduce: 'or',
            label: 'Other Site Matches',
            limit: Infinity,
            link: function(baseUrl,shortcut) {
                var a = $("<a>");

                a.attr('href',shortcut.target);
                a.text(shortcut.label) ;

                return a;
            },
            tests: [
                {test: 'matches', field:'shortcut'},
                {test: 'matches', field:'label'}
            ],
            minLength: 3,
            dummy: true
        },
        conferences: {
            reduce: 'or',
            label: 'Event Matches',
            limit: Infinity,
            link: function(baseUrl,conference) {
                var a = $("<a>");

                a.attr('href',baseUrl + '/template/ConferenceSchedule.vm');
                a.text(conference.name + ', ' + conference.location);

                return a;
            },
            more: function(baseUrl, searchTerm) {
                return baseUrl + '/template/ConferenceSchedule.vm';
            },
            tests: [
                {test: 'matches', field:'name'},
                {test: 'matches', field:'tagline'},
                {test: 'matches', field:'location'}
            ],
            minLength: 3,
            dummy: true
        },
        sitemap: {
            reduce: 'or',
            label: 'Other Page Matches',
            limit: Infinity,
            link: function(baseUrl,page) {
                var a = $("<a>");

                a.attr('href',baseUrl + '/template/' + page.target);
                a.text(page.label);

                return a;
            },
            more: function(baseUrl, searchTerm) {
                return baseUrl + '/template/SiteMap.vm';
            },
            tests: [
                {test:'matches',field:'label'},
                {test:'matches',field:'description'}
            ],
            minLength: 3,
            dummy: true
        },
        pressreleases: {
            reduce: 'or',
            label: 'Press Release Matches',
            limit: Infinity,
            link: function(baseUrl,pressRelease) {
                var a = $("<a>");

                a.attr('href',baseUrl + '/template/pressRelease,PressRelease.vm/number/' + pressRelease.number);
                a.text(pressRelease.title);

                return a;
            },
            more: function(baseUrl, searchTerm) {
                return baseUrl + '/template/pressRelease,Home.vm';
            },
            tests: [
                {test:'matches',field:'title'},
                {test:'equals',field:'number'}
            ],
            minLength: 2,
            dummy: true
        },
        suppliers: {
            reduce: 'or',
            label: 'Supplier Matches',
            limit: Infinity,
            link: function(baseUrl,supplier) {
                var a = $("<a>");

                a.attr('href',baseUrl + '/template/productLine,Supplier.vm/supplier/' + supplier.id);
                a.text(supplier.name);

                return a;
            },
            more: function(baseUrl, searchTerm) {
                return baseUrl + '/template/Suppliers.vm';
            },
            tests: [
                {test:'matches',field:'name'},
                {test:'matches',field:'blurb'},
                {test:'equals',field:'id'},
                {test:'matches',field:'keywords'}
            ],
            minLength: 3,
            dummy: true
        }
    },

    main: function(options) {
        var baseUrl = options.baseUrl;
        var resultsContainerSelector = options.resultsContainerSelector;
        var searchTerm = options.searchTerm;

        var suggestions = $(resultsContainerSelector);

        //  Massage search term
        var searchTermLowerCase = searchTerm.toLowerCase();

        //  Skip individual search modes if search term is blank
        if( typeof searchTerm === 'undefined' || searchTerm.length === 0 )
            return;
        
        for( var mode in CAYMANCHEM.suggest.modes ) {
            if( searchTerm.length >= CAYMANCHEM.suggest.modes[mode].minLength ) {
                var treatedSearchTerm = ( typeof CAYMANCHEM.suggest.modes[mode].treatment === 'function' ) ?
                    CAYMANCHEM.suggest.modes[mode].treatment(searchTerm) :
                    searchTerm;

                var count = 0;
                for( key in CAYMANCHEM.data[mode] ) {
                    var value = CAYMANCHEM.data[mode][key];
                    if( CAYMANCHEM.suggest.test(treatedSearchTerm,CAYMANCHEM.suggest.modes[mode],value) ) {
                        count = count + 1;
                        if( count === 1 ) {
                            suggestions.append($("<h3/>").text(CAYMANCHEM.suggest.modes[mode].label));
                        }
                        if( count > CAYMANCHEM.suggest.modes[mode].limit ) break;
                        suggestions.append(CAYMANCHEM.suggest.modes[mode].link(baseUrl,value));
                    }
                }

                /* Full results link */
                if( count > 0 && typeof CAYMANCHEM.suggest.modes[mode].more === 'function')
                    suggestions.append($("<a/>").text("\u2026").attr('href',CAYMANCHEM.suggest.modes[mode].more(baseUrl, searchTerm)));
            }
        }

   },
   /** Does this object match the search term according to its mode's configuration? */
   test: function(searchTerm,mode,object) {
       var tokens = $.grep(searchTerm.toLowerCase().split(/\s+/),function(token,index){
           return token.length > 0;
       });

       /* tokenResult is a map of tokens to bools indicating whether a token matched any test */
       var tokenResults = {};

       /* Try each token against each searchable field of the object */
       $.each(tokens,function(i,token) {
           /* tokenModeResults is an arary of bools indicating whether the token matched each test configured for this mode */
           var tokenTestResults = [];

           $.each(mode.tests,function(i,test){
               var f = CAYMANCHEM.suggest.tests[test.test];
               var field = test.field;
               var testTerm = object[field];
 
               tokenTestResults.push( (typeof testTerm === 'undefined') ? false : f(token,testTerm.toLowerCase()) );
           });

           /* tokenResult will get a true entry if any test matched */
           tokenResults[token] = false;
           $.each(tokenTestResults,function(i,tokenTestResult){
               if(tokenTestResult) tokenResults[token] = true;
           });
       });

       /* Reduce */
       var result = CAYMANCHEM.suggest.reduce[mode.reduce].initial;
       $.each(tokenResults,function(key,value) {
           result = CAYMANCHEM.suggest.reduce[mode.reduce].lambda(result,value);
       });

       return result;
   }
};

