package com.limegroup.gnutella.gui.search;

import java.util.HashMap;
import java.util.Map;

import com.limegroup.gnutella.settings.AbstractSettings;
import com.limegroup.gnutella.settings.SettingsFactory;
import com.limegroup.gnutella.settings.StringSetting;

/**
 * Maintains the settings of what selectors are associated with
 * the various schema searches.
 */
class SelectorsHandler extends AbstractSettings {
    
    /**
     * The sole instance of this SelectorSettings class.
     */
    private static final SelectorsHandler INSTANCE = new SelectorsHandler();
    
    /**
     * The factory for the SelectorSettings.
     */
    private static final SettingsFactory FACTORY = INSTANCE.getFactory();
    
    /**
     * The mapping of active selectors.
     *
     * The key is the type of search + "_" + the number of the selector.
     * The value is the actual selector.
     *
     * For example, if the default selectors for someone's "any type" searches
     * are first "schema", then "audio/artist", then "audio/album", this would
     * have:
     *  ANY_TYPE_1 -> Selector (of type schema) / StringSetting
     *  ANY_TYPE_2 -> Selector (of type field)  / StringSetting 
     *  ANY_TYPE_3 -> Selector (of type field)  / StringSetting
     */
    private static final Map /* String -> SelectorSettingPair */ FILTERS =
        new HashMap();
    
    /**
     * Returns the instance of this class.
     */
    public static SelectorsHandler instance() {
        return INSTANCE;
    }
    
    /**
     * Private constructor to ensure that nothing else can instantiate.
     */
    private SelectorsHandler() {
        super("filters.props", "LimeWire Filters File");
    }
    
    /**
     * Retrieves the selector for the specified type at the specified depth.
     */
    public static Selector getSelector(String type, int depth) {
        String key = type + "_" + depth;
        SelectorSettingPair pair = (SelectorSettingPair)FILTERS.get(key);
        if(pair == null) {
            pair = addDefault(type, depth);
        }
        return pair.selector;
    }
    
    /**
     * Sets the new selector for the specified type/depth.
     */
    public static void setSelector(String type, int depth, Selector selector) {
        String key = type + "_" + depth;   
        SelectorSettingPair pair = (SelectorSettingPair)FILTERS.get(key);
        StringSetting setting;
        if(pair == null)
            pair = addDefault(type, depth);
        setting = pair.setting;
        setting.setValue(selector.toString());
        FILTERS.put(key, new SelectorSettingPair(selector, setting));
    }   
        
    /**
     * Adds the default selector for the specified type & depth.
     */
    private static SelectorSettingPair addDefault(String type, int depth) {
        String key = type + "_" + depth;
        Selector selector = getDefaultSelector(type, depth);
        StringSetting setting =
            FACTORY.createStringSetting(key, selector.toString());
        try {
            selector = Selector.createFromString(setting.getValue());
        } catch(IllegalArgumentException iae) {
            // invalid data on disk, ignore.
        }
        
        SelectorSettingPair pair = new SelectorSettingPair(selector, setting);
        FILTERS.put(key, pair);
        return pair;
    }
            
    /**
     * Creates default selectors for the specified types and depths.
     *
     * Default 'all' selectors are:
     *   1: schema
     *   2: artist
     *   3: album
     *
     * Default audio selectors are:
     *   1: schema
     *   2: artist
     *   3: album
     *
     * Default 'video' selectors are:
     *   1: schema
     *   2: type
     *   3: rating
     *
     * Default selectors for everything else are:
     *   1: schema
     *   2: extension
     *   3: quality
     */
    private static Selector getDefaultSelector(String type, int depth) {
        if("*".equals(type)) {
            switch(depth) {
            case 0: return Selector.createSchemaSelector();
            case 1: return Selector.createFieldSelector("audio",
                                            "audios__audio__artist__");
            case 2: return Selector.createFieldSelector("audio",
                                            "audios__audio__album__");
            }
        } else if("audio".equals(type)) {
            switch(depth) {
            case 0: return Selector.createFieldSelector("audio",
                                            "audios__audio__genre__");
            case 1: return Selector.createFieldSelector("audio",
                                             "audios__audio__artist__");
            case 2: return Selector.createFieldSelector("audio",
                                            "audios__audio__album__");
            }
        } else if("video".equals(type)) {
            switch(depth) {
            case 0: return Selector.createPropertySelector("RESULT_PANEL_TYPE");
            case 1: return Selector.createFieldSelector("video",
                                            "videos__video__type__");
            case 2: return Selector.createFieldSelector("video",
                                            "videos__video__rating__");
            }
        } else {
            switch(depth) {
            case 0: return Selector.createSchemaSelector();
            case 1: return Selector.createPropertySelector("RESULT_PANEL_TYPE");
            case 2: return Selector.createPropertySelector("RESULT_PANEL_SPEED");
            }
        }
        throw new IllegalArgumentException("invalid depth: " + depth);
    }
    
    /**
     * A Pair of Selector/Setting.
     */
    private static class SelectorSettingPair {
        private final Selector selector;
        private final StringSetting setting;
        
        SelectorSettingPair(Selector selector, StringSetting setting) {
            this.selector = selector;
            this.setting = setting;
        }
    }

}
        