/**
 * Cache manage for filter option lists
 *
 * Events:
 *  - SEARCH
 */
function FilterCache() {
    this.data_ready_callback = null;
    this.cached_pages = {};
    this.all_options = {};
    this.selectors = {};

    this.getSelector('start_time').controller(
                new SelectorController(false));
    this.getSelector('sport').controller(
                new SelectorController(true));
    this.getSelector('country').controller(
                new CountryController());
    this.getSelector('league').controller(
                new SelectorController(true));
    this.getSelector('team').controller(
                new SelectorController(true));
    this.getSelector('matches').controller(
                new MatchController());
    // page element cache, jQuery selector get page element is too slow.
    // Using these cache to decrease search range can imporve 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]
}

FilterCache.prototype.dataReady = function(callback) {
    this.data_ready_callback = callback;
};

FilterCache.prototype.getSearchedGames = function() {
    return this.getSelector('matches').controller().getSearchedGames();
}

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

    var request = this.search_button.getJson(
                            OPTIONS.ajaxURL, {cmd: 'get_candidates'}, function(data) {
        filter_cache.all_options.matches = parseOptions(data.game_options, OPTIONS.protocolDefinitions.game);
        filter_cache.all_options.season = parseOptions(data.season_options, OPTIONS.protocolDefinitions.season);
        filter_cache.all_options.sport= parseOptions(data.sport_options, OPTIONS.protocolDefinitions.sport);
        filter_cache.all_options.country = parseOptions(data.country_options, OPTIONS.protocolDefinitions.country);
        filter_cache.all_options.league = parseOptions(data.league_options, OPTIONS.protocolDefinitions.league);
        filter_cache.all_options.team = parseOptions(data.part_options, 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.endLoading();
    });
};

FilterCache.prototype.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);
        fastUpdateInnerHTML(menu_div, this.cached_pages[selector_id].page);
        selector.controller().registerEvents();
    } else {
        selector.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
 */
FilterCache.prototype.reset = function() {
    this.loadMenu('matches');
    this.updateFilterOptions(null);
    $('div.filter_selector', this.filterArea).each(function() {
        $(this).controller().reset();
    });
    filterSelectorsReady();
};

FilterCache.prototype.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;
    }
};

FilterCache.prototype.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();
};

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


FilterCache.prototype.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);
};

FilterCache.prototype.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').controller().updateOptions(part_options);
    }
};

FilterCache.prototype.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').controller().updateOptions(new_sport_options);
    }
    if($.inArray('country', this.logicalSequence) > changed_index) {
        this.getSelector('country').controller().updateOptions(new_country_options);
    }
    if($.inArray('league', this.logicalSequence) > changed_index) {
        this.getSelector('league').controller().updateOptions(new_league_options);
    }
};

FilterCache.prototype.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);
    }
};

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

FilterCache.prototype.eachGame = function(season_options, callback) {
    for(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]);
        }
    }
};

function FilterSet(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;
};


FilterSet.prototype.getSelectObject = function() {
    return this.filter_cache.getSelector(this.action).controller().search_buffer.obj;
};

FilterSet.prototype.getSeasonOptions = function() {
    var selected_seasons = this.getSelectObject();
    this.season_options = intersectObject(
                selected_seasons, this.season_options);
}

FilterSet.prototype.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;
};


FilterSet.prototype.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;
};

FilterSet.prototype.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;
};

FilterSet.prototype.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 = intersectObject(hit_game_options, pre_game_options);
    }

    return this;
};

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

FilterCache.prototype.onMatchSelectChange = function() {
    var game_ids = $('#matches', this.filterArea).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').controller().updateBarValue(season.category_id);
            this.getSelector('country').controller().updateBarValue(season.region_id);
            this.getSelector('sport').controller().updateBarValue(season.sport_id);
        }
    } else {
        this.getSelector('league').controller().updateBarValue(ALL);
        this.getSelector('country').controller().updateBarValue(ALL);
        this.getSelector('sport').controller().updateBarValue(ALL);
    }
};

FilterCache.prototype.logicalSequence = ['start_time', 'sport', 'country', 'league', 'team'];

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

FilterCache.prototype.getValue = function(selector_id) {
    return this.getSelector(selector_id).controller().getValue();
};

FilterCache.prototype.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').controller();
    match_controller.updateOptions(filter_set.getGameOptions());
    this.updateFilterOptions(filter_set);
};

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


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


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;
}

FilterCache.prototype.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(currentDate.getTime()); // copy currentDate

        var highlimit = new Date(); // copy currentDate
        highlimit.setTime(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;
};

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

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

MatchController.prototype.updateOptions = function(options) {
    var game_list = 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 == $(document).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 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 '0': // region
                region_list.push(option);
                break;
            case '2': // main country
                main_country_list.push(option);
            case '1': // normal country
                country_list.push(option);
        }
    });

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

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


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


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

    $(document).bind(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(EventTypes.ITEM_REMOVED,
        function(event, selector_id, obj_id, search_buffer, context_element) {
          filter_cache.removeItem(selector_id, obj_id, search_buffer);
        });

    $(document).bind(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).isDisabled()) {
            return;
        }
        $(document).trigger(EventTypes.SEARCH,
                            [filter_cache.getSearchedGames()]);
    });
};
