/*
 * 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.codehaus.plexus.util.FileUtils;
import org.jboss.installer.actions.InstallerAction;
import org.jboss.installer.actions.impl.InstallEap;
import org.jboss.installer.common.FontResources;
import org.jboss.installer.common.UiResources;
import org.jboss.installer.core.InstallationData;
import org.jboss.installer.core.LanguageUtils;
import org.wildfly.prospero.api.ProvisioningProgressEvent;

import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import static org.jboss.installer.actions.impl.InstallEap.Stage.CLEANUP;
import static org.jboss.installer.actions.impl.InstallEap.Stage.EXTRA_CONFIG;

public class InstallationScreen extends DefaultScreen implements PropertyChangeListener {

    public static final String NAME = "InstallationScreen";

    public static final String TITLE_KEY = "installation_screen.title";
    public static final String CURRENT_ELEMENT_KEY = "installation_screen.current_element";
    public static final String PACKAGES_STAGE_KEY = "installation_screen.stage.packages";
    public static final String DOWNLOAD_STAGE_KEY = "installation_screen.stage.downloads";
    public static final String FEATURE_PACK_STAGE_KEY = "installation_screen.stage.feature-pack";
    public static final String MODULES_STAGE_KEY = "installation_screen.stage.modules";
    public static final String CONFIG_STAGE_KEY = "installation_screen.stage.config";
    public static final String CLEANUP_STAGE_KEY = "installation_screen.stage.cleanup";
    public static final String EXTRA_CONFIG_STAGE_KEY = "installation_screen.stage.extra_config";
    public static final String EXTRACT_ARCHIVE_KEY = "installation_screen.stage.extract_archive";

    private static final String EMPTY_LABEL_TEXT = "";

    private static final Set<String> REGISTERED_EVENT_TYPES = Arrays.stream(ProvisioningProgressEvent.EventType.values())
            .map(Enum::toString).collect(Collectors.toSet());

    private JLabel currentElementLabel;

    public InstallationScreen(LanguageUtils langUtils) {
        super(langUtils);
    }

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

    private HashMap<InstallEap.Stage, ProgressBar> progressBars = new HashMap<>();

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

    private boolean isMavenRepoZip;

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

        c.insets = DESCRIPTION_INSET;
        currentElementLabel = createFieldLabel(langUtils.getString(CURRENT_ELEMENT_KEY, EMPTY_LABEL_TEXT), false);
        mainPanel.add(currentElementLabel, c);
        c.gridy++;

        // pack a grid of progress bars into the tracker panel
        JPanel trackerPanel = new JPanel(new GridBagLayout());
        trackerPanel.setFont(FontResources.getOpenSansRegular());
        final GridBagConstraints trackerConstraints = initializeConstraints();
        trackerConstraints.insets = SUBSECTION_INSET;
        for (InstallEap.Stage stage : InstallEap.Stage.values()) {
            if(stage == InstallEap.Stage.ZIP_EXTRACT && !isMavenRepoZip) {
                continue;
            }
            createProgressPanel(stage, trackerConstraints, trackerPanel);
            trackerConstraints.gridy++;
            trackerConstraints.gridx = 0;

        }
        // add vertical space filler
        fillEmptySpace(trackerPanel, trackerConstraints);

        mainPanel.add(trackerPanel, c);
        c.gridy++;

        fillEmptySpace(mainPanel, c);

        return mainPanel;
    }

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

    private void createProgressPanel(InstallEap.Stage stage, GridBagConstraints c, JPanel trackerPanel) {
        ProgressBar progressPanel = new ProgressBar(stage.getKey(), c, trackerPanel);
        progressBars.put(stage, progressPanel);
    }

    @Override
    public InstallerAction getPostLoadAction(InstallationData installationData) {
        return new InstallEap(installationData.getTargetFolder(), this, installationData.getExcludedPackages(), installationData.getMavenRepositories(), langUtils);
    }

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        if (!REGISTERED_EVENT_TYPES.contains(evt.getPropertyName())) {
            return;
        }

        final ProvisioningProgressEvent.EventType eventType = ProvisioningProgressEvent.EventType.valueOf(evt.getPropertyName());
        if (eventType == ProvisioningProgressEvent.EventType.UPDATE) {
            final InstallEap.Progress progress = (InstallEap.Progress) evt.getNewValue();
            progressBars.get(progress.getCurrent()).update(progress.getProgress());
            if (progress.getCurrentElement() != null) {
                currentElementLabel.setText(langUtils.getString(CURRENT_ELEMENT_KEY, progress.getCurrentElement()));
            }
            if (progress.getCurrent() == EXTRA_CONFIG) {
                progressBars.get(progress.getCurrent()).updateSpinningBar(1);
            }
        } else if (eventType == ProvisioningProgressEvent.EventType.COMPLETED) {
            final InstallEap.Stage completedStage = (InstallEap.Stage) evt.getNewValue();
            if (progressBars.containsKey(completedStage))
                progressBars.get(completedStage).done();

            final InstallEap.Stage stoppedStage = (InstallEap.Stage) evt.getNewValue();
            if (stoppedStage == CLEANUP) {
                for (int i = 0; i < stoppedStage.ordinal(); i++) {
                    if (progressBars.containsKey(InstallEap.Stage.values()[i]))
                        progressBars.get(InstallEap.Stage.values()[i]).done();
                }
            }
        } else if (eventType == ProvisioningProgressEvent.EventType.STARTING) {
            final InstallEap.Stage startedStage = (InstallEap.Stage) evt.getNewValue();

            for (int i = 0; i < startedStage.ordinal(); i++) {
                if (progressBars.containsKey(InstallEap.Stage.values()[i]))
                    progressBars.get(InstallEap.Stage.values()[i]).done();
            }

            if (startedStage == CLEANUP || startedStage == EXTRA_CONFIG) {
                progressBars.get(startedStage).updateSpinningBar(1);
            } else {
                progressBars.get(startedStage).update(1);
            }
        }
    }

    private class ProgressBar {

        private final JProgressBar progressBar;
        private final JLabel doneLabel;
        private final JLabel percentLabel;

        ProgressBar(String descKey, GridBagConstraints c, JPanel panel) {

            final JLabel stageDescriptionLabel = createFieldLabel(descKey);

            progressBar = new JProgressBar(0, 100);

            doneLabel = new JLabel(EMPTY_LABEL_TEXT);
            doneLabel.setIcon(UiResources.DONE_ICON);
            doneLabel.setVisible(false);

            percentLabel = new JLabel("   ");

            c.gridx = 0;
            c.weightx = 0.2;
            panel.add(stageDescriptionLabel, c);

            c.gridx = 1;
            c.weightx = 0.5;
            panel.add(createStatusPanel(progressBar, doneLabel), c);

            c.gridx = 2;
            c.weightx = 0.3;
            panel.add(percentLabel, c);
        }

        private JPanel createStatusPanel(JProgressBar progressBar, JLabel doneLabel) {
            final JPanel statusPanel = new JPanel(new GridBagLayout());
            final GridBagConstraints c2 = initializeConstraints();
            // make the progress bar take up all the horizontal space in the row
            statusPanel.add(progressBar, c2);

            c2.gridx++;
            // check icon should be centered - disable stretching
            c2.weightx = 0;
            statusPanel.add(doneLabel, c2);
            return statusPanel;
        }

        void update(int progress) {
            if (progressBar.getValue() < progress) {
                progressBar.setValue(progress);
                percentLabel.setText(progress + "%");
            }
        }

        void updateSpinningBar(int progress){
            if (!progressBar.isIndeterminate()) {
                progressBar.setIndeterminate(true);
            }
        }

        void done() {
            doneLabel.setVisible(true);
            doneLabel.revalidate();

            progressBar.setVisible(false);
            percentLabel.setText("");
        }
    }

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

    @Override
    public void load(InstallationData installationData) {
       this.isMavenRepoZip = repositoriesContainZipArchive(installationData.getMavenRepositories());
    }
    private boolean repositoriesContainZipArchive(Map<String, URL> maven_zip){
        for (URL url : maven_zip.values()){
            if(FileUtils.getExtension(url.getPath()).equals("zip")){
               return true;
            }
        }
        return false;
    }
}
