/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2021 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * 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
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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.jboss.installer.screens;

import org.jboss.installer.common.FontResources;
import org.jboss.installer.core.InstallationData;
import org.jboss.installer.core.LanguageUtils;
import org.jboss.installer.core.ScreenManager;
import org.jboss.installer.screens.domain.SecurityDomainScreen;

import javax.swing.ButtonGroup;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ItemEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiPredicate;
import java.util.stream.Collectors;

public class ConfigureRuntimeEnvironmentScreen extends DefaultScreen{

    public static final String NAME = "ConfigureRuntimeEnvironmentScreen";
    public static final String TITLE_KEY = "runtime_environment.title";
    public static final String DESCRIPTION_KEY = "runtime_environment.description";
    public static final String DEFAULT_CONFIG_KEY = "runtime_environment.default_config";
    public static final String ADVANCED_CONFIG_KEY = "runtime_environment.advanced_config";
    public static final String PASSWORD_VAULT_KEY = "runtime_environment.password_vault";
    public static final String SSL_SECURITY_KEY = "runtime_environment.ssl_security";
    public static final String JSSE_SECURITY_KEY = "runtime_environment.jsse_security";
    public static final String LDAP_AUTHENTICATION_KEY = "runtime_environment.ldap_authentication";
    public static final String SECURITY_DOMAIN_KEY = "runtime_environment.security_domain";
    public static final String QUICKSTARTS_KEY = "runtime_environment.quickstarts";
    public static final String PORT_BINDINGS_KEY = "runtime_environment.port_bindings";
    public static final String LOGGING_LEVELS_KEY = "runtime_environment.logging_levels";
    public static final String JSF_IMPLEMENTATION_KEY = "runtime_environment.jsf_implementation";
    public static final String JDBC_DRIVER_KEY = "runtime_environment.jdbc_driver";
    public static final String DATASOURCE_KEY = "runtime_environment.datasource";
    private static final String SIDE_NAV_KEY = "runtime_environment.side_nav";

    private final JRadioButton defaultConfig = createRadioButton(langUtils.getString(DEFAULT_CONFIG_KEY), true);
    private final JRadioButton advancedConfig = createRadioButton(langUtils.getString(ADVANCED_CONFIG_KEY), false);

    private final JCheckBox passwordVault = createDisabledCheckBox(PASSWORD_VAULT_KEY);
    private final JCheckBox sslSecurity = createDisabledCheckBox(SSL_SECURITY_KEY);
    private final JCheckBox jsseSecurity = createDisabledCheckBox(JSSE_SECURITY_KEY);
    private final JCheckBox ldapAuthentication = createDisabledCheckBox(LDAP_AUTHENTICATION_KEY);
    private final JCheckBox securityDomain = createDisabledCheckBox(SECURITY_DOMAIN_KEY);
    private final JCheckBox quickstarts = createDisabledCheckBox(QUICKSTARTS_KEY);
    private final JCheckBox portBindings = createDisabledCheckBox(PORT_BINDINGS_KEY);
    private final JCheckBox loggingLevels = createDisabledCheckBox(LOGGING_LEVELS_KEY);
    private final JCheckBox jsfImplementation = createDisabledCheckBox(JSF_IMPLEMENTATION_KEY);
    private final JCheckBox jdbcDriver = createDisabledCheckBox(JDBC_DRIVER_KEY);
    private final JCheckBox datasource = createDisabledCheckBox(DATASOURCE_KEY);

    private final ArrayList<JCheckBox> checkBoxList = new ArrayList<>();
    private final Map<JCheckBox, List<String>> checkBoxScreenNameMap = new HashMap<>();


    public ConfigureRuntimeEnvironmentScreen(LanguageUtils langUtils) {
        super(langUtils);
        setupCheckBoxButtons();
    }

    @Override
    public String getTitle() {
        return langUtils.getString(TITLE_KEY);
    }

    @Override
    public String getName() {
        return NAME;
    }

    @Override
    public String getSideNavName() {
        return langUtils.getString(SIDE_NAV_KEY);
    }

    @Override
    public JPanel getContent() {
        JPanel content = new JPanel();
        content.setLayout(new GridBagLayout());
        final GridBagConstraints c = initializeConstraints();

        advancedConfig.addItemListener(itemEvent -> {
            enableAllCheckboxes(itemEvent.getStateChange() == ItemEvent.SELECTED);
        });

        ButtonGroup radioButtonGroup = new ButtonGroup();

        radioButtonGroup.add(defaultConfig);
        radioButtonGroup.add(advancedConfig);

        c.gridx = 0;
        c.gridy = 0;
        c.insets = DESCRIPTION_INSET;
        content.add(createDescription(DESCRIPTION_KEY), c);

        c.gridy++;
        c.insets = NO_INSET;
        content.add(defaultConfig, c);
        c.gridy++;
        c.insets = FIELD_ROW_INSET;
        content.add(advancedConfig, c);

        datasource.setBorder(LEFT_BORDER_INDENT);
        c.insets = NO_INSET;
        addCheckboxesToPanel(content, c);
        c.gridy++;

        // add filler to take empty space
        fillEmptySpace(content, c);
        return content;
    }

    @Override
    public JComponent getDefaultFocusComponent() {
        return defaultConfig;
    }

    private JCheckBox createDisabledCheckBox(String textKey) {
        return createCheckBox(textKey, false, false);
    }

    private void setupCheckBoxButtons() {
        addCheckBox(passwordVault, List.of(CredentialStoreScreen.NAME));
        addCheckBox(sslSecurity, List.of(SSLSecurityScreen.NAME));
        addCheckBox(jsseSecurity, List.of(JSSESecurityScreen.NAME));
        addCheckBox(ldapAuthentication, Arrays.asList(LDAPConnectionScreen.NAME, LDAPSecurityRealmScreen.NAME));
        addCheckBox(securityDomain, List.of(SecurityDomainScreen.NAME));
        addCheckBox(quickstarts, List.of(QuickstartsScreen.NAME));
        addCheckBox(portBindings, List.of(PortConfigurationScreen.NAME));
        addCheckBox(loggingLevels, List.of(LoggingConfigurationScreen.NAME));
        addCheckBox(jsfImplementation, List.of(JSFInstallScreen.NAME));
        addCheckBox(jdbcDriver, List.of(JDBCDriverScreen.NAME));
        addCheckBox(datasource, List.of(DatasourceScreen.NAME));

        jdbcDriver.addItemListener(itemEvent -> {
            datasource.setEnabled(itemEvent.getStateChange() == ItemEvent.SELECTED);
            if (!jdbcDriver.isSelected()) {
                datasource.setSelected(false);
            }
        });
    }

    private void addCheckBox(JCheckBox checkBox, List<String> screens) {
        checkBox.setFont(FontResources.getOpenSansRegular());
        checkBox.setAlignmentX(Component.LEFT_ALIGNMENT);
        checkBoxList.add(checkBox);
        checkBoxScreenNameMap.put(checkBox, screens);
    }

    private void addCheckboxesToPanel(JPanel content, GridBagConstraints c) {
        for (JCheckBox checkBox: checkBoxList) {
            c.gridy++;
            content.add(checkBox, c);
        }
    }

    private void enableAllCheckboxes(boolean flag) {
        for (JCheckBox checkBox: checkBoxScreenNameMap.keySet()) {
            checkBox.setEnabled(flag);
        }
        datasource.setEnabled(jdbcDriver.isSelected() && flag);
    }

    private void activateSelectedScreens(ScreenManager screenManager) {
        if (defaultConfig.isSelected()) {
            return;
        }
        screenManager.setScreensActive(getConfigurationScreens((k, v) -> k.isSelected()));
    }

    private List<String> getConfigurationScreens(BiPredicate<JCheckBox, List<String>> selected) {
        List<String> checkedOptionsList = checkBoxScreenNameMap.entrySet()
                .stream()
                .filter(e->selected.test(e.getKey(), e.getValue()))
                .map(Map.Entry::getValue)
                .flatMap(List::stream)
                .collect(Collectors.toList());
        return checkedOptionsList;
    }

    private void deactivateConfigurationScreens(ScreenManager screenManager) {
        screenManager.setScreensInactive(getAllOptionalScreenNames());
    }

    private List<String> getAllOptionalScreenNames() {
        return checkBoxScreenNameMap.values().stream().flatMap(List::stream).collect(Collectors.toList());
    }


    @Override
    public void record(InstallationData installationData, ScreenManager screenManager) {
        activateSelectedScreens(screenManager);
        installationData.removeSummaries(getConfigurationScreens((k, v) -> !k.isSelected()));
    }

    @Override
    public void load(InstallationData installationData, ScreenManager screenManager) {
        deactivateConfigurationScreens(screenManager);
    }

    @Override
    public boolean isReversible() {
        return false;
    }
}
