package org.limewire.core.settings;

import java.io.File;
import java.io.IOException;
import java.util.Hashtable;

import org.limewire.core.api.Category;
import org.limewire.setting.BooleanSetting;
import org.limewire.setting.FileSetting;
import org.limewire.setting.IntSetting;
import org.limewire.setting.Setting;
import org.limewire.setting.StringSetting;
import org.limewire.util.CommonUtils;
import org.limewire.util.FileUtils;
import org.limewire.util.OSUtils;
import org.limewire.util.SystemUtils;
import org.limewire.util.SystemUtils.SpecialLocations;

/**
 * Settings for sharing.
 */
public class SharingSettings extends LimeProps {

    private SharingSettings() {
    }

    /**
     * Stores the download directory file settings for each media type by its
     * description key {@link Category#getSchemaName()}. The settings are
     * loaded lazily during the first request.
     */
    private static final Hashtable<String, FileSetting> downloadDirsByDescription = new Hashtable<String, FileSetting>();

    public static final File DEFAULT_SAVE_DIR = new File(getLimeWireRootFolder(), "Saved");

    public static final File DEFAULT_SHARE_DIR = new File(getLimeWireRootFolder(), "Shared");

    /**
     * Default directory for songs purchased from LWS.
    public static final File DEFAULT_SAVE_LWS_DIR = new File(getLimeWireRootFolder(),
            "Store Purchased");
	*/
    public static final String DEFAULT_LWS_FILENAME_TEMPLATE = "<artist> - <album> - <track> - <title>";

    public static final String DEFAULT_LWS_FOLDER_TEMPLATE = "<artist>" + File.separatorChar + "<album>";

    /**
     * Whether or not we're going to add an alternate for ourselves to our
     * shared files. Primarily set to false for testing.
     */
    public static final BooleanSetting ADD_ALTERNATE_FOR_SELF = FACTORY.createBooleanSetting(
            "ADD_ALTERNATE_FOR_SELF", true);

    /**
     * The directory for saving files.
     */
    public static final FileSetting DIRECTORY_FOR_SAVING_FILES = FACTORY.createFileSetting(
            "DIRECTORY_FOR_SAVING_FILES", DEFAULT_SAVE_DIR).setAlwaysSave(true);
    
    /**
     * Template for substructure when saving songs purchased from LimeWire Store
     * (LWS). The template allows purchased songs to be saved in a unique
     * fashion, ie. LWS_dir/artist/album/songX.mp3.
     */
    public static final StringSetting TEMPLATE_SUBDIRECTORY_LWS_FILES = FACTORY
            .createStringSetting("TEMPLATE_FOR_SAVING_LWS_FILES", DEFAULT_LWS_FOLDER_TEMPLATE);

    /**
     * Template for file name structure when saving songs purchased from the
     * LimeWire Store (LWS). The template allows purchased songs to be named in a
     * unique fashion based on the songs meta data ie. artist - track # -
     * title.mp3.
     */
    public static final StringSetting TEMPLATE_FOR_NAMING_LWS_FILES = FACTORY.createStringSetting(
            "TEMPLATE_FOR_NAMING_LWS_FILES", DEFAULT_LWS_FILENAME_TEMPLATE);

    /**
     * The directory where incomplete files are stored (downloads in progress).
     */
    public static final FileSetting INCOMPLETE_DIRECTORY = FACTORY.createFileSetting(
            "INCOMPLETE_DIRECTORY", (new File(DIRECTORY_FOR_SAVING_FILES.get().getParent(),
                    "Incomplete")));

    /**
     * A file with a snapshot of current downloading files.
     */
    public static final FileSetting OLD_DOWNLOAD_SNAPSHOT_FILE = FACTORY.createFileSetting(
            "DOWNLOAD_SNAPSHOT_FILE", (new File(INCOMPLETE_DIRECTORY.get(), "downloads.dat")));

    /**
     * A file with a snapshot of current downloading files.
     */
    public static final FileSetting OLD_DOWNLOAD_SNAPSHOT_BACKUP_FILE = FACTORY.createFileSetting(
            "DOWNLOAD_SNAPSHOT_BACKUP_FILE",
            (new File(INCOMPLETE_DIRECTORY.get(), "downloads.bak")));

    /**
     * The minimum age in days for which incomplete files will be deleted. This
     * values may be zero or negative; doing so will cause LimeWire to delete
     * ALL incomplete files on startup.
     */
    public static final IntSetting INCOMPLETE_PURGE_TIME = FACTORY.createIntSetting(
            "INCOMPLETE_PURGE_TIME", 7);

    /**
     * The time, in days, after which .torrent meta data files are deleted.
     */
    public static final IntSetting TORRENT_METADATA_PURGE_TIME = FACTORY.createIntSetting(
            "TORRENT_METADATA_PURGE_TIME", 7);

    /**
     * Specifies whether or not completed downloads should automatically be
     * cleared from the download window.
     */
    public static final BooleanSetting CLEAR_DOWNLOAD = FACTORY.createBooleanSetting(
            "CLEAR_DOWNLOAD", false);
    
    /**
     * Specifies whether or not to warn the user when they are about to share a folder.
     */
    public static final BooleanSetting WARN_SHARING_FOLDER = FACTORY.createBooleanSetting(
            "WARN_SHARING_FOLDER", true);
    
    /**
     * Specifies whether or not to warn the user when they are sharing documents with the world.
     */
    public static final BooleanSetting WARN_SHARING_DOCUMENTS_WITH_WORLD = FACTORY.createBooleanSetting(
            "WARN_SHARING_DOCUMENTS_WITH_WORLD", true);
    
    /**
     * Helper method left from SettingsManager.
     * <p>
     * Sets the directory for saving files.
     * <p>
     * <b>Modifies:</b> DIRECTORY_FOR_SAVING_FILES, INCOMPLETE_DIRECTORY,
     * DOWNLOAD_SNAPSHOT_FILE
     * 
     * @param saveDir a <tt>File</tt> instance denoting the abstract pathname of
     *        the directory for saving files.
     * 
     * @throws <tt>IOException</tt> if the directory denoted by the directory
     *         pathname String parameter did not exist prior to this method call
     *         and could not be created, or if the canonical path could not be
     *         retrieved from the file system.
     * 
     * @throws <tt>NullPointerException</tt> If the "dir" parameter is null.
     */
    public static final void setSaveDirectory(File saveDir) throws IOException {
        if (saveDir == null)
            throw new NullPointerException();
        if (!saveDir.isDirectory()) {
            if (!saveDir.mkdirs())
                throw new IOException("could not create save dir");
        }

        String parentDir = saveDir.getParent();
        File incDir = new File(parentDir, "Incomplete");
        if (!incDir.isDirectory()) {
            if (!incDir.mkdirs())
                throw new IOException("could not create incomplete dir");
        }

        FileUtils.setWriteable(saveDir);
        FileUtils.setWriteable(incDir);

        if (!saveDir.canRead() || !FileUtils.canWrite(saveDir) || !incDir.canRead()
                || !FileUtils.canWrite(incDir)) {
            throw new IOException("could not write to selected directory");
        }

        // Canonicalize the files ...
        try {
            saveDir = FileUtils.getCanonicalFile(saveDir);
        } catch (IOException ignored) {
        }
        try {
            incDir = FileUtils.getCanonicalFile(incDir);
        } catch (IOException ignored) {
        }
        File snapFile = new File(incDir, "downloads.dat");
        try {
            snapFile = FileUtils.getCanonicalFile(snapFile);
        } catch (IOException ignored) {
        }
        File snapBackup = new File(incDir, "downloads.bak");
        try {
            snapBackup = FileUtils.getCanonicalFile(snapBackup);
        } catch (IOException ignored) {
        }

        DIRECTORY_FOR_SAVING_FILES.set(saveDir);
        INCOMPLETE_DIRECTORY.set(incDir);
        OLD_DOWNLOAD_SNAPSHOT_FILE.set(snapFile);
        OLD_DOWNLOAD_SNAPSHOT_BACKUP_FILE.set(snapBackup);
    }

    /**
     * Retrieves the default save directory for a given file name. Getting the
     * save directory for null will result in the default save directory.
     */
    public static final File getSaveDirectory(Category category) {
        if(category == null) {
            return DIRECTORY_FOR_SAVING_FILES.get();
        } else {
            FileSetting fs = getFileSettingForCategory(category);
            if (fs.isDefault()) {
                return DIRECTORY_FOR_SAVING_FILES.get();
            }
            return fs.get();
        }
    }
    
    public static final File getSaveDirectory() {
        return DIRECTORY_FOR_SAVING_FILES.get();
    }

    /**
     * Sets the template for creating sub directories of store files using
     * metadata.
     * 
     * @param template the template that describes the sub directory structure
     * @throws NullPointerException if template is null
     */
    public static final void setSubdirectoryLWSTemplate(String template) {
        if (template == null)
            throw new NullPointerException();
        TEMPLATE_SUBDIRECTORY_LWS_FILES.set(template);
    }

    /**
     * @return template of how to create subdirectories of store files If no
     *         subdirectory template is used, will return ""
     */
    public static final String getSubDirectoryLWSTemplate() {
        return TEMPLATE_SUBDIRECTORY_LWS_FILES.get();
    }

    /**
     * Sets the template for naming files purchased from the LWS.
     * 
     * @param template the template that describes how id3 information should be
     *        used to name a Store file
     * @throws NullPointerException if template is null
     */
    public static final void setFileNameLWSTemplate(String template) {
        if (template == null)
            throw new NullPointerException();
        TEMPLATE_FOR_NAMING_LWS_FILES.set(template);
    }

    /**
     * @return template of how to name LWS files.
     */
    public static final String getFileNameLWSTemplate() {
        return TEMPLATE_FOR_NAMING_LWS_FILES.get();
    }

    /*********************************************************************/

    /**
     * Whether or not to auto-share files when using 'Download As'.
     */
    public static final BooleanSetting SHARE_DOWNLOADED_FILES_IN_NON_SHARED_DIRECTORIES = FACTORY
            .createBooleanSetting("SHARE_DOWNLOADED_FILES_IN_NON_SHARED_DIRECTORIES", true);

    /**
     * Sets the probability (expressed as a percentage) that an incoming
     * freeloader will be accepted. For example, if allowed==50, an incoming
     * connection has a 50-50 chance being accepted. If allowed==100, all
     * incoming connections are accepted.
     */
    public static final IntSetting FREELOADER_ALLOWED = FACTORY.createIntSetting(
            "FREELOADER_ALLOWED_2", 100);

    /**
     * Minimum the number of files a host must share to not be considered a
     * freeloader. For example, if files==0, no host is considered a freeloader.
     */
    public static final IntSetting FREELOADER_FILES = FACTORY.createIntSetting(
            "FREELOADER_FILES_2", 1);

    /**
     * The timeout value for persistent HTTP connections in milliseconds.
     */
    public static final IntSetting PERSISTENT_HTTP_CONNECTION_TIMEOUT = FACTORY.createIntSetting(
            "PERSISTENT_HTTP_CONNECTION_TIMEOUT", 15000);

    /**
     * Specifies whether or not completed uploads should automatically be
     * cleared from the upload window.
     */
    public static final BooleanSetting CLEAR_UPLOAD = FACTORY.createBooleanSetting("CLEAR_UPLOAD",
            true);

    /**
     * Whether or not browsers should be allowed to perform uploads.
     */
    public static final BooleanSetting ALLOW_BROWSER = FACTORY.createBooleanSetting(
            "ALLOW_BROWSER", false);

    /**
     * Whether to throttle hashing of shared files.
     */
    public static final BooleanSetting FRIENDLY_HASHING = FACTORY.createBooleanSetting(
            "FRIENDLY_HASHING", true);

    /**
     * Minimum idle time before we start to hash at full throttle.
     */
    public static final IntSetting MIN_IDLE_TIME_FOR_FULL_HASHING = FACTORY.createIntSetting(
            "MIN_IDLE_TIME_FOR_FULL_HASHING", 5 * 60 * 1000);

    /**
     * Setting for the threshold of when to warn the user that a lot of files
     * are being shared.
     */
    public static final IntSetting FILES_FOR_WARNING = FACTORY.createIntSetting(
            "FILES_FOR_WARNING", 1000);

    /**
     * Setting for the threshold of when to warn the user that a lot of files
     * are being shared.
     */
    public static final IntSetting DEPTH_FOR_WARNING = FACTORY.createIntSetting(
            "DEPTH_FOR_WARNING", 4);

    /**
     * Returns the download directory file setting for a mediatype. The settings
     * are created lazily when they are requested for the first time. The
     * default download directory is a file called "invalidfile" the file
     * setting should not be used when its {@link Setting#isDefault()} returns
     * true. Use {@link #DIRECTORY_FOR_SAVING_FILES} instead then.
     * 
     * @param type the mediatype for which to look up the file setting
     * @return the filesetting for the media type
     */
    public static final FileSetting getFileSettingForCategory(Category category) {
        String schema = category.getSchemaName();
        FileSetting setting = downloadDirsByDescription.get(schema);
        if (setting == null) {
            setting = FACTORY.createProxyFileSetting("DIRECTORY_FOR_SAVING_" + schema
                    + "_FILES", DIRECTORY_FOR_SAVING_FILES);
            downloadDirsByDescription.put(schema, setting);
        }
        return setting;
    }

    /**
     * The Creative Commons explanation URL.
     */
    public static final StringSetting CREATIVE_COMMONS_INTRO_URL = FACTORY
            .createRemoteStringSetting("CREATIVE_COMMONS_URL",
                    "http://creativecommons.org/about/licenses/how1");

    /**
     * The Creative Commons verification explanation URL.
     */
    public static final StringSetting CREATIVE_COMMONS_VERIFICATION_URL = FACTORY
            .createRemoteStringSetting("CREATIVE_COMMONS_VERIFICATION_URL",
                    "http://creativecommons.org/technology/embedding#2");

    /**
     * Setting for whether or not to allow partial files to be shared.
     */
    public static final BooleanSetting ALLOW_PARTIAL_SHARING = FACTORY.createBooleanSetting(
            "ALLOW_PARTIAL_SHARING", true);

    /**
     * Remote switch to turn off partial results.
     */
    public static final BooleanSetting ALLOW_PARTIAL_RESPONSES = FACTORY
            .createRemoteBooleanSetting("ALLOW_PARTIAL_RESPONSES", true);

    /**
     * Maximum size in bytes for the encoding of available ranges per Response
     * object.
     */
    public static final IntSetting MAX_PARTIAL_ENCODING_SIZE = FACTORY.createRemoteIntSetting(
            "MAX_PARTIAL_ENCODING_SIZE", 20);

    /**
     * Whether to publish keywords from partial files in the qrp.
     */
    public static final BooleanSetting PUBLISH_PARTIAL_QRP = FACTORY.createRemoteBooleanSetting(
            "PUBLISH_PARTIAL_QRP", true);

    /**
     * Whether to load keywords from incomplete files in the trie.
     */
    public static final BooleanSetting LOAD_PARTIAL_KEYWORDS = FACTORY.createRemoteBooleanSetting(
            "LOAD_PARTIAL_KEYWORDS", true);

    public static final StringSetting LAST_WARNED_SAVE_DIRECTORY = FACTORY.createStringSetting(
            "LAST_WARNED_SAVED_DIRECTORY", "");

    /**
     * Returns the root folder from which all Saved/Shared/etc.. folders should
     * be placed.
     */
    private static File getLimeWireRootFolder() {
        String root = null;

        if (OSUtils.isWindows()) {
            root = SystemUtils.getSpecialPath(SpecialLocations.DOCUMENTS);
        }

        if (root == null || "".equals(root))
            root = CommonUtils.getUserHomeDir().getPath();

        return new File(root, "WireShare");
    }    

}
