package com.srbenoit.log;

import java.io.File;
import java.text.MessageFormat;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.srbenoit.util.ResourceLoader;

/**
 * Properties used by the logging classes.
 */
public final class LogProperties {

    /** the key of the log level property */
    public static final String LEVEL_TAG = "log-level";

    /** the key of the log file path property */
    public static final String FILE_PATH_TAG = "log-file-path";

    /** the key of the log file size limit property */
    public static final String FILE_LIMIT_TAG = "log-file-size-limit";

    /** the key of the log file count property */
    public static final String FILE_COUNT_TAG = "log-file-count";

    /** the key of the log file append property */
    public static final String APPEND_TAG = "log-file-append";

    /** the default file limit. */
    private static final int DEF_FILE_LIMIT = 100000;

    /** the default file count. */
    private static final int DEF_FILE_COUNT = 1;

    /** the configured logging level */
    private transient Level logLevel;

    /** the configured log file path */
    private transient final String logFilePath;

    /** the configured log file size limit */
    private transient int logFileSizeLimit;

    /** the configured log file count */
    private transient int logFileCount;

    /** true to append to existing files; false to overwrite */
    private transient boolean logFileAppend;

    /**
     * Private constructor so the factory method is always used to load.
     *
     * @param  configDir  the configuration data directory
     * @param  res        the resource bundle containing localized strings
     * @param  props      the loaded properties object
     * @param  log        a log to which to write diagnostic messages
     */
    private LogProperties(final File configDir, final ResourceBundle res, final Properties props,
        final Logger log) {

        String str;

        configureLogLevel(res, props, log);
        configureFileLimit(res, props, log);
        configureFileCount(res, props, log);
        configureAppend(res, props, log);

        str = props.getProperty(FILE_PATH_TAG);

        if (configDir == null) {
            this.logFilePath = str;
        } else if (str == null) {
            this.logFilePath = null;
        } else {
            this.logFilePath = str.replace("%dir", configDir.getAbsolutePath());
        }
    }

    /**
     * Configures the log level from the properties.
     *
     * @param  res    the resource bundle containing localized strings
     * @param  props  the loaded properties object
     * @param  log    a log to which to write diagnostic messages
     */
    private void configureLogLevel(final ResourceBundle res, final Properties props,
        final Logger log) {

        String str;
        String msg;

        str = props.getProperty(LEVEL_TAG);

        if (str == null) {
            msg = res.getString(LogRes.MISSING_PARAM);
            log.warning(MessageFormat.format(msg, LEVEL_TAG, "ALL"));
            this.logLevel = Level.ALL;
        } else {

            try {
                this.logLevel = Level.parse(str);
            } catch (IllegalArgumentException e) {
                msg = res.getString(LogRes.BAD_PARAM);
                log.warning(MessageFormat.format(msg, LEVEL_TAG, str, "ALL"));
                this.logLevel = Level.ALL;
            }
        }
    }

    /**
     * Configures the log file limit from the properties.
     *
     * @param  res    the resource bundle containing localized strings
     * @param  props  the loaded properties object
     * @param  log    a log to which to write diagnostic messages
     */
    private void configureFileLimit(final ResourceBundle res, final Properties props,
        final Logger log) {

        String str;
        String msg;

        str = props.getProperty(FILE_LIMIT_TAG);

        if (str == null) {
            msg = res.getString(LogRes.MISSING_PARAM);
            log.warning(MessageFormat.format(msg, FILE_LIMIT_TAG,
                    Integer.toString(DEF_FILE_LIMIT)));
            this.logFileSizeLimit = DEF_FILE_LIMIT;
        } else {

            try {
                this.logFileSizeLimit = Integer.parseInt(str);
            } catch (NumberFormatException e) {
                msg = res.getString(LogRes.BAD_PARAM);
                log.warning(MessageFormat.format(msg, FILE_LIMIT_TAG, str,
                        Integer.toString(DEF_FILE_LIMIT)));
                this.logFileSizeLimit = DEF_FILE_LIMIT;
            }
        }
    }

    /**
     * Configures the log file limit from the properties.
     *
     * @param  res    the resource bundle containing localized strings
     * @param  props  the loaded properties object
     * @param  log    a log to which to write diagnostic messages
     */
    private void configureFileCount(final ResourceBundle res, final Properties props,
        final Logger log) {

        String str;
        String msg;

        str = props.getProperty(FILE_COUNT_TAG);

        if (str == null) {
            msg = res.getString(LogRes.MISSING_PARAM);
            log.warning(MessageFormat.format(msg, FILE_COUNT_TAG,
                    Integer.toString(DEF_FILE_COUNT)));
            this.logFileCount = DEF_FILE_COUNT;
        } else {

            try {
                this.logFileCount = Integer.parseInt(str);
            } catch (NumberFormatException e) {
                msg = res.getString(LogRes.BAD_PARAM);
                log.warning(MessageFormat.format(msg, FILE_LIMIT_TAG, str,
                        Integer.toString(DEF_FILE_LIMIT)));
                this.logFileCount = DEF_FILE_COUNT;
            }
        }
    }

    /**
     * Configures the log file limit from the properties.
     *
     * @param  res    the resource bundle containing localized strings
     * @param  props  the loaded properties object
     * @param  log    a log to which to write diagnostic messages
     */
    private void configureAppend(final ResourceBundle res, final Properties props,
        final Logger log) {

        String str;
        String msg;

        str = props.getProperty(APPEND_TAG);

        if (str == null) {
            msg = res.getString(LogRes.MISSING_PARAM);
            log.warning(MessageFormat.format(msg, APPEND_TAG, "false"));
            this.logFileAppend = false;
        } else {

            try {
                this.logFileAppend = Boolean.parseBoolean(str);
            } catch (NumberFormatException e) {
                msg = res.getString(LogRes.BAD_PARAM);
                log.warning(MessageFormat.format(msg, APPEND_TAG, str, "false"));
                this.logFileAppend = false;
            }
        }
    }

    /**
     * Gets the log level.
     *
     * @return  the log level
     */
    public Level getLogLevel() {

        return this.logLevel;
    }

    /**
     * Gets the log file path.
     *
     * @return  the log file path
     */
    public String getLogFilePath() {

        return this.logFilePath;
    }

    /**
     * Gets the log file size limit.
     *
     * @return  the log file size limit
     */
    public int getLogFileSizeLimit() {

        return this.logFileSizeLimit;
    }

    /**
     * Gets the log file count.
     *
     * @return  the log file count
     */
    public int getLogFileCount() {

        return this.logFileCount;
    }

    /**
     * Gets the log file append flag.
     *
     * @return  <code>true</code> if starting logging will append to an existing log file, <code>
     *          false</code> if it will overwrite
     */
    public boolean isLogFileAppend() {

        return this.logFileAppend;
    }

    /**
     * Loads the properties from the configuration directory, or supplies a set of default
     * properties if the properties file cannot be loaded from that location.
     *
     * @param   configDir  the configuration data directory
     * @param   base       the base name (without a language extension) of the properties file to
     *                     load
     * @param   res        the resource bundle containing localized strings
     * @param   log        a log to which to write diagnostic messages
     * @return  the properties (either what we could load, or a set of defaults), but never <code>
     *          null</code>
     */
    public static LogProperties load(final File configDir, final Class<?> base,
        final ResourceBundle res, final Logger log) {

        Properties props;
        String msg;

        if (configDir == null) {
            props = ResourceLoader.loadProperties(base, base.getSimpleName());
        } else {
            props = ResourceLoader.loadProperties(configDir, base.getSimpleName());
        }

        if (props == null) {
            msg = res.getString(LogRes.CANT_LOAD_PROPS);
            log.warning(MessageFormat.format(msg, configDir));
            props = new Properties();
        }

        return new LogProperties(configDir, res, props, log);
    }
}
