package CH.ifa.draw.application;

import javax.swing.JMenu;
import javax.swing.JMenuItem;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.Mockito.mock;


@ExtendWith(MockitoExtension.class)
public class MenuManagerTest {

    private static MenuManager _testee;


    private String _topLevelSingleId;
    private static int _topLevelCounter = 0;

    @BeforeAll
    public static void setUpAll() {
        _testee = MenuManager.getInstance();
        final DrawApplication gui = mock(DrawApplication.class);
        _testee.setGui(gui);
    }

    @BeforeEach
    public void setUp() {
        _topLevelCounter++;
        _topLevelSingleId = "parentTestMenu" + _topLevelCounter;
    }

    @Test
    public void testInitialization() {
        // when
        final MenuManager testee = MenuManager.getInstance();

        // then
        assertNotNull(testee);
    }

    @Test
    public void testMenuItemExistsAfterRegisteringIt() {
        // given
        final JMenuItem testItem = new JMenuItem();
        testItem.putClientProperty(MenuManager.ID_PROPERTY, "testValue");

        // when
        _testee.registerMenu("testParent", testItem);

        // then
        final JMenu menu = _testee.getToplevelMenu("testParent");
        assertNotNull(menu);
        assertEquals("testParent", menu.getText());
    }

    @Test
    public void testUnregisterMenuButTopLevelRemains() {
        // given
        final String idOfMenu = "menuItem1";
        final JMenuItem testItem1 = new JMenuItem();
        testItem1.putClientProperty(MenuManager.ID_PROPERTY, idOfMenu);
        final JMenuItem testItem2 = new JMenuItem();
        testItem2.putClientProperty(MenuManager.ID_PROPERTY, "menuItem2");
        _testee.registerMenu(_topLevelSingleId, testItem1);
        _testee.registerMenu(_topLevelSingleId, testItem2);

        // when
        _testee.unregisterMenu(idOfMenu);

        // then
        final JMenu topLevelMenu = _testee.getToplevelMenu(_topLevelSingleId);
        assertEquals(1, topLevelMenu.getItemCount());
        assertEquals(testItem2, topLevelMenu.getItem(0));
    }

    @Test
    public void testUnregisterMenuRemoveTrailingSeparators() {
        // given
        final String idOfMenu = "menuItem1";
        final JMenuItem testItem1 = new JMenuItem();
        testItem1.putClientProperty(MenuManager.ID_PROPERTY, idOfMenu);
        final JMenuItem testItem2 = new JMenuItem();
        testItem2.putClientProperty(MenuManager.ID_PROPERTY, "menuItem2");
        final JMenuItem separator = MenuManager.createSeparator("separator1");
        separator.putClientProperty(MenuManager.ID_PROPERTY, "sep1");

        _testee.registerMenu(_topLevelSingleId, testItem1);
        _testee.registerMenu(_topLevelSingleId, testItem2);
        _testee.registerMenu(_topLevelSingleId, separator);

        // when
        _testee.unregisterMenu(testItem1);

        // then
        final JMenu topLevelMenu = _testee.getToplevelMenu(_topLevelSingleId);
        assertEquals(1, topLevelMenu.getItemCount());
        assertEquals(testItem2, topLevelMenu.getItem(0));
    }

    @Test
    public void testUnregisterMenuRemoveLeadingSeparators() {
        // given
        final String idOfMenu = "menuItem1";
        final JMenuItem testItem1 = new JMenuItem();
        testItem1.putClientProperty(MenuManager.ID_PROPERTY, idOfMenu);
        final JMenuItem separator = MenuManager.createSeparator("separator1");
        separator.putClientProperty(MenuManager.ID_PROPERTY, "sep1");
        final JMenuItem testItem2 = new JMenuItem();
        testItem2.putClientProperty(MenuManager.ID_PROPERTY, "menuItem2");

        _testee.registerMenu(_topLevelSingleId, testItem1);
        _testee.registerMenu(_topLevelSingleId, separator);
        _testee.registerMenu(_topLevelSingleId, testItem2);

        // when
        _testee.unregisterMenu(testItem1);

        // then
        final JMenu topLevelMenu = _testee.getToplevelMenu(_topLevelSingleId);
        assertEquals(1, topLevelMenu.getItemCount());
        assertEquals(testItem2, topLevelMenu.getItem(0));
    }

    @Test
    public void testRemoveTopLevelMenuIfLastItemIsUnregistered() {
        // given
        final String menuItem1 = "menuItem1";
        final JMenuItem testItem1 = new JMenuItem();
        testItem1.putClientProperty(MenuManager.ID_PROPERTY, menuItem1);

        _testee.registerMenu(_topLevelSingleId, testItem1);

        // when
        _testee.unregisterMenu(menuItem1);

        // then
        final JMenu topLevelMenu = _testee.getToplevelMenu(_topLevelSingleId);
        assertNull(topLevelMenu);
    }

    @Test
    public void testRegisterMenu() {
        //given
        JMenuItem menuItem1 = new JMenuItem();
        JMenuItem menuItem2 = new JMenuItem();
        final String testItem1 = "menuItem1";
        final String testItem2 = "menuItem2";

        menuItem1.putClientProperty(MenuManager.ID_PROPERTY, testItem1);
        menuItem2.putClientProperty(MenuManager.ID_PROPERTY, testItem2);
        //when
        _testee.registerMenu(_topLevelSingleId, menuItem1, 1, 2);
        _testee.registerMenu(_topLevelSingleId, menuItem2, 1, 1);

        //then
        JMenu topLevelMenu = _testee.getToplevelMenu(_topLevelSingleId);
        assertEquals(menuItem1, topLevelMenu.getItem(0));
        assertEquals(menuItem2, topLevelMenu.getItem(1));
    }

    @Test
    public void testRegisterMenuWhereMiddle() {
        //given
        JMenuItem menuItem1 = new JMenuItem();
        JMenuItem menuItem2 = new JMenuItem();
        JMenuItem menuItem3 = new JMenuItem();
        final String testItem1 = "menuItem1";
        final String testItem2 = "menuItem2";
        final String testItem3 = "menuItem3";

        menuItem1.putClientProperty(MenuManager.ID_PROPERTY, testItem1);
        menuItem2.putClientProperty(MenuManager.ID_PROPERTY, testItem2);
        menuItem3.putClientProperty(MenuManager.ID_PROPERTY, testItem3);

        _testee.registerMenu(_topLevelSingleId, menuItem1, 1, 0);
        _testee.registerMenu(_topLevelSingleId, menuItem2, 1, 1);

        //when
        _testee.registerMenu(_topLevelSingleId, menuItem3, 1, 2);

        //then
        JMenu topLevelMenu = _testee.getToplevelMenu(_topLevelSingleId);
        assertEquals(menuItem1, topLevelMenu.getItem(0));
        assertEquals(menuItem2, topLevelMenu.getItem(2));
        assertEquals(menuItem3, topLevelMenu.getItem(1));
    }

    @Test
    public void testUnregisterMenuReduceAdjacentSeparatorsToOne() {
        // given
        final JMenuItem testItem1 = new JMenuItem();
        testItem1.putClientProperty(MenuManager.ID_PROPERTY, "menuItem1");
        final JMenuItem testItem2 = new JMenuItem();
        testItem2.putClientProperty(MenuManager.ID_PROPERTY, "menuItem2");

        final JMenuItem separator1 = MenuManager.createSeparator("separator1");
        separator1.putClientProperty(MenuManager.ID_PROPERTY, "sep1");
        final JMenuItem separator2 = MenuManager.createSeparator("separator2");
        separator2.putClientProperty(MenuManager.ID_PROPERTY, "sep2");
        final JMenuItem separator3 = MenuManager.createSeparator("separator3");
        separator3.putClientProperty(MenuManager.ID_PROPERTY, "sep3");

        final JMenuItem testItem3 = new JMenuItem();
        testItem3.putClientProperty(MenuManager.ID_PROPERTY, "menuItem3");


        _testee.registerMenu(_topLevelSingleId, testItem1);
        _testee.registerMenu(_topLevelSingleId, testItem2);
        _testee.registerMenu(_topLevelSingleId, separator1);
        _testee.registerMenu(_topLevelSingleId, separator2);
        _testee.registerMenu(_topLevelSingleId, separator3);
        _testee.registerMenu(_topLevelSingleId, testItem3);


        // when
        _testee.unregisterMenu(testItem1);

        // then
        final JMenu topLevelMenu = _testee.getToplevelMenu(_topLevelSingleId);
        assertEquals(3, topLevelMenu.getItemCount());
        assertEquals(testItem2, topLevelMenu.getItem(0));
    }
}
