/**
 * Cache manage for filter option lists
 *
 * Events:
 *  - SEARCH
 */

nb.module({builder: function(nb) {

    function formatDigit(digit, num) {
        if(num == undefined) {
            num = 2;
        }
        var repr = String(digit);
        while(repr.length < num) {
            repr = '0' + repr;
        }
        return repr;
    }

    /**
     * Represent time in the form of '2009-01-01 01:01'
     */
    function reprTime(dateObj) {
        var h = formatDigit(dateObj.getHours());
        var m = formatDigit(dateObj.getMinutes());
        var month = formatDigit(dateObj.getMonth() + 1);
        var day = formatDigit(dateObj.getDate());
        var year = formatDigit(dateObj.getFullYear(), 4);
        return year + '-' + month + '-' + day + ' ' + h + ':' + m;
    }

    var FilterCache = function() {
        this.data_ready_callback = null;
        this.cached_pages = {};
        this.all_options = {};
        this.selectors = {};

        this.getSelector('start_time').sbfilter('controller',
                    new nb.sbfilter.SelectorController(false));
        this.getSelector('sport').sbfilter('controller',
                    new nb.sbfilter.SelectorController(true));
        this.getSelector('country').sbfilter('controller',
                    new CountryController());
        this.getSelector('league').sbfilter('controller',
                    new nb.sbfilter.SelectorController(true));
        this.getSelector('team').sbfilter('controller',
                    new nb.sbfilter.SelectorController(true));
        this.getSelector('matches').sbfilter('controller',
                    new MatchController());
        // page element cache, jQuery selector get page element is too slow.
        // Using these cache to decrease search range can improve performance
        // eg. $('#matches', this.filterArea)
        this.filterArea = $('#simp_selec_filters');
        this.search_button = $('div.filter_buttons a#search', this.filterArea);
        this.clear_search_link = $('div.filter_buttons a#reset', this.filterArea);
    };

    FilterCache.prototype = {    
        getSelector: function(selector_id) {
            if (!(selector_id in this.selectors))
                this.selectors[selector_id] = $('div.filter_selector#' + selector_id, this.filterArea);
            return this.selectors[selector_id]
        },

        dataReady: function(callback) {
            this.data_ready_callback = callback;
        },

        getSearchedGames: function() {
            return this.getSelector('matches').sbfilter('controller').getSearchedGames();
        },

        getCandidates: function() {
            var filter_cache = this;
            // invalidate page cache
            this.cached_pages = {};

            var request = this.search_button.sbfilter('getJson',
                                    nb.sbfilter.OPTIONS.ajaxURL, {cmd: 'get_candidates'}, function(data) {
                filter_cache.all_options.matches = nb.sbfilter.parseOptions(data.game_options, nb.sbfilter.OPTIONS.protocolDefinitions.game);
                filter_cache.all_options.season = nb.sbfilter.parseOptions(data.season_options, nb.sbfilter.OPTIONS.protocolDefinitions.season);
                filter_cache.all_options.sport= nb.sbfilter.parseOptions(data.sport_options, nb.sbfilter.OPTIONS.protocolDefinitions.sport);
                filter_cache.all_options.country = nb.sbfilter.parseOptions(data.country_options, nb.sbfilter.OPTIONS.protocolDefinitions.country);
                filter_cache.all_options.league = nb.sbfilter.parseOptions(data.league_options, nb.sbfilter.OPTIONS.protocolDefinitions.league);
                filter_cache.all_options.team = nb.sbfilter.parseOptions(data.part_options, nb.sbfilter.OPTIONS.protocolDefinitions.part);
                filter_cache.initializeOptions();
                if(filter_cache.data_ready_callback) {
                    filter_cache.data_ready_callback.apply(filter_cache);
                }
            }, function(){
                filter_cache.search_button.sbfilter('endLoading');
            });
        },

        loadMenu: function(selector_id) {
            var selector = this.getSelector(selector_id);

            if(selector_id in this.cached_pages) {
                var menu_div = $('div.menu', selector);
                menu_div.width(this.cached_pages[selector_id].width);
                menu_div.height(this.cached_pages[selector_id].height);
                nb.sbfilter.fastUpdateInnerHTML(menu_div, this.cached_pages[selector_id].page);
                selector.sbfilter('controller').registerEvents();
            } else {
                selector.sbfilter('controller').updateOptions(this.all_options[selector_id]);
                // the above line changes the DOM node 'div.menu' so the below line
                // can't be merged together will line 73
                var menu_div = $('div.menu', selector);
                var cached_page_html = menu_div.html();
                if(cached_page_html) {
                    this.cached_pages[selector_id] = {
                    width : menu_div.width(),
                    height : menu_div.height(),
                    page : cached_page_html};
                }
            }
        },

        /**
         * Reset all selects
         */
        reset: function() {
            this.loadMenu('matches');
            this.updateFilterOptions(null);
            $('div.filter_selector', this.filterArea).each(function() {
                $(this).sbfilter('controller').reset();
            });
            nb.sbfilter.filterSelectorsReady();
        },

        installSeasons: function(options) {
            for(var key in options) {
                var option_obj = options[key];
                var seasonsObj = {};
                for(var i=0; i< option_obj.seasons.length; i++) {
                    var season_id = option_obj.seasons[i];
                    seasonsObj[season_id] = this.all_options.season[season_id];
                }
                option_obj.seasons = seasonsObj;
            }
        },

        initializeOptions: function() {
            for(var season_id in this.all_options.season) {
                this.all_options.season[season_id].games = new Object();
            }
            for(var game_id in this.all_options.matches) {
                var game = this.all_options.matches[game_id];
                this.all_options.matches[game.id] = game;
                var season = this.all_options.season[game.season_id];
                season.games[game_id] = game;
            }
            this.installSeasons(this.all_options.team);
            this.installSeasons(this.all_options.sport);
            this.installSeasons(this.all_options.country);
            this.installSeasons(this.all_options.league);
            this.installStartTimeGames(this.all_options.matches);
            var filter_cache = this;
            this.timer = setInterval(function() {
                filter_cache.installStartTimeGames(filter_cache.all_options.matches);
            }, 300*1000);  // run time segment every 5 minutes.
            this.reset();
        },

        updateInitOptions: function() {
            // Sport
            this.loadMenu('sport');
            // Country
            this.loadMenu('country');
            // League
            this.loadMenu('league');
            // Team
            this.loadMenu('team');
        },

        updateOptionsFromGame: function(game_options, what_changed) {
            var season_options = {};
            for(var game_id in game_options) {
                var game = game_options[game_id];
                var season = this.all_options.season[game.season_id];
                season_options[game.season_id] = season;
            }
            this.updateOptionsFromSeason(season_options, what_changed);
        },

        updateTeamOptions: function(game_options, what_changed) {
            var part_options = {};
            for(var game_id in game_options) {
                var game = game_options[game_id];
                part_options[game.home_id] = this.all_options.team[game.home_id];
                part_options[game.away_id] = this.all_options.team[game.away_id];
            }

            var changed_index = $.inArray(what_changed, this.logicalSequence);

            if($.inArray('team', this.logicalSequence) > changed_index) {
                this.getSelector('team').sbfilter('controller').updateOptions(part_options);
            }
        },

        updateOptionsFromSeason: function(season_options, what_changed) {
            var new_country_options = {};
            var new_sport_options = {};
            var new_league_options = {};
            for(var season_id in season_options) {
                var season = season_options[season_id];
                // Shrink the size of country options
                new_country_options[season.region_id] = this.all_options.country[season.region_id];
                new_sport_options[season.sport_id] = this.all_options.sport[season.sport_id];
                new_league_options[season.category_id] = this.all_options.league[season.category_id];
            }

            var changed_index = $.inArray(what_changed, this.logicalSequence);

            if($.inArray('sport', this.logicalSequence) > changed_index) {
                this.getSelector('sport').sbfilter('controller').updateOptions(new_sport_options);
            }
            if($.inArray('country', this.logicalSequence) > changed_index) {
                this.getSelector('country').sbfilter('controller').updateOptions(new_country_options);
            }
            if($.inArray('league', this.logicalSequence) > changed_index) {
                this.getSelector('league').sbfilter('controller').updateOptions(new_league_options);
            }
        },

        updateFilterOptions: function(filter_set) {
            if(filter_set == null || filter_set.action == null) {
                this.updateInitOptions();
            } else if(filter_set.action == 'start_time') {
                this.updateOptionsFromGame(filter_set.game_options, filter_set.what_changed);
                this.updateTeamOptions(filter_set.game_options, filter_set.what_changed);
            } else if(filter_set.action == 'team') {
                // Do nothing
            } else {
                this.updateOptionsFromSeason(filter_set.season_options, filter_set.what_changed);
                this.updateTeamOptions(filter_set.getGameOptions(), filter_set.what_changed);
            }
        },

        getSeasonGames: function(season_options) {
            var game_options = new Object();
            for(var season_id in season_options) {
                var season = season_options[season_id];
                nb.sbfilter.mergeObject(game_options, season.games);
            }
            return game_options;
        },

        eachGame: function(season_options, callback) {
            for(var season_id in season_options) {
                var season = season_options[season_id];
                for(var game_id in season.games) {
                    callback(game_id, season.games[game_id]);
                }
            }
        },
        
        onMatchSelectChange: function() {
            var game_ids = $('#matches', this.filterArea).sbfilter('controller').getSortedValue(true);
            if(game_ids.length > 0) {
                var game = this.all_options.matches[game_ids[0]];
                var season = this.all_options.season[game.season_id];
                if(season) {
                    this.getSelector('league').sbfilter('controller').updateBarValue(season.category_id);
                    this.getSelector('country').sbfilter('controller').updateBarValue(season.region_id);
                    this.getSelector('sport').sbfilter('controller').updateBarValue(season.sport_id);
                }
            } else {
                this.getSelector('league').sbfilter('controller').updateBarValue(nb.sbfilter.ALL);
                this.getSelector('country').sbfilter('controller').updateBarValue(nb.sbfilter.ALL);
                this.getSelector('sport').sbfilter('controller').updateBarValue(nb.sbfilter.ALL);
            }
        },

        logicalSequence: ['start_time', 'sport', 'country', 'league', 'team'],

        diffInSeq: function(what_changed, what_tested) {
            return ($.inArray(what_changed, this.logicalSequence) -
                    $.inArray(what_tested, this.logicalSequence));
        },

        getValue: function(selector_id) {
            return this.getSelector(selector_id).sbfilter('controller').getValue();
        },

        onSelectionChange: function(changed_selector) {
            var what_changed = changed_selector.attr('id');
            var filter_set = new FilterSet(this);
            filter_set.what_changed = what_changed;
            filter_set.filterSport()
                .filterCountry()
                .filterLeague()
                .filterTeam()
                .filterStartTime();
            var match_controller = this.getSelector('matches').sbfilter('controller');
            match_controller.updateOptions(filter_set.getGameOptions());
            this.updateFilterOptions(filter_set);
        },

        addItem: function(selector_id, option_id, search_buffer) {
            if(selector_id == 'start_time')
                return;
            nb.sbfilter.mergeObjectWithCounters(search_buffer,
                                    this.all_options[selector_id][option_id].seasons);
        },

        removeItem: function(selector_id, option_id, search_buffer) {
            if(selector_id == 'start_time')
                return;
            nb.sbfilter.removeSrc(search_buffer, this.all_options[selector_id][option_id].seasons);
        },

        installStartTimeGames: function() {
            var timePattern = new RegExp(/(day|hour)(\d+)/);
            var filter_cache = this;

            var segments = [];
            var time_segments = {};
            $('#start_time div.menu ul li', this.filterArea).each(function() {
                var start_time = $(this).attr('id');
                var result = timePattern.exec(start_time);
                if(!result) {
                    return;
                }

                // currentDate is defined in header.cs, in local format
                var lowlimit = new Date();
                lowlimit.setTime(nb.globals.currentDate.getTime()); // copy currentDate

                var highlimit = new Date(); // copy currentDate
                highlimit.setTime(nb.globals.currentDate.getTime());

                time_segments[result[0]] = {};
                var diff = parseInt(result[2]);
                if(result[1] == 'hour') {
                    var hours = highlimit.getHours();
                    highlimit.setHours(hours + diff);
                } else { // day
                    var date = highlimit.getDate();
                    if(diff != 0) {
                        lowlimit = new Date(lowlimit.getFullYear(),
                                            lowlimit.getMonth(),
                                            date + diff);
                    }
                    highlimit = new Date(highlimit.getFullYear(),
                                         highlimit.getMonth(),
                                         date + diff + 1);
                }

                // low limit in local time
                var lowlimit_ts = reprTime(lowlimit);
                // high limit in local time
                var highlimit_ts = reprTime(highlimit);
                segments.push({'label': result[0],
                               'lowlimit': lowlimit_ts,
                               'highlimit': highlimit_ts});
            });

            // Segment games using segments
            for(var game_id in this.all_options.matches) {
                var game = this.all_options.matches[game_id];
                var real_start_time = game.start_time;
                for(var k=0; k< segments.length; k++) {
                    var seq = segments[k];
                    if(real_start_time > seq.lowlimit &&
                       real_start_time <= seq.highlimit) {
                        time_segments[seq.label][game.id] = game;
                    }
                }
            }
            this.time_segments = time_segments;
        },

        onReady: function() {
            var filter_cache = this;
            $(document).bind(nb.sbfilter.EventTypes.AJAX_LOADER_START, function(event, target){
                 filter_cache.search_button.sbfilter('disableCommand');
            });
            $(document).bind(nb.sbfilter.EventTypes.AJAX_LOADER_STOP, function(event, target){
                filter_cache.search_button.sbfilter('enableCommand');
            });


            $(document).bind(nb.sbfilter.EventTypes.ITEM_SELECTED,
                function(event, selector_id, obj_id, search_buffer, context_element) {
                  filter_cache.addItem(selector_id, obj_id, search_buffer);
                });

            $(document).bind(nb.sbfilter.EventTypes.ITEM_RANGE_SELECTED,
                function(event, selector_id, obj_id_list, search_buffer) {
                  for(var i=0; i< obj_id_list.length; i++) {
                    filter_cache.addItem(selector_id, obj_id_list[i], search_buffer);
                  }
                });

            $(document).bind(nb.sbfilter.EventTypes.ITEM_REMOVED,
                function(event, selector_id, obj_id, search_buffer, context_element) {
                  filter_cache.removeItem(selector_id, obj_id, search_buffer);
                });

            $(document).bind(nb.sbfilter.EventTypes.SELECTOR_CHANGED,
                function(event, controller) {
                  if(controller.selector_id == 'matches') {
                    filter_cache.onMatchSelectChange();
                  } else {
                    var selector = controller.selector;
                    filter_cache.onSelectionChange(selector);
                  }
                });

            this.clear_search_link.click(function() {
                // Execute in different thread
                setTimeout(function () {
                    filter_cache.reset();
                }, 0);
            });

            this.search_button.click(function() {
                if($(this).sbfilter('isDisabled')) {
                    return;
                }
                $(document).trigger(nb.sbfilter.EventTypes.SEARCH,
                                    [filter_cache.getSearchedGames()]);
            });
        }
    };

    var FilterSet = function(filter_cache) {
        this.filter_cache = filter_cache;
        this.season_options = filter_cache.all_options.season;
        this.game_options = null;
        this.action = null;
    };

    FilterSet.prototype = {
        filterCountry: function() {
            var country_val = this.filter_cache.getValue('country');
            if(country_val.length == 0 ||
               0 > this.filter_cache.diffInSeq(this.what_changed, 'country')) {
                return this;
            }
            this.action = 'country';
            this.getSeasonOptions();
            return this;
        },

        getSelectObject: function() {
            return this.filter_cache.getSelector(this.action).sbfilter('controller').search_buffer.obj;
        },

        getSeasonOptions: function() {
            var selected_seasons = this.getSelectObject();
            this.season_options = nb.sbfilter.intersectObject(
                        selected_seasons, this.season_options);
        },

        filterSport: function() {
            var sport_val = this.filter_cache.getValue('sport');
            if(sport_val.length == 0 ||
               0 > this.filter_cache.diffInSeq(this.what_changed, 'sport')) {
                return this;
            }
            this.action = 'sport';
            this.getSeasonOptions();
            return this;
        },

        filterLeague: function() {
            var league_val = this.filter_cache.getValue('league');
            if(league_val.length == 0 ||
               0 > this.filter_cache.diffInSeq(this.what_changed, 'league')) {
                return this;
            }
            this.action = 'league';
            this.getSeasonOptions();

            return this;
        },

        filterTeam: function() {
            var team_val = this.filter_cache.getValue('team');
            if(team_val.length == 0 ||
               0 > this.filter_cache.diffInSeq(this.what_changed, 'team')) {
                return this;
            }
            this.action = 'team';
            this.getSeasonOptions();
            var tmp_game_options = new Object();
            this.filter_cache.eachGame(this.season_options,
                                       function(game_id, game) {
                if(($.inArray(game.home_id, team_val) != -1) ||
                   ($.inArray(game.away_id, team_val) != -1)) {
                    tmp_game_options[game_id] = game;
                }
            });
            this.game_options = tmp_game_options;
            return this;
        },

        filterStartTime: function() {
            var start_time = this.filter_cache.getValue('start_time');
            if(start_time.length == 0 ||
               0 > this.filter_cache.diffInSeq(this.what_changed, 'start_time')) {
                return this;
            }
            this.action = 'start_time';
            var timePattern = new RegExp(/(day|hour)(\d+)/);
            var result = timePattern.exec(start_time);
            if(!result) {
                return this;
            }
            var pre_game_options = this.getGameOptions();
            var hit_game_options = this.filter_cache.time_segments[result[0]];
            if(pre_game_options == this.filter_cache.all_options.matches) {
                this.game_options = hit_game_options;
            } else {
                this.game_options = nb.sbfilter.intersectObject(hit_game_options, pre_game_options);
            }

            return this;
        },

        getGameOptions: function() {
            if(this.game_options == null) {
                this.game_options = this.filter_cache.getSeasonGames(this.season_options);
            }
            return this.game_options;
        }
    };

    MatchController.prototype = new nb.sbfilter.SelectorController(true);
    MatchController.prototype.constructor = MatchController;
    function MatchController() {
        this.result_game_id_list = [];
        this.all_game_list = [];
    }

    MatchController.prototype.reset = function() {
        nb.sbfilter.SelectorController.prototype.reset.apply(this, []);
        this.result_game_id_list = this.all_game_list;
    };

    MatchController.prototype.updateOptions = function(options) {
        var game_list = nb.sbfilter.SelectorController.prototype.updateOptions.apply(this,
                             [options]);
        this.result_game_id_list = [];
        for(var i=0; i< game_list.length; i++) {
            this.result_game_id_list.push(game_list[i].id);
        }

        if(options == nb.ssbs.filter_cache.all_options.matches) {
            this.all_game_list = this.result_game_id_list;
        }
        return this.result_game_id_list;
    };
    /**
     * return a list of game ids that can be sent to servers
     */
    MatchController.prototype.getSearchedGames = function() {
        var game_id_list = this.getSortedValue();
        if(game_id_list.length <= 0) {
            game_id_list = game_id_list.concat(this.result_game_id_list);
        }
        return game_id_list;
    };

    /**
     * CountryController extends SelectorController
     */
    CountryController.prototype = new nb.sbfilter.SelectorController(true);
    CountryController.prototype.constructor = CountryController;
    function CountryController() {

    }

    CountryController.prototype.updateOptions = function(options) {
        var main_country_list = new Array();
        var region_list = new Array();
        var country_list = new Array();
        $.each(options, function(country_id, info){
            var option = {id: country_id, name: info.name};
            switch (info.country_type) {
                case '1': // region
                    region_list.push(option);
                    break;
                case '0': // main country
                    main_country_list.push(option);
                case '2': // normal country
                    country_list.push(option);
            }
        });

        // sort options.
        main_country_list.sort(nb.sbfilter.sortObjectByName);
        region_list.sort(nb.sbfilter.sortObjectByName);
        country_list.sort(nb.sbfilter.sortObjectByName);

        // render html.
        this.updateMultipleOptionList(main_country_list, region_list, country_list);
    };

    nb.ssbs.FilterCache = FilterCache;

}});

