package de.renew.draw.ui.impl.services;

import java.util.Vector;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.MockedStatic;
import org.mockito.Mockito;

import CH.ifa.draw.DrawPlugin;
import CH.ifa.draw.application.DrawApplication;
import de.renew.draw.storables.ontology.Figure;
import de.renew.draw.storables.ontology.FigureEnumeration;
import de.renew.draw.ui.ontology.DrawingView;
import de.renew.draw.ui.ontology.ErrorState;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

class SelectionServiceImplTest {

    private SelectionServiceImpl _mockedService;
    private DrawApplication _mockedDrawApplication;
    private MockedStatic<DrawPlugin> _mockedDrawPluginStatic;
    private DrawingView _mockedDrawingView;

    @BeforeEach
    void setUp() {
        _mockedService = new SelectionServiceImpl();
        _mockedDrawingView = mock(DrawingView.class);
        _mockedDrawApplication = mock(DrawApplication.class);
        _mockedDrawPluginStatic = Mockito.mockStatic(DrawPlugin.class);
        _mockedDrawPluginStatic.when(DrawPlugin::getGui).thenReturn(_mockedDrawApplication);
    }

    @AfterEach
    void tearDown() {
        _mockedService = null;
        _mockedDrawApplication = null;
        _mockedDrawPluginStatic.close();
        _mockedDrawPluginStatic = null;

    }

    @Test
    void testGetSelectedFigures() {
        //given
        Vector<Figure> figureVector = new Vector<>();
        when(_mockedDrawApplication.view()).thenReturn(_mockedDrawingView);
        when(_mockedDrawingView.selection()).thenReturn(figureVector);
        //when
        Vector<Figure> returnedVector = _mockedService.getSelectedFigures();
        //then
        verify(_mockedDrawingView).selection();
        assertThat(figureVector).isEqualTo(returnedVector);
    }

    @Test
    void testGetSelectedFiguresZOrdered() {
        //given
        Vector<Figure> figureVector = new Vector<>();
        when(_mockedDrawApplication.view()).thenReturn(_mockedDrawingView);
        when(_mockedDrawingView.selectionZOrdered()).thenReturn(figureVector);
        //when
        Vector<Figure> returnedVector = _mockedService.getSelectedFiguresZOrdered();
        //then
        verify(_mockedDrawingView).selectionZOrdered();
        assertThat(figureVector).isEqualTo(returnedVector);
    }

    @Test
    void testAddToSelection() {
        //given
        Figure figure = mock(Figure.class);
        when(_mockedDrawApplication.view()).thenReturn(_mockedDrawingView);
        //when
        _mockedService.addToSelection(figure);
        //then
        verify(_mockedDrawingView).addToSelection(figure);
    }

    @Test
    void testAddToSelectionAllCollection() {
        //given
        Vector<Figure> figures = new Vector<>();
        figures.add(mock(Figure.class));
        when(_mockedDrawApplication.view()).thenReturn(_mockedDrawingView);
        //when
        _mockedService.addToSelectionAll(figures);
        //then
        verify(_mockedDrawingView).addToSelectionAll(any(FigureEnumeration.class));
    }

    @Test
    void testAddToSelectionAllEnumeration() {
        //given
        FigureEnumeration figures = mock(FigureEnumeration.class);
        when(_mockedDrawApplication.view()).thenReturn(_mockedDrawingView);
        //when
        _mockedService.addToSelectionAll(figures);
        //then
        verify(_mockedDrawingView).addToSelectionAll(figures);
    }

    @Test
    void testSelectionInvalidateHandles() {
        //given
        when(_mockedDrawApplication.view()).thenReturn(_mockedDrawingView);
        //when
        _mockedService.selectionInvalidateHandles();
        //then
        verify(_mockedDrawingView).selectionInvalidateHandles();
    }

    @ParameterizedTest
    @ValueSource(booleans = { true, false })
    void testSelectOffendingElements(boolean value) {
        //given
        ErrorState errorState = mock(ErrorState.class);
        when(_mockedDrawApplication.selectOffendingElements(errorState)).thenReturn(value);
        //when
        boolean returnedValue = _mockedService.selectOffendingElements(errorState);
        //then
        verify(_mockedDrawApplication).selectOffendingElements(errorState);
        assertThat(returnedValue).isEqualTo(value);
    }

    @ParameterizedTest
    @ValueSource(ints = { 1, 1000, 10, 30, 22, 0 })
    void testGetSelectionCount(int number) {
        //given
        DrawingView drawingView = mock(DrawingView.class);
        when(_mockedDrawApplication.view()).thenReturn(drawingView);
        when(drawingView.selectionCount()).thenReturn(number);
        //when
        int result = _mockedService.getSelectionCount();
        //then
        verify(drawingView).selectionCount();
        Assertions.assertThat(result).isEqualTo(number);
    }

    @Test
    void testClearSelection() {
        //given
        DrawingView drawingView = mock(DrawingView.class);
        when(_mockedDrawApplication.view()).thenReturn(drawingView);
        //when
        _mockedService.clearSelection();
        //then
        verify(drawingView).clearSelection();
    }
}