/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2022 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.actions.ActionException;
import org.jboss.installer.actions.ActionResult;
import org.jboss.installer.actions.InstallerAction;
import org.jboss.installer.common.FontResources;
import org.jboss.installer.core.DefaultActionExecutor;
import org.jboss.installer.core.InstallationData;
import org.jboss.installer.core.InstallationFailedException;
import org.jboss.installer.core.LanguageUtils;
import org.jboss.installer.postinstall.PostInstallTaskRunner;
import org.jboss.installer.postinstall.server.DomainServer;
import org.jboss.installer.postinstall.server.StandaloneServer;

import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingWorker;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.file.Path;

import static org.jboss.installer.core.DefaultActionExecutor.ERROR_TASK_STATE;
import static org.jboss.installer.postinstall.PostInstallTaskRunner.POST_INSTALL_TASK_ERROR;
import static org.jboss.installer.postinstall.PostInstallTaskRunner.POST_INSTALL_TASK_LOG_ENTRY;

public class ProcessingScreen extends DefaultScreen implements PropertyChangeListener {

    private static final String PROCESSING_SCREEN_TITLE = "processing_screen.title";
    private static final String PROCESSING_SCREEN_COMPLETE = "processing_screen.complete";
    private static final String PROCESSING_SCREEN_FAILED = "processing_screen.failed";
    private JLabel currentTaskDescription = createFieldLabel("", false);
    private JProgressBar progressBar;

    private JTextArea textArea = new JTextArea();
    private boolean postInstallTasksFailed = false;

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

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

    @Override
    public String getName() {
        return "ProcessingScreen";
    }

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

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

        c.insets = DESCRIPTION_INSET;
        content.add(currentTaskDescription, c);
        c.gridy++;

        progressBar = new JProgressBar();
        progressBar.setIndeterminate(true);
        content.add(progressBar, c);

        c.gridy++;
        c.weighty = 1;
        c.fill = GridBagConstraints.BOTH;
        final JScrollPane scrollPane = new JScrollPane(textArea);
        // hack to ensure the textArea's scroll pane isn't taken over by the screen's scroll pane
        scrollPane.setPreferredSize(new Dimension(100, 200));
        content.add(scrollPane, c);

        textArea.setEditable(false);

        return content;
    }

    @Override
    public JComponent getDefaultFocusComponent() {
        // take away the focus from buttons while the installation happens
        return textArea;
    }

    @Override
    public InstallerAction getPostLoadAction(InstallationData installationData) {

        final Path serverPath = installationData.getTargetFolder();
        return new InstallerAction() {
            @Override
            public ActionResult perform(PropertyChangeSupport propertyChangeSupport) throws ActionException {
                try (final StandaloneServer standaloneServer = new StandaloneServer(serverPath)) {
                    try (final DomainServer domainServer = new DomainServer(serverPath)) {
                        final PostInstallTaskRunner taskRunner = new PostInstallTaskRunner(standaloneServer, domainServer,
                                langUtils, ProcessingScreen.this);
                        try {
                            taskRunner.execute(installationData, propertyChangeSupport);
                            return ActionResult.success();
                        } catch (InstallationFailedException e) {
                            e.printStackTrace();
                            return ActionResult.error(e.getMessage());
                        }
                    }
                }

            }
        };
    }

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        if (PostInstallTaskRunner.POST_INSTALL_TASK_NAME_PROPERTY.equals(evt.getPropertyName())) {
            currentTaskDescription.setText((String) evt.getNewValue());
        } else if (POST_INSTALL_TASK_LOG_ENTRY.equals(evt.getPropertyName())) {
            final TaskLogEntry log = (TaskLogEntry)evt.getNewValue();
            textArea.append(langUtils.getString(log.getMsgKey(), log.getParams()) + "\n");
        } else if (POST_INSTALL_TASK_ERROR.equals(evt.getPropertyName())) {
            final Exception ex = (Exception) evt.getNewValue();
            final StringWriter out = new StringWriter();
            PrintWriter pw = new PrintWriter(out);
            ex.printStackTrace(pw);
            textArea.append(out.toString());
        } else if (DefaultActionExecutor.EXECUTOR_STATE_PROPERTY.equals(evt.getPropertyName())) {
            if (evt.getNewValue().equals(ERROR_TASK_STATE)) {
                postInstallTasksFailed = true;
            }
        } else if (evt.getPropertyName().equals("state")) {
            if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
                if (postInstallTasksFailed) {
                    currentTaskDescription.setText(langUtils.getString(PROCESSING_SCREEN_FAILED));
                } else {
                    currentTaskDescription.setText(langUtils.getString(PROCESSING_SCREEN_COMPLETE));
                }
                currentTaskDescription.setFont(FontResources.getOpenSansLightSubHeading());
                progressBar.setIndeterminate(false);
                progressBar.setValue(100);
            }
        }
    }

    public static class TaskLogEntry {
        private String msgKey;
        private String[] params;

        public TaskLogEntry(String msgKey, String... params) {
            this.msgKey = msgKey;
            this.params = params;
        }

        public String getMsgKey() {
            return msgKey;
        }

        public String[] getParams() {
            return params;
        }
    }
}
