package de.renew.draw.ui.api;

import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.Enumeration;
import java.util.Vector;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import de.renew.draw.storables.ontology.Drawing;
import de.renew.draw.storables.ontology.DrawingListener;
import de.renew.draw.ui.impl.services.EditorServiceImpl;
import de.renew.draw.ui.ontology.DrawingEditor;
import de.renew.draw.ui.ontology.DrawingView;

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

class EditorApiTest {

    private EditorServiceImpl _mockedService;
    private Drawing _mockedDrawing;

    @BeforeEach
    void setUp() throws IllegalAccessException, NoSuchFieldException {
        _mockedDrawing = mock(Drawing.class);
        _mockedService = mock(EditorServiceImpl.class);


        Field serviceField = EditorApi.class.getDeclaredField("_service");
        serviceField.setAccessible(true);
        serviceField.set(null, _mockedService);
    }

    @AfterEach
    void tearDown() {
        _mockedService = null;
        _mockedDrawing = null;
    }

    @Test
    void testGetCurrentEditor() {
        // given
        DrawingEditor expectedEditor = mock(DrawingEditor.class);
        when(_mockedService.getCurrentEditor()).thenReturn(expectedEditor);

        // when
        DrawingEditor actualEditor = EditorApi.getCurrentEditor();

        // then
        assertThat(actualEditor).isSameAs(expectedEditor);
        verify(_mockedService).getCurrentEditor();
    }

    @Test
    void testGetNullDrawingEditor() {
        // given
        DrawingEditor expectedEditor = mock(DrawingEditor.class);
        when(_mockedService.getNullDrawingEditor()).thenReturn(expectedEditor);

        // when
        DrawingEditor actualEditor = EditorApi.getNullDrawingEditor();

        // then
        assertThat(actualEditor).isSameAs(expectedEditor);
        verify(_mockedService).getNullDrawingEditor();
    }

    @Test
    void testGetNullDrawingView() {
        // given
        DrawingView expectedView = mock(DrawingView.class);
        when(_mockedService.getNullDrawingView()).thenReturn(expectedView);

        // when
        DrawingView actualView = EditorApi.getNullDrawingView();

        // then
        assertThat(actualView).isSameAs(expectedView);
        verify(_mockedService).getNullDrawingView();
    }


    @Test
    void testGetCurrentDrawing() {
        // given
        when(_mockedService.getCurrentDrawing()).thenReturn(_mockedDrawing);

        //when
        Drawing res = EditorApi.getCurrentDrawing();

        //then
        assertThat(res).isEqualTo(_mockedDrawing);
        verify(_mockedService).getCurrentDrawing();
    }

    @Test
    void testGetCurrentDrawings() {
        //given
        Vector<Drawing> drawings = new Vector<>();
        drawings.add(_mockedDrawing);
        Enumeration enumeration = drawings.elements();
        when(_mockedService.getCurrentDrawings()).thenReturn(enumeration);

        //when
        Enumeration res = EditorApi.getCurrentDrawings();

        //then
        assertThat(enumeration).isEqualTo(res);
        verify(_mockedService).getCurrentDrawings();
    }

    @Test
    void testOpenDrawing() {
        // given
        when(_mockedService.openDrawing(_mockedDrawing)).thenReturn(_mockedDrawing);

        //when
        Drawing res = EditorApi.openDrawing(_mockedDrawing);

        //then
        assertThat(res).isEqualTo(_mockedDrawing);
        verify(_mockedService).openDrawing(_mockedDrawing);
    }

    @Test
    void testOpenDrawingWithDimensionAndPoint() {
        // given
        Point mockedPoint = Mockito.mock(Point.class);
        Dimension mockedDimension = Mockito.mock(Dimension.class);
        when(_mockedService.getCurrentDrawing()).thenReturn(_mockedDrawing);

        //when
        EditorApi.openDrawing(_mockedDrawing, mockedPoint, mockedDimension);

        //then
        verify(_mockedService).openDrawing(_mockedDrawing, mockedPoint, mockedDimension);
    }

    @Test
    void testCloseDrawing() {
        // given/when
        EditorApi.closeDrawing(_mockedDrawing);

        //then
        verify(_mockedService).closeDrawing(_mockedDrawing);
    }

    @Test
    void testAddDrawingListener() {
        //given
        DrawingListener mockedListener = Mockito.mock(DrawingListener.class);

        //when
        EditorApi.addDrawingListener(mockedListener);

        //then
        verify(_mockedService).addDrawingListener(mockedListener);
    }

    @Test
    void testAddRemoveListener() {
        //given
        DrawingListener mockedListener = Mockito.mock(DrawingListener.class);

        //when
        EditorApi.removeDrawingListener(mockedListener);

        //then
        verify(_mockedService).removeDrawingListener(mockedListener);
    }

    @Test
    void testLoadAndOpeDrawingsURL() throws IOException {
        //given
        File dummyFile = new File("test", ".drw");
        URL dummyUrl = dummyFile.toURI().toURL();
        //when
        EditorApi.loadAndOpenDrawings(dummyUrl);
        //then:
        verify(_mockedService).loadAndOpenDrawings(dummyUrl);
    }

    @Test
    void testLoadAndOpeDrawingsInput() throws IOException {
        //given
        InputStream inputStream = mock(InputStream.class);
        String dummyFileName = "test.drw";
        //when
        EditorApi.loadAndOpenDrawings(inputStream, dummyFileName);
        //then
        verify(_mockedService).loadAndOpenDrawings(inputStream, dummyFileName);
    }

    @Test
    void testOpenOrLoadDrawing() throws IOException {
        //given
        File file = File.createTempFile("test", ".drw");
        //when
        EditorApi.openOrLoadDrawing(file);
        //then
        verify(_mockedService).openOrLoadDrawing(file);
    }

    @Test
    void testCheckDamage() {
        //given/when
        EditorApi.checkDamage();
        //then
        verify(_mockedService).checkDamage();
    }

    @Test
    void testRepairDamage() {
        //given/when
        EditorApi.repairDamage();
        //then
        verify(_mockedService).repairDamage();
    }

    @Test
    void testToolDone() {
        //given/when
        EditorApi.toolDone();
        //then
        verify(_mockedService).toolDone();
    }

    @Test
    void testAddMouseListener() {
        //given
        MouseListener mockedListener = Mockito.mock(MouseListener.class);
        //when
        EditorApi.addMouseListener(mockedListener);
        //then
        verify(_mockedService).addMouseListener(mockedListener);
    }

    @Test
    void testRemoveMouseListener() {
        //given
        MouseListener mockedListener = Mockito.mock(MouseListener.class);
        //when
        EditorApi.removeMouseListener(mockedListener);
        //then
        verify(_mockedService).removeMouseListener(mockedListener);
    }
}
