/*
 * 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.junit.Assert;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class ScreenManagerTest {
    private final List<Screen> screens = new ArrayList<>();
    private final Map<String, Screen> screenNameMap = new HashMap<>();
    private final ScreenManager screenManager = new ScreenManager(screens, screenNameMap);

    private static final String FIRST_SCREEN = "first";
    private static final String SECOND_SCREEN = "second";
    private static final String THIRD_SCREEN = "third";
    private static final boolean ACTIVATE = true;
    private static final boolean DEACTIVATE = false;


    @Test
    public void nextPassesInactiveScreen() {
        Screen firstScreen = defaultScreen(FIRST_SCREEN, ACTIVATE);
        Screen secondScreen = defaultScreen(SECOND_SCREEN, DEACTIVATE);
        Screen thirdScreen = defaultScreen(THIRD_SCREEN, ACTIVATE);
        screens.addAll(Arrays.asList(firstScreen, secondScreen, thirdScreen));
        screenManager.next();
        Assert.assertEquals(thirdScreen, screenManager.current());
    }

    @Test
    public void previousPassesInactiveScreen() {
        Screen firstScreen = defaultScreen(FIRST_SCREEN, ACTIVATE);
        Screen secondScreen = defaultScreen(SECOND_SCREEN, DEACTIVATE);
        Screen thirdScreen = defaultScreen(THIRD_SCREEN, ACTIVATE);
        screens.addAll(Arrays.asList(firstScreen, secondScreen, thirdScreen));
        screenManager.next();
        screenManager.previous();
        Assert.assertEquals(firstScreen, screenManager.current());
    }

    @Test
    public void nextAfterSetNextScreenActive() {
        Screen firstScreen = defaultScreen(FIRST_SCREEN, ACTIVATE);
        Screen secondScreen = defaultScreen(SECOND_SCREEN, DEACTIVATE);
        Screen thirdScreen = defaultScreen(THIRD_SCREEN, ACTIVATE);
        screens.addAll(Arrays.asList(firstScreen,secondScreen,thirdScreen));
        screenNameMap.put(secondScreen.getName(), secondScreen);
        screenManager.setScreenActivity(secondScreen.getName(), ACTIVATE);
        screenManager.next();
        Assert.assertEquals(secondScreen, screenManager.current());
    }

    @Test
    public void nextAfterSetNextScreenInactive() {
        Screen firstScreen = defaultScreen(FIRST_SCREEN, ACTIVATE);
        Screen secondScreen = defaultScreen(SECOND_SCREEN, ACTIVATE);
        Screen thirdScreen = defaultScreen(THIRD_SCREEN, ACTIVATE);
        screens.addAll(Arrays.asList(firstScreen,secondScreen,thirdScreen));
        screenNameMap.put(secondScreen.getName(), secondScreen);
        screenManager.setScreenActivity(secondScreen.getName(), DEACTIVATE);
        screenManager.next();
        Assert.assertEquals(thirdScreen, screenManager.current());
    }

    @Test
    public void screenStateChangeNotifiesListeners() {
        final Screen firstScreen = defaultScreen(FIRST_SCREEN, DEACTIVATE);
        screens.add(firstScreen);
        screenNameMap.put(FIRST_SCREEN, firstScreen);
        final AtomicInteger calledCounter = new AtomicInteger(0);
        screenManager.addScreenStateListener((s)->calledCounter.incrementAndGet());
        assertEquals("The screen listener should have been called on adding", 1, calledCounter.get());

        screenManager.setScreenActivity(FIRST_SCREEN, ACTIVATE);

        assertEquals("The screen listener should have been called after change", 2, calledCounter.get());
    }

    @Test
    public void bulkScreenStateChangeNotifiesListeners() {
        final Screen firstScreen = defaultScreen(FIRST_SCREEN, DEACTIVATE);
        screens.add(firstScreen);
        final Screen secondScreen = defaultScreen(SECOND_SCREEN, DEACTIVATE);
        screens.add(secondScreen);
        screenNameMap.put(FIRST_SCREEN, firstScreen);
        screenNameMap.put(SECOND_SCREEN, secondScreen);

        final AtomicInteger calledCounter = new AtomicInteger(0);
        screenManager.addScreenStateListener((s)->calledCounter.incrementAndGet());
        assertEquals("The screen listener should have been called on adding", 1, calledCounter.get());

        screenManager.setScreensActive(Arrays.asList(FIRST_SCREEN, SECOND_SCREEN));

        assertEquals("The screen listener should have been called after change", 2, calledCounter.get());
    }

    @Test
    public void nextAndPreviousScreenNotifiesListeners() {
        final Screen firstScreen = defaultScreen(FIRST_SCREEN, ACTIVATE);
        final Screen secondScreen = defaultScreen(SECOND_SCREEN, ACTIVATE);
        screens.addAll(Arrays.asList(firstScreen, secondScreen));

        final AtomicInteger calledCounter = new AtomicInteger(0);
        screenManager.addScreenStateListener((s)->calledCounter.incrementAndGet());
        assertEquals("The screen listener should have been called on adding", 1, calledCounter.get());

        screenManager.next();

        assertEquals("The screen listener should have been called after change", 2, calledCounter.get());

        screenManager.previous();

        assertEquals("The screen listener should have been called after change", 3, calledCounter.get());
    }

    private Screen defaultScreen(String name, boolean isActive) {
        final Screen screen = mock(Screen.class);
        doAnswer(invocationOnMock -> {
            when(screen.isActive()).thenReturn(invocationOnMock.getArgument(0));
            return null;
        }).when(screen).setActive(any(boolean.class));
        when(screen.isActive()).thenReturn(isActive);
        when(screen.getName()).thenReturn(name);

        return screen;
    }
}
