/*
 * 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.FileChooserPanel;
import org.jboss.installer.common.FontResources;
import org.jboss.installer.common.InstallerDialogs;
import org.jboss.installer.common.UiResources;
import org.jboss.installer.core.DownloadUtils;
import org.jboss.installer.core.FileUtils;
import org.jboss.installer.core.InstallationData;
import org.jboss.installer.core.LanguageUtils;
import org.jboss.installer.core.MnemonicUtils;
import org.jboss.installer.core.Screen;
import org.jboss.installer.core.ScreenManager;
import org.jboss.installer.core.ValidationResult;
import org.jboss.installer.dialogs.downloader.DownloadDisplayPanel;
import org.jboss.installer.dialogs.downloader.DownloadHandler;
import org.jboss.installer.dialogs.downloader.DownloadManager;
import org.jboss.installer.postinstall.task.JsfDefaultLibraryTask;
import org.jboss.installer.postinstall.task.JsfLibraryTask;
import org.jboss.installer.screens.jsf.ChannelPanel;
import org.jboss.installer.screens.jsf.DownloadArtifactsPanel;
import org.jboss.installer.validators.JsfUtils;
import org.jboss.installer.validators.JsfValidator;
import org.jboss.installer.validators.PathValidator;

import javax.swing.Box;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JPanel;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.nio.file.Path;

public class JSFInstallScreen extends DefaultScreen {

    public static final String NAME = "JSFInstallScreen";
    public static final String TITLE_KEY = "jsf_install.title";
    public static final String DESCRIPTION_KEY = "jsf_install.description_label";
    public static final String INSTALL_DEFAULT_KEY = "jsf_install.default.check";
    public static final String JSF_PROJECT_KEY = "jsf_install.dropdown.label";
    public static final String JSF_VERSION_KEY = "jsf_install.version.text";
    public static final String JSF_INSTALL_JSFIMPL_KEY = "jsf_install.jsfimpl.label";
    public static final int FULL_COLUMN_WIDTH = 4;
    public static final String JSF_CHANGE_VERSION_CONFIRM_KEY = "jsf_install.version.confirm";
    private final JCheckBox setAsDefaultCheckbox = createCheckBox(INSTALL_DEFAULT_KEY, true, false);
    private JsfLibraryTask.Config config;
    private final JsfLibraryTask libInstallTask = new JsfLibraryTask();
    private final JsfDefaultLibraryTask setAsDefaultImplTask = new JsfDefaultLibraryTask();
    private JPanel content;
    private DownloadUtils downloadUtils;
    private DownloadArtifactsPanel downloadArtifactsPanel;
    private ChannelPanel channelPanel;
    private Path targetFolder;
    private JComboBox<String> projectSelector;

    public JSFInstallScreen(Screen parent, LanguageUtils langUtils, boolean isActive) {
        super(parent, langUtils, isActive);
    }

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

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

    @Override
    public void load(InstallationData installationData) {
        targetFolder = installationData.getTargetFolder();
    }

    @Override
    public JPanel getContent() {
        if (this.channelPanel == null) {
            this.channelPanel = new ChannelPanel(langUtils, new MnemonicUtils(), targetFolder);
        }
        if (this.downloadArtifactsPanel == null) {
            this.downloadArtifactsPanel = new DownloadArtifactsPanel(langUtils, new MnemonicUtils(), targetFolder);
        }

        content = new JPanel();

        content.setLayout(new GridBagLayout());
        GridBagConstraints c = initializeConstraints();

        c.insets = DESCRIPTION_INSET;
        c.gridwidth = FULL_COLUMN_WIDTH;
        content.add(createDescription(DESCRIPTION_KEY), c);
        c.gridy++;

        c.insets = SUBSECTION_INSET;
        content.add(setAsDefaultCheckbox, c);
        c.gridy++;

        if (projectSelector == null) {
            this.projectSelector = new JComboBox<>(new String[]{
                    JsfLibraryTask.Config.JsfProject.Mojarra.toString(),
                    JsfLibraryTask.Config.JsfProject.MyFaces.toString()});

            this.projectSelector.addActionListener(l-> {
                if (getSelectedProject() == JsfLibraryTask.Config.JsfProject.Mojarra) {
                    downloadArtifactsPanel.setVisible(true);
                    channelPanel.setVisible(false);
                    content.revalidate();
                } else if (getSelectedProject() == JsfLibraryTask.Config.JsfProject.MyFaces) {
                    downloadArtifactsPanel.setVisible(false);
                    channelPanel.setVisible(true);
                    content.revalidate();
                }
            });

        }
        c.insets = FIELD_ROW_INSET;
        c.fill = GridBagConstraints.HORIZONTAL;
        c.gridx = 0;
        c.gridwidth = 1;
        c.weightx = 1;
        content.add(UiResources.createFieldLabel(langUtils.getString(JSF_PROJECT_KEY)), c);

        c.gridx = 1;
        c.gridwidth = 1;
        projectSelector.setFont(FontResources.getOpenSansRegular());
        content.add(projectSelector, c);

        c.gridx = 2;
        c.gridwidth = 1;
        c.weightx = 0.5;
        projectSelector.setFont(FontResources.getOpenSansRegular());
        content.add(Box.createHorizontalBox(), c);
        c.weightx = 1;
        c.gridy++;
        c.gridx = 0;

        c.gridwidth = FULL_COLUMN_WIDTH;

        content.add(downloadArtifactsPanel, c);
        downloadArtifactsPanel.setVisible(getSelectedProject() == JsfLibraryTask.Config.JsfProject.Mojarra);
        c.gridy++;

        channelPanel.setVisible(getSelectedProject() == JsfLibraryTask.Config.JsfProject.MyFaces);
        content.add(channelPanel, c);

        c.gridy++;
        fillEmptySpace(content, c);

        return content;
    }

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

    @Override
    public boolean prevalidate() {
        if (getSelectedProject() == JsfLibraryTask.Config.JsfProject.MyFaces) {
            return true;
        }

        try {
            config = populateConfig();
        } catch (FileChooserPanel.InvalidPathException e) {
            final ValidationResult result = PathValidator.newInstance("generic", langUtils).validate(e.getPath());
            InstallerDialogs.showErrorMessage(result.getMessage(),
                    langUtils, content.getRootPane());
            return false;
        }

        if (downloadUtils == null) {
            downloadUtils = new DownloadUtils(
                    new DownloadManager(),
                    (downloads, downloadManager) -> {
                        final DownloadDisplayPanel panel = new DownloadDisplayPanel(
                                downloads,
                                downloadManager, langUtils);
                        return new DownloadUtils.DownloadUI() {
                            @Override
                            public boolean display() {
                                return InstallerDialogs.showDownloadMessage(content.getRootPane(), panel, langUtils);
                            }

                            @Override
                            public DownloadHandler getDownloadHandler() {
                                return panel;
                            }
                        };
                    },
                    (download) -> InstallerDialogs.showErrorMessage(langUtils.getString(InstallerDialogs.DOWNLOAD_UNREACHABLE, download.getUrl().toString()),
                            langUtils, content));
        }

        // note the values can be null or empty at this point. Do not user List.of
        final ArrayList<String> downloadable = new ArrayList<>();
        downloadable.add(config.getImplJarPath());
        final List<String> resolvedJarPaths = downloadUtils.downloadAll(downloadable);

        if (resolvedJarPaths == null) {
            return false;
        }

        config.setResolvedImplJarPath(resolvedJarPaths.get(0));

        return true;
    }

    @Override
    public ValidationResult validate() {
        if (getSelectedProject() == JsfLibraryTask.Config.JsfProject.Mojarra) {

            final ValidationResult result = new JsfValidator(new JsfUtils(), langUtils).validate(config);
            if (result.getResult() != ValidationResult.Result.OK) {
                return result;
            }

            try {
                final JsfUtils jsfUtils = new JsfUtils();
                config.setJsfVersion(jsfUtils.getVersion(FileUtils.getJarFromPath(config.getImplJarPath())));
            } catch (IOException e) {
                // this should already be checked by validator
                ValidationResult.error("Unable to read JSF jars.");
            }
        } else {
            final ValidationResult result = channelPanel.validateInput();
            if (result.getResult() != ValidationResult.Result.ERROR) {
                try {
                    this.config = populateConfig();
                } catch (FileChooserPanel.InvalidPathException e) {
                    // already handled in validate input
                    throw new RuntimeException(e);
                }
            }
            return result;
        }

        return ValidationResult.ok();
    }

    @Override
    public void record(InstallationData installationData, ScreenManager screenManager) {
        installationData.putConfig(config);
        installationData.addPostInstallTask(libInstallTask);
        installationData.addPostInstallTask(setAsDefaultImplTask);
    }

    @Override
    public void rollback(InstallationData installationData) {
        installationData.removeConfig(JsfLibraryTask.Config.class);
        installationData.removePostInstallTask(libInstallTask);
        installationData.removePostInstallTask(setAsDefaultImplTask);
    }

    private JsfLibraryTask.Config populateConfig() throws FileChooserPanel.InvalidPathException {
        final JsfLibraryTask.Config config = new JsfLibraryTask.Config();
        if (getSelectedProject() == JsfLibraryTask.Config.JsfProject.Mojarra) {

            config.setJsfProject(JsfLibraryTask.Config.JsfProject.Mojarra);

            config.setImplJarPath(downloadArtifactsPanel.getImplFileValue());
        } else {
            channelPanel.populateConfig(config);
        }

        config.setMakeDefault(setAsDefaultCheckbox.isSelected());

        return config;
    }

    public JsfLibraryTask.Config.JsfProject getSelectedProject() {
        JsfLibraryTask.Config.JsfProject selectedProject;
        final int selectedItem = projectSelector.getSelectedIndex();
        if (selectedItem == 0) {
            selectedProject = JsfLibraryTask.Config.JsfProject.Mojarra;
        } else {
            selectedProject = JsfLibraryTask.Config.JsfProject.MyFaces;
        }
        return selectedProject;
    }
}
