package org.jboss.installer.core;

import org.jboss.installer.actions.ActionException;
import org.jboss.installer.actions.ActionResult;
import org.jboss.installer.actions.InstallerAction;
import org.jboss.installer.navigation.NavigationState;

import javax.swing.SwingWorker;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;

/**
 * Swing implementation of ActionExecutor. Uses {@link DialogManager} to communicate errors in {@link InstallerAction}.
 */
public class DefaultActionExecutor implements ActionExecutor {

    public static final String ERROR_TASK_STATE = "ERROR";
    public static final String EXECUTOR_STATE_PROPERTY = "EXECUTOR_STATE";
    private DialogManager dialogManager;

    private SwingWorker worker = null;

    public DefaultActionExecutor(DialogManager dialogManager) {
        this.dialogManager = dialogManager;
    }

    @Override
    public synchronized void execute(InstallerAction action, NavigationState navState) {
        if (worker != null) {
            throw new IllegalStateException("Only one action can be executed at any time");
        }
        // skip post load action if it's empty
        if (action == InstallerAction.EMPTY) {
            return;
        }

        boolean nextState = navState.isNextEnabled();
        navState.setNextEnabled(false);
        final InstallerAction postLoadAction = action;
        this.worker = new SwingWorker<ActionResult, Void>() {

            @Override
            protected ActionResult doInBackground() throws Exception {
                return postLoadAction.perform(this.getPropertyChangeSupport());
            }

            @Override
            protected void done() {
                try {
                    final ActionResult result = get();
                    if (result.isSuccess()) {
                        navState.setNextEnabled(nextState);
                        navState.requestFocus();
                    } else {
                        this.getPropertyChangeSupport().firePropertyChange(EXECUTOR_STATE_PROPERTY, "", ERROR_TASK_STATE);
                        dialogManager.actionError(result.getMessage());
                        // disable navigation
                        navState.setPreviousEnabled(false);
                        navState.setErrorState();
                        navState.requestFocus();
                    }
                } catch (InterruptedException | ExecutionException e) {
                    this.getPropertyChangeSupport().firePropertyChange(EXECUTOR_STATE_PROPERTY, "", ERROR_TASK_STATE);
                    LoggerUtils.taskLog.error(e);
                    if (e.getCause() instanceof ActionException) {
                        dialogManager.actionError((ActionException) e.getCause());
                    } else {
                        dialogManager.actionError(e.getCause().getLocalizedMessage());
                    }
                    // disable navigation
                    navState.setPreviousEnabled(false);
                    navState.setErrorState();
                } catch (CancellationException e) {
                    LoggerUtils.taskLog.debug("Task was cancelled", e);
                } finally {
                    synchronized (DefaultActionExecutor.this) {
                        DefaultActionExecutor.this.worker = null;
                    }
                    actionCompleted();
                }
            }
        };

        this.worker.execute();
    }

    @Override
    public synchronized void stopCurrentAction() {
        if (this.worker != null) {
            this.worker.cancel(true);
        }
    }

    protected void actionCompleted() {
        // do nothing - used for testing
    }
}
