/**
 * Copyright (C) 2004-2011 Jive Software. All rights reserved.
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jivesoftware.sparkimpl.settings.local;

import org.apache.commons.lang3.StringUtils;
import org.jivesoftware.Spark;
import org.jivesoftware.resource.Default;
import org.jivesoftware.resource.Res;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.spark.PluginRes;
import org.jivesoftware.spark.SparkManager;

import java.io.File;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import javax.swing.UIManager;

import org.jivesoftware.spark.util.Encryptor;
import org.jivesoftware.spark.util.log.Log;
import org.jivesoftware.sparkimpl.settings.Sizes;
import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.jid.parts.Resourcepart;

import static org.apache.commons.lang3.StringUtils.isBlank;

/**
 * Application preferences (from the "spark.properties").
 */
public class LocalPreferences {
    private final Properties props;

    LocalPreferences(Properties props) {
        this.props = props;
    }

    Properties getProperties() {
        return props;
    }

    /**
     * Returns the XMPP Port to communicate on. Default is 5222.
     */
    public int getXmppPort() {
        return getInt("xmppPort", Default.getInt(Default.XMPP_PORT));
    }

    public void setXmppPort(int xmppPort) {
        setInt("xmppPort", xmppPort);
    }

    public void setGroupChatPassword(String roomName, String password) {
        String pw = Encryptor.encrypt(password);
        setString(roomName, pw);
    }

    /**
     * Returns decrypted room's password.
     */
    public String getGroupChatPassword(String roomName) {
        String propVal = getString(roomName);
        return !isBlank(propVal) ? Encryptor.decrypt(propVal) : null;
    }

    /**
     * Return the smack timeout for requests. Default is 5 seconds.
     */
    public int getTimeOut() {
        return getInt("timeout", Default.getInt(Default.TIME_OUT));
    }

    public void setTimeOut(int timeOut) {
        setInt("timeout", timeOut);
    }

    public int getReconnectDelay() {
        return getInt("reconnectDelay", 10);
    }

    public void setReconnectDelay(int reconnectDelay) {
        setInt("reconnectDelay", reconnectDelay);
    }

    /**
     * Returns room's password.
     */
    public String getPasswordForRoom(String room) {
        return getString(room);
    }

    /**
     * returns the password for an encrypted jid
     */
    public String getPasswordForUser(String barejid) {
        String pw = "password" + Encryptor.encrypt(barejid);
        return Encryptor.decrypt(getString(pw));
    }

    /**
     * Sets the password for barejid<br>
     * both will be encrypted
     */
    public void setPasswordForUser(EntityBareJid bareJid, String password) {
        String userPasswordProp = "password" + Encryptor.encrypt(bareJid.toString());
        if (password == null) {
            props.remove(userPasswordProp);
            return;
        }
        // Don't save empty password, this can be a bug
        if (password.isEmpty()) {
            Log.error("The password for " + bareJid + " is empty");
            return;
        }
        String pw = Encryptor.encrypt(password);
        setString(userPasswordProp, pw);
    }

    /**
     * Checks if the preferences contain any stored/saved account passwords.
     */
    public boolean hasStoredPasswords() {
        return !findPropertyNamesForStoredPasswords().isEmpty();
    }

    /**
     * Remove all previously saved user-account passwords.
     */
    public void clearPasswordForAllUsers() {
        findPropertyNamesForStoredPasswords().forEach(props::remove);
    }

    /**
     * Find the name of all properties that store a password.
     */
    private List<String> findPropertyNamesForStoredPasswords() {
        List<String> passwordProps = new ArrayList<>();
        for (var prop : props.entrySet()) {
            String name = (String) prop.getKey();
            if (!name.startsWith("password") || name.equals("passwordSaved")) {
                continue;
            }
            String value = (String) prop.getValue();
            try {
                Encryptor.decrypt(value);
            } catch (Exception ignored) {
                continue;
            }
            String jidPart = name.substring("password".length());
            try {
                JidCreate.bareFrom(Encryptor.decryptOrThrow(jidPart));
                passwordProps.add(name);
            } catch (Exception ignored) {
                // Skip properties with invalid JID
                continue;
            }
        }
        return passwordProps;
    }

    /**
     * Return true if the IDLE feature is on. The IDLE feature allows to monitor
     * computer activity and set presence accordingly.
     */
    public boolean isIdleOn() {
        return getBoolean("idleOn", true);
    }

    public void setIdleOn(boolean idleOn) {
        setBoolean("idleOn", idleOn);
    }

    /**
     * Is "Use ad hoc room" enabled
     * When disabled, if there is at least one bookmark room, that
     * room will be used in Actions/Start conference room Invitation Dialog (instead of creating an ad-hoc room)
     * If there is more than one bookmark room, you can select the bookmarked room that you want to be used
     * Also, when invite to join group chat room will be sent from the chat window, the bookmark room will automatically
     * be opened.
     * @return true if it is wanted that a new ad hoc room to be created every time Actions/Start conference room is chosen,
     * 	 or, from a chat window - invite to group chat room icon is pressed.
     * 	 Returns false if it is wanted that the bookmarked room (if any) to be opened every time Actions/Start conference room is chosen,
     * 	 or, from a chat window - invite to group chat room icon is pressed.
     */
    public boolean isUseAdHocRoom() {
        String adhoc = PluginRes.getPreferenceRes("useAdHocRoom");
        return getBoolean("useAdHocRoom", adhoc == null || Boolean.parseBoolean(adhoc));
    }

    public void setUseAdHocRoom(boolean adHocRoom) {
        setBoolean("useAdHocRoom", adHocRoom);
    }

    /**
     * Returns the Idle Message to Display when going automatically away
     */
    public String getIdleMessage() {
        return getString("idleOnMessage", Res.getString("status.away"));
    }

    public void setIdleMessage(String message) {
        setString("idleOnMessage", message);
    }

    /**
     * Returns the number of minutes to set to unavailable if the computer has
     * no activity.
     */
    public int getIdleTime() {
        return getInt("idleTime", Default.getInt(Default.IDLE_TIME));
    }

    public void setIdleTime(int idleTime) {
        setInt("idleTime", idleTime);
    }

    /**
     * Return true if Auto Login is on.
     */
    public boolean isAutoLogin() {
        return getBoolean("autoLoginEnabled", false);
    }

    public void setAutoLogin(boolean autoLogin) {
        setBoolean("autoLoginEnabled", autoLogin);
    }

    /**
     * Return true if Login As Invisible is on.
     */
    public boolean isLoginAsInvisible() {
        return getBoolean("loginAsInvisibleEnabled", false);
    }

    public void setLoginAsInvisible(boolean loginAsInvisible) {
        setBoolean("loginAsInvisibleEnabled", loginAsInvisible);
    }

    /**
     * Return true if Login Anonymously is on.
     */
    public boolean isLoginAnonymously() {
        return getBoolean("loginAnonymously", false);
    }

    public void setLoginAnonymously(boolean loginAnonymously) {
        setBoolean("loginAnonymously", loginAnonymously);
    }

    /**
     * Return true if the password should be encoded and persisted.
     */
    public boolean isSavePassword() {
        return getBoolean("passwordSaved", true);
    }

    public void setSavePassword(boolean savePassword) {
        setBoolean("passwordSaved", savePassword);
    }

    /**
     * Returns the last used Username
     */
    public String getLastUsername() {
        return getString("username");
    }

    public void setLastUsername(String username) {
        setString("username", username);
    }

    /**
     * Returns the last Server accessed.
     */
    public String getServer() {
        return getString("server");
    }

    public void setServer(String server) {
        setString("server", server);
    }

    /**
     * Return the desirability of encryption.
     */
    public ConnectionConfiguration.SecurityMode getSecurityMode() {
        try {
            String securityMode = getString("securityMode", Default.getString(Default.SECURITY_MODE));
            return ConnectionConfiguration.SecurityMode.valueOf(securityMode);
        } catch (Exception e) {
            Log.warning("Unable to parse 'securityMode' value. Using default instead.", e);
            return ConnectionConfiguration.SecurityMode.ifpossible;
        }
    }

    public void setSecurityMode(ConnectionConfiguration.SecurityMode securityMode) {
        setString("securityMode", securityMode.toString());
    }

    /**
     * Returns true to use Direct TLS.
     * <p>
     * This type of encryption typically occurs on port 5223, and causes the socket to be TLS-encrypted immediately.
     * <p>
     * When this option is <em>disabled</em>, but encryption is still to be used, STARTTLS will be used instead.
     */
    public boolean isDirectTls() {
        return getBoolean("sslEnabled", Default.getBoolean(Default.OLD_SSL_ENABLED));
    }

    public void setDirectTls(boolean directTls) {
        setBoolean("sslEnabled", directTls);
    }

    /**
     * Returns the Download Directory, doesn't return <code>null</code>.
     * Uses ~/Downloads as the standard location across all platforms.
     */
    public File getDownloadDir() {
        var propVal = getString("downloadDirectory");
        File downloadDir = !isBlank(propVal) ? new File(propVal) : new File(System.getProperty("user.home"), "Downloads");
        //noinspection ResultOfMethodCallIgnored
        downloadDir.mkdir();
        return downloadDir;
    }

    public void setDownloadDir(String downloadDir) {
        setString("downloadDirectory", downloadDir);
    }

    public boolean isProxyEnabled() {
        return getBoolean("proxyEnabled", Default.getBoolean(Default.PROXY_ENABLED));
    }

    public void setProxyEnabled(boolean proxyEnabled) {
        setBoolean("proxyEnabled", proxyEnabled);
    }

    public String getHost() {
        return getString("host");
    }

    public void setHost(String host) {
        setString("host", host);
    }

    public int getPort() {
        return getInt("port", 0);
    }

    public void setPort(int port) {
        setInt("port", port);
    }

    public String getProxyUsername() {
        return getString("proxyUsername");
    }

    public void setProxyUsername(String proxyUsername) {
        setString("proxyUsername", proxyUsername);
    }

    public String getProxyPassword() {
        return getString("proxyPassword");
    }

    public void setProxyPassword(String proxyPassword) {
        setString("proxyPassword", proxyPassword);
    }

    public String getProtocol() {
        return getString("protocol");
    }

    public void setProtocol(String protocol) {
        setString("protocol", protocol);
    }

    public int getCheckForUpdates() {
        return getInt("checkForUpdates", 7);
    }

    public Instant getLastCheckForUpdates() {
        String date = getString("lastUpdateCheck");
        if (isBlank(date)) {
            return null;
        }
        long time = Long.parseLong(date);
        return Instant.ofEpochMilli(time);
    }

    public void setLastCheckForUpdates(Instant lastCheckForUpdates) {
        String time = Long.toString(lastCheckForUpdates.toEpochMilli());
        setString("lastUpdateCheck", time);
    }

    public String getXmppHost() {
        return getString("xmppHost");
    }

    public void setXmppHost(String xmppHost) {
        setString("xmppHost", xmppHost);
    }

    public boolean isHostAndPortConfigured() {
        return getBoolean("hostAndPort", Default.getBoolean(Default.HOST_AND_PORT_CONFIGURED));
    }

    public void setHostAndPortConfigured(boolean configured) {
        setBoolean("hostAndPort", configured);
    }

    public String getResource() {
        return getString("resource", Default.getString(Default.SHORT_NAME));
    }

    public void setResource(String resource) {
        setString("resource", resource);
    }

    public boolean isStartedHidden() {
        return getBoolean("startHidden", false);
    }

    public void setStartedHidden(boolean startedHidden) {
        setBoolean("startHidden", startedHidden);
    }

    public boolean isUsingSingleTrayClick() {
        return getBoolean("useSingleTrayClick", true);
    }

    public void setUsingSingleTrayClick(boolean useSingle) {
        setBoolean("useSingleTrayClick", useSingle);
    }

    public boolean isTimeDisplayedInChat() {
        return getBoolean("timeDisplayed", true);
    }

    public void setTimeDisplayedInChat(boolean timeDisplayedInChat) {
        setBoolean("timeDisplayed", timeDisplayedInChat);
    }

    public void setTimeFormat(String format) {
        setString("timeFormat", format);
    }

    public String getTimeFormat() {
        return getString("timeFormat", "HH:mm");
    }

    public boolean isChatRoomNotificationsOn() {
        return getBoolean("chatNotificationOn", true);
    }

    public void setChatRoomNotifications(boolean on) {
        setBoolean("chatNotificationOn", on);
    }

    public boolean isChatHistoryEnabled() {
        return getBoolean("showHistory", true);
    }

    public void setChatHistoryEnabled(boolean chatHistoryEnabled) {
        setBoolean("showHistory", chatHistoryEnabled);
    }

    public boolean isPrevChatHistoryEnabled() {
        return getBoolean("showPrevHistory", true);
    }

    public void setPrevChatHistoryEnabled(boolean prevChatHistoryEnabled) {
        setBoolean("showPrevHistory", prevChatHistoryEnabled);
    }

    public boolean isEmptyGroupsShown() {
        return getBoolean("showEmptyGroups", false);
    }

    public void setEmptyGroupsShown(boolean shown) {
        setBoolean("showEmptyGroups", shown);
    }

    public boolean isOfflineUsersShown() {
        return getBoolean("showOfflineUsers", true);
    }

    public void setOfflineUsersShown(boolean shown) {
        setBoolean("showOfflineUsers", shown);
    }

    public boolean isTypingNotificationShown() {
        String showTypingNotification = PluginRes.getPreferenceRes("showTypingNotification");
        return getBoolean("showTypingNotification", Boolean.parseBoolean(showTypingNotification));
    }

    public void setSystemTrayNotificationEnabled(boolean shown) {
        setBoolean("SystemTrayNotificationEnabled", shown);
    }

    public boolean isSystemTrayNotificationEnabled() {
        String SystemTrayNotificationEnabled = PluginRes.getPreferenceRes("SystemTrayNotificationEnabled");
        return getBoolean("SystemTrayNotificationEnabled", Boolean.parseBoolean(SystemTrayNotificationEnabled));
    }

    public void setTypingNotificationOn(boolean shown) {
        setBoolean("showTypingNotification", shown);
    }

    public int getFileTransferTimeout() {
        return getInt("fileTransferTimeout", 30);
    }

    public void setFileTransferTimeout(int minutes) {
        setInt("fileTransferTimeout", minutes);
    }

    public void setChatLengthDefaultTimeout(int minutes) {
        setInt("defaultChatLengthTimeout", minutes);
    }

    public int getChatLengthDefaultTimeout() {
        return getInt("defaultChatLengthTimeout", 15);
    }

    public void setNickname(Resourcepart nickname) {
        setString("nickname", nickname.toString());
    }

    public Resourcepart getNickname() {
        String nicknameString = getString("nickname");
        return Resourcepart.fromOrThrowUnchecked(!isBlank(nicknameString) ? nicknameString : SparkManager.getUserManager().getNickname());
    }

    public void setShowToasterPopup(boolean show) {
        setBoolean("toasterPopup", show);
    }

    public boolean isShowToasterPopup() {
        String toasterPopup = PluginRes.getPreferenceRes("toasterPopup");
        return getBoolean("toasterPopup", Boolean.parseBoolean(toasterPopup));
    }

    public void setDisableAsteriskToasterPopup(boolean disable) {
        setBoolean("disableAsteriskToasterPopup", disable);
    }

    public boolean isDisableAsteriskToasterPopup() {
        return getBoolean("disableAsteriskToasterPopup", false);
    }

    public void setWindowTakesFocus(boolean focus) {
        setBoolean("windowTakesFocus", focus);
    }

    public boolean isWindowTakesFocus() {
        return getBoolean("windowTakesFocus", false);
    }

    public void setStartOnStartup(boolean startup) {
        setBoolean("startOnStartup", startup);
    }

    public boolean isStartOnStartup() {
        return getBoolean("startOnStartup", false);
    }

    /**
     * Reconnection display type</p>
     * 0 = ReconnectPanel </p>
     * 1 = Reconnect as Group</p>
     * 2 = Reconnect as Icon</p>
     */
    public int getReconnectPanelType() {
        return getInt("ReconnectPanelType", 1);
    }

    public void setReconnectPanelType(int reconnect) {
        setInt("ReconnectPanelType", reconnect);
    }

    public void setCompressionEnabled(boolean on) {
        setBoolean("compressionOn", on);
    }

    public boolean isCompressionEnabled() {
        return getBoolean("compressionOn", Default.getBoolean(Default.COMPRESSION_ENABLED));
    }

    public void setEmoticonPack(String pack) {
        setString("emoticonPack", pack);
    }

    public String getEmoticonPack() {
        return getString("emoticonPack", "Default");
    }

    public void setNotificationsDisplayTime(Integer DisplayTime) {
        setInt("DisplayTime", DisplayTime);
    }

    public int getNotificationsDisplayTime() {
        return getInt("DisplayTime", 3);
    }

    public void setOfflineNotifications(boolean notify) {
        setBoolean("notifyOnOffline", notify);
    }

    public boolean isOfflineNotificationsOn() {
        String notifyOnOffline = PluginRes.getPreferenceRes("notifyOnOffline");
        return getBoolean("notifyOnOffline", Boolean.parseBoolean(notifyOnOffline));
    }

    public void setOnlineNotifications(boolean notify) {
        setBoolean("notifyOnOnline", notify);
    }

    public boolean isOnlineNotificationsOn() {
        String notifyOnOnline = PluginRes.getPreferenceRes("notifyOnOnline");
        return getBoolean("notifyOnOnline", Boolean.parseBoolean(notifyOnOnline));
    }

    public void setDockingEnabled(boolean dockingEnabled) {
        setBoolean("dockingEnabled", dockingEnabled);
    }

    public boolean isDockingEnabled() {
        return getBoolean("dockingEnabled", false);
    }

    public boolean isHideInTaskbar() {
        return getBoolean("hideInTaskbar", false);
    }

    public void setHideInTaskbar(boolean hideInTaskbar) {
        setBoolean("hideInTaskbar", hideInTaskbar);
    }

    public void setAutoCloseChatRoomsEnabled(boolean autoCloseChatRoomsEnabled) {
        setBoolean("autoCloseChatRoomsEnabled", autoCloseChatRoomsEnabled);
    }

    public boolean isAutoCloseChatRoomsEnabled() {
        return getBoolean("autoCloseChatRoomsEnabled", true);
    }

    public void setTabsOnTop(boolean onTop) {
        setBoolean("tabsOnTop", onTop);
    }

    public boolean isTabOnTop() {
        return getBoolean("tabsOnTop", true);
    }

    public void setTabsScroll(boolean tabsScroll) {
        setBoolean("tabsScroll", tabsScroll);
    }

    public boolean isTabsScroll() {
        return getBoolean("tabsScroll", true);
    }

    public void setBuzzEnabled(boolean enabled) {
        setBoolean("buzzEnabled", enabled);
    }

    public boolean isBuzzEnabled() {
        return getBoolean("buzzEnabled", true);
    }

    public void setCloseUnreadMessageEnabled(boolean askBeforeCLosing) {
        setBoolean("closeUnreadMessage", askBeforeCLosing);
    }

    public boolean isCloseUnreadMessage() {
        return getBoolean("closeUnreadMessage", true);
    }

    public void setOfflineGroupVisible(boolean visible) {
        setBoolean("offlineGroupVisible", visible);
    }

    public boolean isOfflineGroupVisible() {
        return getBoolean("offlineGroupVisible", true);
    }

    public void setEmoticonsEnabled(boolean enabled) {
        setBoolean("emoticonsEnabled", enabled);
    }

    public boolean areEmoticonsEnabled() {
        return getBoolean("emoticonsEnabled", true);
    }

    public void setGrayingOutEnabled(boolean enabled) {
        setBoolean("GrayingOut", enabled);
    }

    /**
     * SPARK-979 Some users complain about hardly readable nicknames of idle contacts.
     * This option makes them look like all other contacts.
     * Presence icon should be enough to distinguish idle users.
     */
    public boolean isGrayingOutEnabled() {
        return getBoolean("GrayingOut", false);
    }

    public void setLookAndFeel(String laf) {
        setString("LookAndFeel", laf);
    }

    public String getLookAndFeel() {
        var lafProp = getString("LookAndFeel");
        if (!isBlank(lafProp)) {
            return lafProp;
        }
        String defaultstring = Default.getString(Spark.isMac() ? Default.DEFAULT_LOOK_AND_FEEL_MAC : Default.DEFAULT_LOOK_AND_FEEL);
        if (!isBlank(defaultstring)) {
            return defaultstring;
        }
        return UIManager.getSystemLookAndFeelClassName();
    }

    public void setCheckForBeta(boolean checkForBeta) {
        setBoolean("checkForBeta", checkForBeta);
    }

    public boolean isBetaCheckingEnabled() {
        return getBoolean("checkForBeta", false);
    }

    public boolean isMucHighNameEnabled() {
        return getBoolean("isMucHighNameOn", false);
    }

    public void setMucHighNameEnabled(boolean setMucNHigh) {
        setBoolean("isMucHighNameOn", setMucNHigh);
    }

    public boolean isMucHighTextEnabled() {
        return getBoolean("isMucHighTextOn", false);
    }

    public void setMucHighTextEnabled(boolean setMucTHigh) {
        setBoolean("isMucHighTextOn", setMucTHigh);
    }

    public boolean isMucRandomColors() {
        return getBoolean("isMucRandomColors", true);
    }

    public void setMucRandomColors(boolean value) {
        setBoolean("isMucRandomColors", value);
    }

    public boolean isMucHighToastEnabled() {
        return getBoolean("isMucHighToastOn", false);
    }

    public void setMuchHighToastEnabled(boolean setMucPHigh) {
        setBoolean("isMucHighToastOn", setMucPHigh);
    }

    public boolean isShowingRoleIcons() {
        return getBoolean("isShowingRoleIcons", false);
    }

    public void setShowRoleIconInsteadStatusIcon(boolean roleicons) {
        setBoolean("isShowingRoleIcons", roleicons);
    }

    public boolean isShowJoinLeaveMessagesEnabled() {
        return getBoolean("isShowJoinLeaveMessagesOn", false);
    }

    public void setShowJoinLeaveMessagesEnabled(boolean enabled) {
        setBoolean("isShowJoinLeaveMessagesOn", enabled);
    }

    public void setSSOEnabled(boolean enabled) {
        setBoolean("ssoEnabled", enabled);
    }

    public boolean isSSOEnabled() {
        return getBoolean("ssoEnabled", Default.getBoolean(Default.USE_SSO));
    }

    public boolean isSaslGssapiSmack3Compatible() {
        return getBoolean("saslGssapiSmack3compat", Default.getBoolean(Default.USE_SASL_GSS_API_SMACK_3_COMPATIBLE));
    }

    public void setSaslGssapiSmack3Compatible(boolean b) {
        setBoolean("saslGssapiSmack3compat", b);
    }

    public void setSSOMethod(String method) {
        setString("ssoMethod", method);
    }

    public String getSSOMethod() {
        return getString("ssoMethod", "file");
    }

    public void setSSORealm(String realm) {
        setString("ssoRealm", realm);
    }

    public String getSSORealm() {
        return getString("ssoRealm");
    }

    public void setSSOKDC(String kdc) {
        setString("ssoKDC", kdc);
    }

    public String getSSOKDC() {
        return getString("ssoKDC");
    }

    public void setDebuggerEnabled(boolean enabled) {
        setBoolean("debuggerEnabled", enabled);
    }

    public boolean isDebuggerEnabled() {
        return getBoolean("debuggerEnabled", Default.getBoolean(Default.DEBUGGER_ENABLED));
    }

    public void setContactListFontSize(int fontSize) {
        setInt("contactListFontSize", fontSize);
    }

    public int getContactListFontSize() {
        return getInt("contactListFontSize", 14);
    }

    public void setContactListIconSize(int iconSize) {
        setInt("contactListIconSize", iconSize);
    }

    public int getContactListIconSize() {
        return getInt("contactListIconSize", Sizes.Avatar.CONTACT_LIST);
    }

    public void setChatRoomFontSize(int fontSize) {
        setInt("chatRoomFontSize", fontSize);
    }

    public int getChatRoomFontSize() {
        return getInt("chatRoomFontSize", 16);
    }

    public void setLanguage(String language) {
        setString("language", language);
    }

    public String getLanguage() {
        return getString("language");
    }

    public void setAvatarVisible(boolean visible) {
        setBoolean("showAvatar", visible);
    }

    public boolean areAvatarsVisible() {
        return getBoolean("showAvatar", false);
    }

    public void setVCardsVisible(boolean visible) {
        setBoolean("showVCards", visible);
    }

    public boolean areVCardsVisible() {
        return getBoolean("showVCards", true);
    }

    public void setAudioSystem(String device) {
        setString("audioSystem", device);
    }

    public String getAudioSystem() {
        return getString("audioSystem", "wasapi");
    }

    public void setAudioDevice(String device) {
        setString("audioDevice", device);
    }

    public String getAudioDevice() {
        return getString("audioDevice", "javasound://");
    }

    public void setPlaybackDevice(String device) {
        setString("playbackDevice", device);
    }

    public String getPlaybackDevice() {
        return getString("playbackDevice", "javasound://");
    }

    public void setVideoDevice(String device) {
        setString("videoDevice", device);
    }

    public String getVideoDevice() {
        return getString("videoDevice");
    }

    public boolean isMainWindowAlwaysOnTop() {
        return getBoolean("MainWindowAlwaysOnTop", false);
    }

    public void setMainWindowAlwaysOnTop(boolean onTop) {
        setBoolean("MainWindowAlwaysOnTop", onTop);
    }

    public boolean isChatWindowAlwaysOnTop() {
        return getBoolean("ChatWindowAlwaysOnTop", false);
    }

    public void setChatWindowAlwaysOnTop(boolean onTop) {
        setBoolean("ChatWindowAlwaysOnTop", onTop);
    }

    public String getSelectedCodecs() {
        return getString("SelectedCodecs");
    }

    public void setSelectedCodecs(String value) {
        setString("SelectedCodecs", value);
    }

    public String getStunFallbackHost() {
        return getString("stunFallbackHost");
    }

    public void setStunFallbackHost(String host) {
        setString("stunFallbackHost", host);
    }

    public int getStunFallbackPort() {
        return getInt("stunFallbackPort", 3478);
    }

    public void setStunFallbackPort(int port) {
        setInt("stunFallbackPort", port);
    }

    public boolean isShowTransportTab() {
        return getBoolean("useTabForTransport", false);
    }

    public void setShowTransportTab(boolean value) {
        setBoolean("useTabForTransport", value);
    }

    public boolean isShowConferenceTab() {
        return getBoolean("useTabForConference", true);
    }

    public void setShowConferenceTab(boolean value) {
        setBoolean("useTabForConference", value);
    }

    public String getAvailableCodecs() {
        return getString("AvailableCodecs");
    }

    public void setAvailableCodecs(String value) {
        setString("AvailableCodecs", value);
    }

    public void setDisableHostnameVerification(boolean accept) {
        setBoolean("DisableHostnameVerification", accept);
    }

    public boolean isDisableHostnameVerification() {
        return getBoolean("DisableHostnameVerification", Default.getBoolean(Default.DISABLE_HOSTNAME_VERIFICATION));
    }

    private boolean getBoolean(String property, boolean defaultValue) {
        String propertyVal = props.getProperty(property);
        return propertyVal != null ? Boolean.parseBoolean(propertyVal) : defaultValue;
    }

    private void setBoolean(String property, boolean value) {
        props.setProperty(property, Boolean.toString(value));
    }

    private int getInt(String property, int defaultValue) {
        String propertyVal = props.getProperty(property);
        return propertyVal != null ? Integer.parseInt(propertyVal) : defaultValue;
    }

    private void setInt(String property, int value) {
        props.setProperty(property, Integer.toString(value));
    }

    private String getString(String property, String defaultValue) {
        return props.getProperty(property, defaultValue);
    }

    private String getString(String property) {
        return getString(property, "");
    }

    private void setString(String property, String value) {
        props.setProperty(property, value);
    }

    public boolean isAutoAcceptMucInvite() {
        return getBoolean("autoAcceptMucInvite", false);
    }

    public void setAutoAcceptMucInvite(boolean autoAcceptMuc) {
        setBoolean("autoAcceptMucInvite", autoAcceptMuc);
    }

    public EntityBareJid getDefaultBookmarkedConf() {
        var jidString = getString("defaultBookmarkedConf");
        if (isBlank(jidString)) {
            return null;
        }
        return JidCreate.entityBareFromOrThrowUnchecked(jidString);
    }

    public void setDefaultBookmarkedConf(EntityBareJid bookmarkedConferenceJid) {
        setString("defaultBookmarkedConf", bookmarkedConferenceJid.toString());
    }

    /**
     * The message-history should display the messages by date ascending (<b>true</b>) or descending (<b>false</b>)
     */
    public boolean isChatHistoryAscending() {
        return getBoolean("HISTORY_SORT_DATEASC", true);
    }

    public void setChatHistoryAscending(boolean dateIsAsc) {
        setBoolean("HISTORY_SORT_DATEASC", dateIsAsc);
    }

    /**
     * Get the default value for the search period in the
     * history transcript plugin
     * @param defaultValue the default value for the period
     */
    public String getSearchPeriod(String defaultValue) {
        return getString("HISTORY_SEARCH_PERIOD", defaultValue);
    }

    public void setSearchPeriod(String value) {
        setString("HISTORY_SEARCH_PERIOD", value);
    }

    public List<String> getDeactivatedPlugins() {
        String plugs = getString("deactivatedPlugins");
        return List.of(StringUtils.split(plugs, ","));
    }

    public void setDeactivatedPlugins(List<String> list) {
        // [hallo, hallo, hallo, hallo, hallo] => hallo,hallo,hallo,hallo,hallo
        if (!list.isEmpty()) {
            String liste = list.toString().substring(1, list.toString().length() - 1);
            liste = liste.replace(", ", ",");
            setString("deactivatedPlugins", liste);
        } else {
            setString("deactivatedPlugins", "");
        }
    }

    public boolean isUseHostnameAsResource() {
        return getBoolean("useHostnameAsResource", Default.getBoolean(Default.USE_HOSTNAME_AS_RESOURCE));
    }

    public void setUseHostnameAsResource(boolean useHostnameAsResource) {
        setBoolean("useHostnameAsResource", useHostnameAsResource);
    }

    public boolean isUseVersionAsResource() {
        return getBoolean("useVersionAsResource", Default.getBoolean(Default.USE_VERSION_AS_RESOURCE));
    }

    public void setUseVersionAsResource(boolean useVersionAsResource) {
        setBoolean("useVersionAsResource", useVersionAsResource);
    }

    // The following methods are used by LoginPanel to support certain Enterprise features 
    public boolean isAccountsReg() {
        return getBoolean("ccAccountsReg", true);
    }

    public void setAccountsReg(boolean ccAccountsReg) {
        setBoolean("ccAccountsReg", ccAccountsReg);
    }

    public boolean isAdvancedConfig() {
        return getBoolean("ccAdvancedConfig", true);
    }

    public void setAdvancedConfig(boolean ccAdvancedConfig) {
        setBoolean("ccAdvancedConfig", ccAdvancedConfig);
    }

    public boolean isHostNameChange() {
        return getBoolean("ccHostNameChange", true);
    }

    public void setHostNameChange(boolean ccHostNameChange) {
        setBoolean("ccHostNameChange", ccHostNameChange);
    }

    public boolean isInvisibleLogin() {
        return getBoolean("ccInvisibleLogin", true);
    }

    public void setInvisibleLogin(boolean ccInvisibleLogin) {
        setBoolean("ccInvisibleLogin", ccInvisibleLogin);
    }

    public boolean isAnonymousLogin() {
        return getBoolean("ccAnonymousLogin", true);
    }

    public void setAnonymousLogin(boolean ccAnonymousLogin) {
        setBoolean("ccAnonymousLogin", ccAnonymousLogin);
    }

    public boolean isPswdAutologin() {
        return getBoolean("ccPswdAutologin", true);
    }

    public void setPswdAutologin(boolean ccPswdAutologin) {
        setBoolean("ccPswdAutologin", ccPswdAutologin);
    }

    public boolean isAcceptSelfSigned() {
        return getBoolean("acceptSelfSigned", Default.getBoolean(Default.ACCEPT_SELF_SIGNED));
    }

    public void setAcceptSelfSigned(boolean acceptSelfSigned) {
        setBoolean("acceptSelfSigned", acceptSelfSigned);
    }

    public boolean isAcceptRevoked() {
        return getBoolean("acceptRevoked", Default.getBoolean(Default.ACCEPT_REVOKED));
    }

    public void setAcceptRevoked(boolean acceptRevoked) {
        setBoolean("acceptRevoked", acceptRevoked);
    }

    public boolean isAcceptExpired() {
        return getBoolean("acceptExpired", Default.getBoolean(Default.ACCEPT_EXPIRED));
    }

    public void setAcceptExpired(boolean acceptExpired) {
        setBoolean("acceptExpired", acceptExpired);
    }

    public boolean isAcceptNotValidYet() {
        return getBoolean("acceptNotValidYet", Default.getBoolean(Default.ACCEPT_NOT_VALID_YET));
    }

    public void setAcceptNotValidYet(boolean acceptNotValidYet) {
        setBoolean("acceptNotValidYet", acceptNotValidYet);
    }

    public boolean isCheckCRL() {
        return getBoolean("checkCRL", Default.getBoolean(Default.CHECK_CRL));
    }

    public void setCheckCRL(boolean checkCRL) {
        setBoolean("checkCRL", checkCRL);
    }

    public boolean isCheckOCSP() {
        return getBoolean("checkOCSP", Default.getBoolean(Default.CHECK_OCSP));
    }

    public void setCheckOCSP(boolean checkOCSP) {
        setBoolean("checkOCSP", checkOCSP);
    }

    public boolean isAllowSoftFail() {
        return getBoolean("allowSoftFail", Default.getBoolean(Default.ALLOW_SOFT_FAIL));
    }

    public void setAllowSoftFail(boolean allowSoftFail) {
        setBoolean("allowSoftFail", allowSoftFail);
    }

    public boolean isAllowClientSideAuthentication() {
        return getBoolean("allowClientSideAuthentication", Default.getBoolean(Default.ALLOW_CLIENT_SIDE_AUTH));
    }

    public void setAllowClientSideAuthentication(boolean allowClientSideAuthentication) {
        setBoolean("allowClientSideAuthentication", allowClientSideAuthentication);
    }

    public void setFileTransferIbbOnly(boolean enable) {
        setBoolean("fileTransferIbbOnly", enable);
    }

    public boolean isFileTransferIbbOnly() {
        return getBoolean("fileTransferIbbOnly", Default.getBoolean(Default.FILE_TRANSFER_IBB_ONLY));
    }

    public void setAutoAcceptFileTransferFromContacts(boolean enable) {
        setBoolean("fileTransferAutoAcceptPresence", enable);
    }

    public boolean isAutoAcceptFileTransferFromContacts() {
        return getBoolean("fileTransferAutoAcceptPresence", Default.getBoolean(Default.FILE_TRANSFER_AUTO_ACCEPT_PRESENCE));
    }

    public void setMaxCurrentHistorySize(int value) {
        setInt("currentHistoryMaxSize", value);
    }

    public int getMaxCurrentHistorySize() {
        return getInt("currentHistoryMaxSize", 20);
    }

    @Override
    public String toString() {
        StringBuilder propsText = new StringBuilder();
        for (var entry : props.entrySet()) {
            String name = entry.getKey().toString();
            String val = entry.getValue().toString();
            if (name.startsWith("password") && !name.equals("passwordSaved")) {
                if (!isBlank(val)) {
                    val = "HIDDEN";
                }
            }
            propsText.append(name).append("=").append(val).append('\n');
        }
        return propsText.toString();
    }
}
