/*
 * 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.common.HtmlTextPanel;
import org.jboss.installer.common.UiResources;
import org.jboss.installer.core.LanguageUtils;
import org.jboss.installer.core.MnemonicUtils;
import org.jboss.installer.core.Screen;
import org.jboss.installer.validators.PortConfigurationValidator;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JRadioButton;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.border.Border;
import javax.swing.text.NumberFormatter;
import java.awt.Color;
import java.awt.event.ActionListener;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.Insets;
import java.text.NumberFormat;
import java.text.ParseException;


public abstract class DefaultScreen implements Screen {

    public static final String HOME_DIR = System.getProperty("user.home");
    public static final int CONTENT_PANEL_WIDTH = 650;
    public static final int FRAME_WIDTH = 900;
    public static final int ROW_HEIGHT = 20;
    public static final double LEFT_COLUMN_WEIGHT = 0.35;
    public static final double RIGHT_COLUMN_WEIGHT = 0.65;
    public static final Border LEFT_BORDER_INDENT = BorderFactory.createEmptyBorder(0,20,0,0);
    public static final Insets SUBSECTION_INSET = new Insets(0,10,10,0);
    public static final Insets RIGHT_INDENT_INSET = new Insets(0,0,10,10);
    public static final Insets NO_INSET = new Insets(0, 0, 0, 0);
    public static final Insets FIELD_ROW_INSET = new Insets(0,0,10,0);
    public static final Insets DESCRIPTION_INSET = new Insets(0,0,20,0);

    private Screen parent;
    protected final MnemonicUtils mnemonicUtils = new MnemonicUtils();

    public static Component emptyRow() {
        // small box not to overflow the width of a panel
        return Box.createRigidArea(new Dimension(20, ROW_HEIGHT));
    }

    protected final LanguageUtils langUtils;
    protected boolean isActive;

    public DefaultScreen(LanguageUtils langUtils, boolean isActive) {
        this(null, langUtils, isActive);
    }

    public DefaultScreen(Screen parent, LanguageUtils langUtils, boolean isActive) {
        this.isActive = isActive;
        this.langUtils = langUtils;
        this.parent = parent;
    }

    static JList<String> createMultiSelectionList(String[] allowedProtocols) {
        final JList<String> list = new JList<>(allowedProtocols);
        list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
        list.setFont(FontResources.getOpenSansRegular());
        list.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY, 1, true));
        list.addPropertyChangeListener(evt -> {
            if (evt.getPropertyName().equals("enabled")) {
                if (evt.getNewValue().equals(evt.getOldValue())) {
                    return;
                }
                final boolean isEnabled = (boolean) evt.getNewValue();
                Color borderColor = isEnabled?Color.DARK_GRAY:Color.LIGHT_GRAY;
                list.setBorder(BorderFactory.createLineBorder(borderColor, 1, true));
            }
        });
        return list;
    }

    protected JTextArea createDescription(String textKey) {
        JTextArea textArea = new JTextArea(langUtils.getString(textKey));
        textArea.setMargin(NO_INSET);
        textArea.setFont(FontResources.getOpenSansRegular());
        textArea.setEditable(false);
        textArea.setBackground(null);
        textArea.setLineWrap(true);
        textArea.setWrapStyleWord(true);
        // prevent the description from taking the focus when TAB-cycling
        textArea.setFocusable(false);

        return textArea;
    }

    protected JComponent createHtmlDescription(String textKey) {
        return new HtmlTextPanel(langUtils.getString(textKey));
    }

    protected void addToolTip(JComponent component, String msgKey) {
        final String text = langUtils.getString(msgKey);
        component.setToolTipText("<html><p width=\"200\">" + text + "</p></html>");
    }

    protected GridBagConstraints initializeConstraints() {
        return UiResources.initializeConstraints();
    }

    protected JLabel createFieldLabel(String textKey) {
        final JLabel label = createFieldLabel(textKey, true);
        if (langUtils.hasString(textKey + ".tooltip")) {
            UiResources.setTooltip(label, langUtils.getString(textKey + ".tooltip"));
        }
        return label;
    }

    protected JLabel createFieldLabel(String text, boolean evaluateKey) {
        text = evaluateKey?langUtils.getString(text):text;
        final JLabel label = UiResources.createFieldLabel(text);
        if (evaluateKey && langUtils.hasString(text + ".tooltip")) {
            UiResources.setTooltip(label, langUtils.getString(text + ".tooltip"));
        }
        return label;
    }

    protected JTextField createTextField(String text) {
        return UiResources.createTextField(text);
    }

    protected JRadioButton createRadioButton(String text, boolean selected, boolean evaluateKey) {
        String effectiveText = evaluateKey?langUtils.getString(text):text;
        final JRadioButton radioButton = createRadioButton(effectiveText, selected);

        if (evaluateKey && langUtils.hasString(text+".tooltip")) {
            addToolTip(radioButton, text+".tooltip");
        }

        return UiResources.withScrollBarListener(radioButton);
    }

    protected JRadioButton createRadioButton(String text, boolean selected) {
        return UiResources.createRadioButton(text, selected);
    }

    public JPasswordField createPasswordField() {
        return UiResources.createPasswordField();
    }

    protected void fillEmptySpace(JPanel content, GridBagConstraints c) {
        c.weighty = 10;
        content.add(Box.createVerticalBox(), c);
    }

    protected JLabel createSubHeadingLabel(String textKey) {
        JLabel label = new JLabel(langUtils.getString(textKey));
        label.setFont(FontResources.getOpenSansLightSubHeading());
        return label;
    }

    protected JButton createButton(String key, ActionListener listener) {
        final JButton button = UiResources.createButton(langUtils.getString(key), listener);
        mnemonicUtils.findMnemonic(button.getText()).ifPresent(button::setMnemonic);
        return button;
    }

    protected JComboBox<String> createDropdown(String[] labelKeys) {
        return createDropdown(labelKeys, true);
    }

    protected JComboBox<String> createDropdown(String[] values, boolean translate) {
        final String[] translated;

        if (translate) {
            translated= new String[values.length];
            for (int i = 0; i < values.length; i++) {
                String value = values[i];
                translated[i] = langUtils.getString(value);
            }
        } else {
            translated = values;
        }

        return UiResources.createDropdown(translated);
    }

    protected JCheckBox createCheckBox(String textKey, boolean enabled, boolean selected) {
        JCheckBox checkBox = new JCheckBox(langUtils.getString(textKey));
        checkBox.setFont(FontResources.getOpenSansRegular());
        checkBox.setEnabled(enabled);
        checkBox.setSelected(selected);
        if (langUtils.hasString(textKey+".tooltip")) {
            addToolTip(checkBox, textKey+".tooltip");
        }
        return UiResources.withScrollBarListener(checkBox);
    }

    protected JCheckBox createEmptyCheckBox(boolean enabled, boolean selected) {
        JCheckBox checkBox = new JCheckBox();
        checkBox.setEnabled(enabled);
        checkBox.setSelected(selected);
        return UiResources.withScrollBarListener(checkBox);
    }

    protected void addField(JPanel content, GridBagConstraints c, String labelKey, JComponent textField) {
        String tooltip = langUtils.hasString(labelKey+".tooltip") ? langUtils.getString(labelKey+".tooltip"):"";
        UiResources.addField(content, c, langUtils.getString(labelKey), tooltip, textField);
    }

    public static NumberFormatter getIntegerValueFormatter(int minValue, int maxValue) {
        NumberFormat format = NumberFormat.getIntegerInstance();
        format.setGroupingUsed(false);
        NumberFormatter formatter = new NumberFormatter(format) {
            @Override
            public Object stringToValue(String text) throws ParseException {
                if (text.length() == 0)
                    return null;
                return super.stringToValue(text);
            }
        };
        formatter.setValueClass(Integer.class);
        formatter.setMinimum(minValue);
        formatter.setMaximum(maxValue);
        formatter.setAllowsInvalid(false);
        formatter.setCommitsOnValidEdit(true);
        return formatter;
    }

    public static NumberFormatter getPortValueFormatter() {
        return getIntegerValueFormatter(PortConfigurationValidator.MIN_PORT_VALUE, PortConfigurationValidator.MAX_PORT_VALUE);
    }

    public DefaultScreen(LanguageUtils langUtils) {
        this(langUtils, true);
    }

    @Override
    public boolean isActive() {
        return isActive;
    }

    @Override
    public void setActive(boolean flag) {
        isActive = flag;
    }

    @Override
    public String getSideNavName() {
        return getTitle();
    }

    @Override
    public Screen getParent() {
        return parent;
    }
}
