package org.limewire.setting;

import java.io.File;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;


/**
 * Provides a {@link File} <code>Set</code> setting value. As a subclass of 
 * <code>Setting</code>, the setting has a key.
 * <p> 
 * <code>FileSetSetting</code> class includes methods to add/remove 
 * <code>File</code>s, get <code>File</code> values as an array and return 
 * the length of the <code>File</code> set.
 * <p>
 * Create a <code>FileSetSetting</code> object with a 
 * {@link SettingsFactory#createFileSetSetting(String, File[])}.
 */
 
public class FileSetSetting extends AbstractSetting<Set<? extends File>> {
    
    private volatile Set<File> value;

    /**
     * Creates a new <tt>FileSetSetting</tt> instance with the specified
     * key and default value.
     *
     * @param key the constant key to use for the setting
     * @param defaultValue the default value to use for the setting
     */
    FileSetSetting(Properties defaultProps, Properties props, String key, File[] defaultValue) {
        super(defaultProps, props, key, decode(new HashSet<File>(Arrays.asList(defaultValue))));
        setPrivate(true);
    }

    /**
     * Returns the value of this setting.
     * 
     * @return the value of this setting
     */
    public Set<File> get() {
        return value;
    }
    
    /** Gets the value as an array. */
    public synchronized File[] getValueAsArray() {
        return value.toArray(new File[value.size()]);
    }

    /**
     * Mutator for this setting.
     *
     * @param value the value to store
     */
    public void set(Set<? extends File> value) {
        setValueInternal(decode(value));
    }

    /**
     * Mutator for this setting.
     * @param file file to add to the array.
     */
    public synchronized void add(File file) {
        value.add(file);
        set(value);
    }
    
    /**
     * Mutator for this setting.
     *
     * @param file remove file from the array, if it exists.
     * @return false when the array does not contain the file or when the
     * file is <code>null</code> 
     */
    public synchronized boolean remove(File file) {
        if(value.remove(file)) {
            set(value);
            return true;
        } else {
            return false;
        }
    }
    
    /** Returns true if the given file is contained in this array. */
    public synchronized boolean contains(File file) {
        return value.contains(file);
    }
    
    /** Returns the length of the array. */
    public synchronized int length() {
        return value.size();
    }
    
    /** Load value from property string value.
     * @param sValue property string value
     *
     */
    @Override
    protected synchronized void loadValue(String sValue) {
        value = encode(sValue);
    }
    
    /** Splits the string into a Set.    */
    private static Set<File> encode(String src) {
        if (src == null || src.length()==0)
            return new HashSet<File>();
        
        StringTokenizer tokenizer = new StringTokenizer(src, ";");
        int size = tokenizer.countTokens();
        Set<File> set = new HashSet<File>();
        for(int i = 0; i < size; i++)
            set.add(new File(tokenizer.nextToken()));
        return set;
    }
    
    /** Separates each field of the array by a semicolon     */
    private static String decode(Set<? extends File> src) {
        if (src == null || src.isEmpty())
            return "";
        
        StringBuilder buffer = new StringBuilder();
        for(File file : src) {
            buffer.append(file.getAbsolutePath()).append(';');
        }
        
        if (buffer.length() > 0) {
            buffer.setLength(buffer.length()-1);
        }  
        return buffer.toString();
    }

    /** Removes non-existent members. */
    public synchronized void clean() {
        for(Iterator<File> i = value.iterator(); i.hasNext(); ) {
            File next = i.next();
            if(!next.exists())
                i.remove();
        }
        
        set(value);
    }
    
    /** Removes all members. **/
    public synchronized void clear() {
        value.clear();
        set(value);
    }
}