/*
 * 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.panels;

import org.jboss.installer.common.UiResources;
import org.jboss.installer.core.DatabaseDriver;
import org.jboss.installer.core.LanguageUtils;
import org.jboss.installer.core.MnemonicUtils;
import org.jboss.installer.postinstall.task.DatasourceConfig;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import static org.jboss.installer.screens.DefaultScreen.RIGHT_INDENT_INSET;

public class DatasourcePropertiesPanel extends JPanel {

    public static final String XA_PROPERTIES_KEY = "datasource.type.datasource_xa.properties";
    public static final String XA_PROPERTIES_KEY_KEY = "datasource.type.datasource_xa.properties.key";
    public static final String XA_PROPERTIES_VALUE_KEY = "datasource.type.datasource_xa.properties.value";
    public static final String ADD_PROPERTY_KEY = "datasource.type.datasource_xa.properties.add_property";
    public static final String REMOVE_PROPERTY_KEY = "datasource.type.datasource_xa.properties.remove_property";
    private static final int MAX_XA_PROPS = 10;

    private final JLabel xaPropertiesLabel;
    private final JLabel xaPropertiesKey;
    private final JLabel xaPropertiesValue;
    private final JButton addPropertyButton;
    private final JButton removePropertyButton;
    private int minPropertyListSize;
    private final List<PropertyField> propertyFieldList = new ArrayList<>();
    private int visiblePropertiesCounter = 0;

    public DatasourcePropertiesPanel(LanguageUtils languageUtils, DatabaseDriver driver, MnemonicUtils mnemonicUtils) {
        this.xaPropertiesLabel = UiResources.createFieldLabel(languageUtils.getString(XA_PROPERTIES_KEY));
        this.xaPropertiesKey = UiResources.createFieldLabel(languageUtils.getString(XA_PROPERTIES_KEY_KEY));
        this.xaPropertiesValue = UiResources.createFieldLabel(languageUtils.getString(XA_PROPERTIES_VALUE_KEY));
        this.addPropertyButton = UiResources.createButton(languageUtils.getString(ADD_PROPERTY_KEY), actionEvent -> addPropertyField());
        mnemonicUtils.findMnemonic(addPropertyButton.getText()).ifPresent(addPropertyButton::setMnemonic);
        this.removePropertyButton = UiResources.createButton(languageUtils.getString(REMOVE_PROPERTY_KEY), actionEvent -> removePropertyField());
        mnemonicUtils.findMnemonic(removePropertyButton.getText()).ifPresent(removePropertyButton::setMnemonic);
        removePropertyButton.setEnabled(false);
        setDefaultProperties(driver);
        displayPanel();
    }

    public void displayPanel() {
        this.setLayout(new GridBagLayout());
        GridBagConstraints c = UiResources.initializeConstraints();
        c.gridwidth = 1;

        c.gridx = 0;
        c.weightx = 0.5;
        this.add(xaPropertiesLabel, c);

        c.gridx = 1;
        c.insets = RIGHT_INDENT_INSET;
        c.weightx = 0.25;
        this.add(xaPropertiesKey, c);

        c.gridx = 2;
        this.add(xaPropertiesValue, c);
        addPropertyFields(c);
        c.gridy++;

        c.gridx = 1;
        this.add(addPropertyButton, c);

        c.gridx = 2;
        this.add(removePropertyButton, c);
    }

    private void addPropertyField() {
        visiblePropertiesCounter++;
        propertyFieldList.get(visiblePropertiesCounter).setVisible(true);

        if (visiblePropertiesCounter == MAX_XA_PROPS) {
            addPropertyButton.setEnabled(false);
        }
        if (visiblePropertiesCounter > minPropertyListSize) {
            removePropertyButton.setEnabled(true);
        }

        this.revalidate();
        propertyFieldList.get(visiblePropertiesCounter).requestFocus();

        Rectangle bounds = addPropertyButton.getBounds();
        bounds = new Rectangle(bounds.x, bounds.y + propertyFieldList.get(0).getHeight(), bounds.width, bounds.height);
        ((JComponent)addPropertyButton.getParent()).scrollRectToVisible(bounds);
    }

    private void removePropertyField() {
        propertyFieldList.get(visiblePropertiesCounter).setVisible(false);
        visiblePropertiesCounter--;

        if (visiblePropertiesCounter == minPropertyListSize) {
            removePropertyButton.setEnabled(false);
        }
        if (visiblePropertiesCounter < MAX_XA_PROPS) {
            addPropertyButton.setEnabled(true);
        }

        this.revalidate();

        if (propertyFieldList.size() == minPropertyListSize) {
            addPropertyButton.requestFocus();
        } else {
            removePropertyButton.requestFocus();
        }
    }

    private void addPropertyFields(GridBagConstraints c) {
        for (PropertyField property : propertyFieldList) {
            c.gridy++;
            c.gridx = 1;
            this.add(property.keyField, c);

            c.gridx = 2;
            this.add(property.valueField, c);
        }
        c.gridx = 0;
    }

    public void setDefaultProperties(DatabaseDriver driver) {
        switch (driver) {
            case IBM_DB2:
                minPropertyListSize = 3;
                propertyFieldList.add(new PropertyField("ServerName", "localhost"));
                propertyFieldList.add(new PropertyField("DatabaseName", "ibmdb2db"));
                propertyFieldList.add(new PropertyField("PortNumber", "50000"));
                break;
            case MICROSOFT_SQL_SERVER:
                minPropertyListSize = 4;
                propertyFieldList.add(new PropertyField("ServerName", "localhost"));
                propertyFieldList.add(new PropertyField("DatabaseName", "mssqldb"));
                propertyFieldList.add(new PropertyField("PortNumber", "1433"));
                propertyFieldList.add(new PropertyField("SelectMethod", "cursor"));
                break;
            case MYSQL:
                minPropertyListSize = 3;
                propertyFieldList.add(new PropertyField("ServerName", "localhost"));
                propertyFieldList.add(new PropertyField("DatabaseName", "mysqldb"));
                propertyFieldList.add(new PropertyField("PortNumber", "3306"));
                break;
            case ORACLE:
                minPropertyListSize = 1;
                propertyFieldList.add(new PropertyField("Url", "jdbc:oracle:oci8:@tc"));
                break;
            case POSTGRE_SQL:
                minPropertyListSize = 3;
                propertyFieldList.add(new PropertyField("ServerName", "localhost"));
                propertyFieldList.add(new PropertyField("DatabaseName", "postgresdb"));
                propertyFieldList.add(new PropertyField("PortNumber", "5432"));
                break;
            case ENTERPRISE_DB:
                minPropertyListSize = 3;
                propertyFieldList.add(new PropertyField("ServerName", "localhost"));
                propertyFieldList.add(new PropertyField("DatabaseName", "enterprisedb"));
                propertyFieldList.add(new PropertyField("PortNumber", "5432"));
                break;
            case SYBASE_JCONN:
                minPropertyListSize = 4;
                propertyFieldList.add(new PropertyField("ServerName", "localhost"));
                propertyFieldList.add(new PropertyField("DatabaseName", "sapasedb"));
                propertyFieldList.add(new PropertyField("NetworkProtocol", "Tds"));
                propertyFieldList.add(new PropertyField("PortNumber", "4100"));
                break;
            case MARIA_DB:
                minPropertyListSize = 3;
                propertyFieldList.add(new PropertyField("ServerName", "localhost"));
                propertyFieldList.add(new PropertyField("DatabaseName", "mariadb"));
                propertyFieldList.add(new PropertyField("PortNumber", "3306"));
                break;
        }

        visiblePropertiesCounter = propertyFieldList.size();

        // fill up the rest of available spaces with empty properties
        for (int i = propertyFieldList.size()-1; i< MAX_XA_PROPS; i++) {
            propertyFieldList.add(new PropertyField());
        }
    }

    public List<DatasourceConfig.XaProperty> getXaPropertyList() {
        return propertyFieldList.stream()
                .filter(PropertyField::isVisible)
                .map(propertyField -> new DatasourceConfig.XaProperty(propertyField.getKey(), propertyField.getValue()))
                .collect(Collectors.toList());
    }

    public static class PropertyField {
        private final JTextField keyField;
        private final JTextField valueField;

        public PropertyField(String defaultKey, String defaultValue) {
            this.keyField = UiResources.createTextField(defaultKey);
            this.valueField = UiResources.createTextField(defaultValue);
            keyField.setFocusable(false);
            setColumns();
        }

        public PropertyField() {
            this.keyField = UiResources.createTextField("");
            this.valueField = UiResources.createTextField("");
            setVisible(false);
            setColumns();
        }

        private void setColumns() {
            keyField.setColumns(MAX_XA_PROPS);
            valueField.setColumns(MAX_XA_PROPS);
        }

        public String getKey() {
            return UiResources.readTextValue(keyField);
        }

        public String getValue() {
            return UiResources.readTextValue(valueField);
        }

        public void requestFocus() {
            keyField.requestFocus();
        }

        public boolean isVisible() {
            return keyField.isVisible() && valueField.isVisible();
        }

        public void setVisible(boolean visible) {
            keyField.setVisible(visible);
            valueField.setVisible(visible);

            keyField.setText("");
            valueField.setText("");
        }

        public int getHeight() {
            // multiply by 2 two account for spaces between
            return 2* Integer.max(keyField.getHeight(), valueField.getHeight());
        }
    }
}
