// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.

package org.cef;

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

/**
 * Initialization settings. Specify NULL or 0 to get the recommended default
 * values. Many of these and other settings can also configured using command-
 * line switches.
 */
public class CefSettings {
    /**
     * Log severity levels.
     */
    public enum LogSeverity {
        /**
         * Default logging (currently INFO logging).
         */
        LOGSEVERITY_DEFAULT,

        /**
         * Verbose logging.
         */
        LOGSEVERITY_VERBOSE,

        /**
         * INFO logging.
         */
        LOGSEVERITY_INFO,

        /**
         * WARNING logging.
         */
        LOGSEVERITY_WARNING,

        /**
         * ERROR logging.
         */
        LOGSEVERITY_ERROR,

        /**
         * FATAL logging.
         */
        LOGSEVERITY_FATAL,

        /**
         * Completely disable logging.
         */
        LOGSEVERITY_DISABLE
    }

    /**
     * 32-bit ARGB color value, not premultiplied. The color components are always
     * in a known order. Equivalent to the SkColor type.
     */
    public class ColorType {
        private long color_value = 0;

        private ColorType() {}

        public ColorType(int alpha, int red, int green, int blue) {
            color_value = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
        }

        public long getColor() {
            return color_value;
        }

        @Override
        public ColorType clone() {
            ColorType res = new ColorType();
            res.color_value = this.color_value;
            return res;
        }
    }

    /**
     * The path to a separate executable that will be launched for sub-processes.
     * By default the browser process executable is used. See the comments on
     * CefExecuteProcess() for details. Also configurable using the
     * "browser-subprocess-path" command-line switch.
     */
    public String browser_subprocess_path = null;

    /**
     * Set to true to enable windowless (off-screen) rendering support. Do not
     * enable this value if the application does not use windowless rendering as
     * it may reduce rendering performance on some systems.
     */
    public boolean windowless_rendering_enabled = true;

    /**
     * Set to true to disable configuration of browser process features using
     * standard CEF and Chromium command-line arguments. Configuration can still
     * be specified using CEF data structures or via the
     * CefApp::OnBeforeCommandLineProcessing() method.
     */
    public boolean command_line_args_disabled = false;

    /**
     * The location where cache data will be stored on disk. If empty an in-memory
     * cache will be used for some features and a temporary disk cache for others.
     * HTML5 databases such as localStorage will only persist across sessions if a
     * cache path is specified.
     */
    public String cache_path = null;

    /**
     * To persist session cookies (cookies without an expiry date or validity
     * interval) by default when using the global cookie manager set this value to
     * true. Session cookies are generally intended to be transient and most Web
     * browsers do not persist them. A |cache_path| value must also be specified to
     * enable this feature. Also configurable using the "persist-session-cookies"
     * command-line switch.
     */
    public boolean persist_session_cookies = false;

    /**
     * Value that will be returned as the User-Agent HTTP header. If empty the
     * default User-Agent string will be used. Also configurable using the
     * "user-agent" command-line switch.
     */
    public String user_agent = null;

    /**
     * Value that will be inserted as the product portion of the default
     * User-Agent string. If empty the Chromium product version will be used. If
     * |userAgent| is specified this value will be ignored. Also configurable
     * using the "user_agent_product" command-line switch.
     */
    public String user_agent_product = null;

    /**
     * The locale string that will be passed to Blink. If empty the default
     * locale of "en-US" will be used. This value is ignored on Linux where locale
     * is determined using environment variable parsing with the precedence order:
     * LANGUAGE, LC_ALL, LC_MESSAGES and LANG. Also configurable using the "lang"
     * command-line switch.
     */
    public String locale = null;

    /**
     * The directory and file name to use for the debug log. If empty, the
     * default name of "debug.log" will be used and the file will be written
     * to the application directory. Also configurable using the "log-file"
     * command-line switch.
     */
    public String log_file = null;

    /**
     * The log severity. Only messages of this severity level or higher will be
     * logged. Also configurable using the "log-severity" command-line switch with
     * a value of "verbose", "info", "warning", "error", "error-report" or
     * "disable".
     */
    public LogSeverity log_severity = LogSeverity.LOGSEVERITY_DEFAULT;

    /**
     * Custom flags that will be used when initializing the V8 JavaScript engine.
     * The consequences of using custom flags may not be well tested. Also
     * configurable using the "js-flags" command-line switch.
     */
    public String javascript_flags = null;

    /**
     * The fully qualified path for the resources directory. If this value is
     * empty the cef.pak and/or devtools_resources.pak files must be located in
     * the module directory on Windows/Linux or the app bundle Resources directory
     * on Mac OS X. Also configurable using the "resources-dir-path" command-line
     * switch.
     */
    public String resources_dir_path = null;

    /**
     * The fully qualified path for the locales directory. If this value is empty
     * the locales directory must be located in the module directory. This value
     * is ignored on Mac OS X where pack files are always loaded from the app
     * bundle Resources directory. Also configurable using the "locales-dir-path"
     * command-line switch.
     */
    public String locales_dir_path = null;

    /**
     * Set to a value between 1024 and 65535 to enable remote debugging on the
     * specified port. For example, if 8080 is specified the remote debugging URL
     * will be http: *localhost:8080. CEF can be remotely debugged from any CEF or
     * Chrome browser window. Also configurable using the "remote-debugging-port"
     * command-line switch.
     */
    public int remote_debugging_port = 0;

    /**
     * The number of stack trace frames to capture for uncaught exceptions.
     * Specify a positive value to enable the CefV8ContextHandler::
     * OnUncaughtException() callback. Specify 0 (default value) and
     * OnUncaughtException() will not be called. Also configurable using the
     * "uncaught-exception-stack-size" command-line switch.
     */
    public int uncaught_exception_stack_size = 0;

    /**
     * Opaque background color used for accelerated content. By default the
     * background color will be white. Only the RGB compontents of the specified
     * value will be used. The alpha component must greater than 0 to enable use
     * of the background color but will be otherwise ignored.
     */
    public ColorType background_color = null;

    ///
    // Comma delimited list of schemes supported by the associated
    // CefCookieManager. If |cookieable_schemes_exclude_defaults| is false (0) the
    // default schemes ("http", "https", "ws" and "wss") will also be supported.
    // Specifying a |cookieable_schemes_list| value and setting
    // |cookieable_schemes_exclude_defaults| to true (1) will disable all loading
    // and saving of cookies for this manager. Can be overridden
    // for individual CefRequestContext instances via the
    // CefRequestContextSettings.cookieable_schemes_list and
    // CefRequestContextSettings.cookieable_schemes_exclude_defaults values.
    ///
    public String cookieable_schemes_list = null;
    public boolean cookieable_schemes_exclude_defaults = false;
    public boolean no_sandbox = !Boolean.getBoolean("jcef.use_sandbox");

    public CefSettings() {}

    @Override
    public CefSettings clone() {
        CefSettings tmp = new CefSettings();
        tmp.browser_subprocess_path = browser_subprocess_path;
        tmp.windowless_rendering_enabled = windowless_rendering_enabled;
        tmp.command_line_args_disabled = command_line_args_disabled;
        tmp.cache_path = cache_path;
        tmp.persist_session_cookies = persist_session_cookies;
        tmp.user_agent = user_agent;
        tmp.user_agent_product = user_agent_product;
        tmp.locale = locale;
        tmp.log_file = log_file;
        tmp.log_severity = log_severity;
        tmp.javascript_flags = javascript_flags;
        tmp.resources_dir_path = resources_dir_path;
        tmp.locales_dir_path = locales_dir_path;
        tmp.remote_debugging_port = remote_debugging_port;
        tmp.uncaught_exception_stack_size = uncaught_exception_stack_size;
        if (background_color != null) tmp.background_color = background_color.clone();
        tmp.cookieable_schemes_list = cookieable_schemes_list;
        tmp.cookieable_schemes_exclude_defaults = cookieable_schemes_exclude_defaults;
        tmp.no_sandbox = no_sandbox;
        return tmp;
    }
    
    public String getDescription() {
        StringBuilder sb = new StringBuilder();
        sb.append("browser_subprocess_path=" + browser_subprocess_path + ", ");
        sb.append("windowless_rendering_enabled=" + windowless_rendering_enabled + ", ");
        sb.append("command_line_args_disabled=" + command_line_args_disabled + ", ");
        sb.append("cache_path=" + cache_path + ", ");
        sb.append("persist_session_cookies=" + persist_session_cookies + ", ");
        sb.append("user_agent=" + user_agent + ", ");
        sb.append("user_agent_product=" + user_agent_product + ", ");
        sb.append("locale=" + locale + ", ");
        sb.append("log_file=" + log_file + ", ");
        sb.append("log_severity=" + log_severity + ", ");
        sb.append("javascript_flags=" + javascript_flags + ", ");
        sb.append("resources_dir_path=" + resources_dir_path + ", ");
        sb.append("locales_dir_path=" + locales_dir_path + ", ");
        sb.append("remote_debugging_port=" + remote_debugging_port + ", ");
        sb.append("uncaught_exception_stack_size=" + uncaught_exception_stack_size + ", ");
        if (background_color != null) sb.append("background_color=" + background_color.clone() + ", ");
        sb.append("cookieable_schemes_list=" + cookieable_schemes_list + ", ");
        sb.append("cookieable_schemes_exclude_defaults=" + cookieable_schemes_exclude_defaults + ", ");
        sb.append("no_sandbox=" + no_sandbox);
        return sb.toString();
    }

    private static void putNonEmpty(Map<String, String> result, String key, String value) {
        if (value != null && !value.isEmpty())
            result.put(key, value);
    }

    public Map<String, String> toMap() {
        Map<String, String> result = new HashMap<>();
        putNonEmpty(result, "browser_subprocess_path", browser_subprocess_path);
        putNonEmpty(result, "windowless_rendering_enabled", String.valueOf(windowless_rendering_enabled));
        putNonEmpty(result, "command_line_args_disabled", String.valueOf(command_line_args_disabled));
        putNonEmpty(result, "cache_path", cache_path);
        putNonEmpty(result, "persist_session_cookies", String.valueOf(persist_session_cookies));
        putNonEmpty(result, "user_agent", user_agent);
        putNonEmpty(result, "user_agent_product", user_agent_product);
        putNonEmpty(result, "locale", locale);
        putNonEmpty(result, "log_file", log_file);
        putNonEmpty(result, "log_severity", String.valueOf(log_severity));
        putNonEmpty(result, "javascript_flags", javascript_flags);
        putNonEmpty(result, "resources_dir_path", resources_dir_path);
        putNonEmpty(result, "locales_dir_path", locales_dir_path);
        putNonEmpty(result, "remote_debugging_port", String.valueOf(remote_debugging_port));
        putNonEmpty(result, "uncaught_exception_stack_size", String.valueOf(uncaught_exception_stack_size));
        if (background_color != null) putNonEmpty(result, "background_color", String.valueOf(background_color.getColor()));
        putNonEmpty(result, "cookieable_schemes_list", cookieable_schemes_list);
        putNonEmpty(result, "cookieable_schemes_exclude_defaults", String.valueOf(cookieable_schemes_exclude_defaults));
        putNonEmpty(result, "no_sandbox", String.valueOf(no_sandbox));
        return result;
    }

    // Returns true when all SIGNIFICANT fields are equal.
    public boolean isAlmostEqual(CefSettings other) {
            return Objects.equals(cache_path, other.cache_path)
                    && Objects.equals(persist_session_cookies, other.persist_session_cookies)
                    && Objects.equals(user_agent, other.user_agent)
                    && Objects.equals(user_agent_product, other.user_agent_product)
                    && Objects.equals(locale, other.locale)
                    && Objects.equals(javascript_flags, other.javascript_flags)
                    && Objects.equals(remote_debugging_port, other.remote_debugging_port)
                    && Objects.equals(uncaught_exception_stack_size, other.uncaught_exception_stack_size)
                    && Objects.equals(cookieable_schemes_list, other.cookieable_schemes_list)
                    && Objects.equals(cookieable_schemes_exclude_defaults, other.cookieable_schemes_exclude_defaults);

    }

}
