/*
 * 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.core;

import org.jboss.installer.navigation.NavigationState;

import javax.swing.JComponent;
import java.util.Optional;
import java.util.function.Consumer;

import static javax.swing.JOptionPane.YES_OPTION;

public class Navigator {

    private final ScreenManager screenManager;
    private final Consumer<Screen> screenUpdater;
    private final DialogManager dialogManager;
    private final ActionExecutor executor;
    private InstallationData installationData;
    private final NavigationState navState;

    public Navigator(ScreenManager screenManager, NavigationState navState, DialogManager dialogManager,
                     Consumer<Screen> screenUpdater, ActionExecutor executor, InstallationData installationData) {
        this.screenManager = screenManager;
        this.screenUpdater = screenUpdater;
        this.dialogManager = dialogManager;
        this.navState = navState;
        this.executor = executor;
        this.installationData = installationData;
    }

    public void startNavigation() {
        loadScreen(screenManager.first());
        navState.setPreviousVisible(false);
    }

    public void moveToNext() {
        try {
            final Screen currentScreen = screenManager.current();
            if (!currentScreen.prevalidate()) {
                return;
            }
            final ValidationResult validationResult = currentScreen.validate();
            if (validationResult.getResult() == ValidationResult.Result.ERROR) {
                dialogManager.validationError(validationResult.getMessage());
                return;
            }

            if (validationResult.getResult() == ValidationResult.Result.WARNING) {
                int result = dialogManager.validationWarning(validationResult.getMessages());
                if (result != YES_OPTION) {
                    return;
                }
            }

            if (screenManager.hasNext()) {
                currentScreen.record(installationData, screenManager);
                final Screen next = screenManager.next();

                prepareNavigationForNextScreen(next);

                loadScreen(next);
            } else {
                // do nothing - in future throw error?
            }
        } catch (InstallerRuntimeException e) {
            disableNavigation();
            dialogManager.systemError(e);
        }
    }

    private void disableNavigation() {
        navState.setNextEnabled(false);
        navState.setPreviousEnabled(false);
    }

    private void prepareNavigationForNextScreen(Screen next) {
        navState.reset();
        // set focus on buttons. Screens can override this at later stage
        navState.requestFocus();
        if (!screenManager.hasNext()) {
            navState.setNextVisible(false);
            navState.setQuitVisible(false);
            navState.setPreviousVisible(false);
            navState.setDoneVisible(true);
        }
        if (!next.isReversible()) {
            navState.setPreviousEnabled(false);
        }
    }

    public void moveToPrevious() {
        try {
            // no need to validate when we're going back
            if (screenManager.hasPrevious()) {
                screenManager.current().rollback(installationData);
                final Screen previous = screenManager.previous();

                navState.reset();
                if (!screenManager.hasPrevious()) {
                    navState.setPreviousVisible(false);
                }
                if (!previous.isReversible()) {
                    navState.setPreviousEnabled(false);
                }
                loadScreen(previous);
            } else {
                // do nothing - in future throw error?
            }
        } catch (InstallerRuntimeException e) {
            disableNavigation();
            dialogManager.systemError(e);
        }
    }

    public InstallationData getInstallationData() {
        return installationData;
    }

    private void loadScreen(Screen previous) {
        final Screen next = previous;
        next.load(installationData, screenManager);
        screenUpdater.accept(next);
        executePostLoadAction(next);
        // allow the screen to take the focus
        Optional.ofNullable(next.getDefaultFocusComponent()).ifPresent(JComponent::requestFocus);
    }

    private void executePostLoadAction(Screen screen) {
        executor.execute(screen.getPostLoadAction(installationData), navState);
    }

    public void stopCurrentAction() {
        executor.stopCurrentAction();
    }
}
