package org.limewire.setting;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;

import org.limewire.util.FileUtils;


/**
 * Provides a {@link File} setting value. As a subclass 
 * of <code>Setting</code>, the setting has a key. 
 * <p>
 * Create a <code>FileArraySetting</code> object with a 
 * {@link SettingsFactory#createFileArraySetting(String, File[])}.
 */
 
public class FileArraySetting extends AbstractSetting<File[]> {
    
    private volatile File[] value;

    /**
     * Creates a new <tt>FileArraySetting</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
     */
    FileArraySetting(Properties defaultProps, Properties props, String key, 
                                                         File[] defaultValue) {
        super(defaultProps, props, key, decode(defaultValue));
        setPrivate(true);
    }
    
    /**
     * Returns the value of this setting.
     * 
     * @return the value of this setting
     */
    public File[] get() {
        return value;
    }

    /**
     * Mutator for this setting.
     *
     * @param value the value to store
     */
    public synchronized void set(File[] value) {
        setValueInternal(decode(value));
    }

    /**
     * Mutator for this setting.
     *
     * @param file file to the array
     */
    public synchronized void add(File file) {
        if (file == null)
            return;
        
        File[] newValue = new File[value.length+1];
        System.arraycopy(value, 0, newValue, 0, value.length);
        newValue[value.length] = file;
        set(newValue);
    }
    
    /**
     * 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 (file == null)
            return false;
        
        int index = indexOf(file);
        if (index == -1) {
            return false;
        }
        
        File[] newValue = new File[value.length-1];
        
        //  copy first half, up to first occurrence's index
        System.arraycopy(value, 0, newValue, 0, index);
        //  copy second half, for the length of the rest of the array
        System.arraycopy(value, index+1, newValue, index, value.length - index - 1);
        
        set(newValue);
        return true;
    }
    
    /** Returns true if the given file is contained in this array. */
    public synchronized boolean contains(File file) {
        return indexOf(file) >= 0;
    }
    
    /** Returns the index of the given file in this array, -1 if file is not found. */
    public synchronized int indexOf(File file) {
        if (file == null)
            return -1;

        for (int i = 0; i < value.length; i++) {
            try {
                if ((FileUtils.getCanonicalFile(value[i])).equals(FileUtils.getCanonicalFile(file)))
                    return i;
            } catch(IOException ioe) {
                continue;
            }
        }

        return -1;
    }

    /** Returns the length of the array.	 */
    public synchronized int length() {
        return value.length;
    }

    /** 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 an Array.     */
    private static File[] encode(String src) {
        
        if (src == null || src.length()==0) {
            return (new File[0]);
        }
        
        StringTokenizer tokenizer = new StringTokenizer(src, ";");
        File[] dirs = new File[tokenizer.countTokens()];
        for(int i = 0; i < dirs.length; i++) {
            dirs[i] = new File(tokenizer.nextToken());
        }
        
        return dirs;
    }
    
    /** Separates each field of the array by a semicolon.     */
    private static String decode(File[] src) {
        
        if (src == null || src.length==0) {
            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 from this. */
    public synchronized void clean() {
        List<File> list = new ArrayList<File>(value.length);
        File file;
        for (File aValue : value) {
            file = aValue;
            if (file == null)
                continue;
            if (!file.exists())
                continue;
            list.add(file);
        }
        set(list.toArray(new File[list.size()]));
    }
}
