////////////////////////////////////////
// Programming project code
// UniBw M, 2022, 2023, 2024
// www.unibw.de/inf2
// (c) Mark Minas (mark.minas@unibw.de)
////////////////////////////////////////

package pp.battleship.model;

import org.junit.Before;
import org.junit.Test;
import pp.battleship.notification.GameEventBroker;
import pp.battleship.notification.ItemAddedEvent;
import pp.battleship.notification.ItemRemovedEvent;

import java.util.List;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static pp.battleship.model.BattleshipTest.p;

public class ShipMapTest {
    private ShipMap map;
    private GameEventBroker mockBroker;
    private Battleship battleship;

    @Before
    public void setUp() {
        mockBroker = mock(GameEventBroker.class);
        map = new ShipMap(10, 10, mockBroker);
        battleship = new Battleship(3, 2, 2, Rotation.RIGHT);
    }

    @Test
    public void testConstructorWithValidSize() {
        assertEquals(10, map.getWidth());
        assertEquals(10, map.getHeight());
    }

    @Test(expected = IllegalArgumentException.class)
    public void testConstructorWithInvalidSize() {
        new ShipMap(0, 10, null);
    }

    @Test
    public void testAddItem() {
        map.add(battleship);
        List<Item> items = map.getItems();
        assertTrue(items.contains(battleship));
        verify(mockBroker).notifyListeners(any(ItemAddedEvent.class));
    }

    @Test
    public void testRemoveItem() {
        map.add(battleship);
        map.remove(battleship);
        final List<Item> items = map.getItems();
        assertFalse(items.contains(battleship));
        verify(mockBroker).notifyListeners(any(ItemAddedEvent.class));
        verify(mockBroker).notifyListeners(any(ItemRemovedEvent.class));
    }

    @Test
    public void testClear() {
        map.add(battleship);
        map.clear();
        final List<Item> items = map.getItems();
        assertTrue(items.isEmpty());
        verify(mockBroker).notifyListeners(any(ItemAddedEvent.class));
        verify(mockBroker).notifyListeners(any(ItemRemovedEvent.class));
    }

    @Test
    public void testGetRemainingShips() {
        map.add(battleship);
        final List<Battleship> remainingShips = map.getRemainingShips();
        assertTrue(remainingShips.contains(battleship));
    }

    @Test
    public void testGetShots() {
        final Shot shot = new Shot(1, 1, false);
        map.add(shot);
        final List<Shot> shots = map.getShots().toList();
        assertTrue(shots.contains(shot));
    }

    @Test
    public void testGetItems() {
        map.add(battleship);
        final List<Item> items = map.getItems();
        assertTrue(items.contains(battleship));
    }

    @Test
    public void testFindShipAt() {
        map.add(battleship);
        Battleship foundShip = map.findShipAt(2, 2);
        assertEquals(battleship, foundShip);
    }

    @Test
    public void testIsValidPosition() {
        assertTrue(map.isValid(p(5, 5)));
    }

    @Test
    public void testIsValidCoordinates() {
        assertTrue(map.isValid(5, 5));
        assertFalse(map.isValid(10, 10));
    }
}
