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

package pp.battleship.game.client;

import org.junit.Before;
import org.junit.Test;
import pp.battleship.message.client.ClientMessage;
import pp.battleship.message.server.EffectMessage;
import pp.battleship.message.server.GameDetails;
import pp.battleship.message.server.StartBattleMessage;
import pp.battleship.model.IntPoint;

import java.io.File;
import java.io.IOException;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class ClientGameLogicTest {
    private ClientGameLogic logic;
    private ClientSender clientSender;
    private ClientState initialState;
    private ClientState editorState;
    private ClientState gameOverState;

    @Before
    public void setUp() {
        clientSender = mock(ClientSender.class);
        initialState = mock(ClientState.class);
        editorState = mock(ClientState.class);
        gameOverState = mock(ClientState.class);
        logic = new ClientGameLogic(clientSender);
        logic.setState(initialState);
    }

    @Test
    public void testSetState() {
        logic.setState(editorState);
        assertEquals(editorState, logic.getState());
    }

    @Test
    public void testShowEditor() {
        when(initialState.showEditor()).thenReturn(true);
        when(editorState.showEditor()).thenReturn(false);
        assertTrue(logic.showEditor());
        logic.setState(editorState);
        assertFalse(logic.showEditor());
    }

    @Test
    public void testShowBattle() {
        when(initialState.showBattle()).thenReturn(false);
        when(gameOverState.showBattle()).thenReturn(true);
        assertFalse(logic.showBattle());
        logic.setState(gameOverState);
        assertTrue(logic.showBattle());
    }

    @Test
    public void testReceivedGameDetails() {
        final GameDetails details = mock(GameDetails.class);
        logic.received(details);
        verify(initialState).receivedGameDetails(details);
    }

    @Test
    public void testMovePreview() {
        final IntPoint pos = new IntPoint(42, 42);
        logic.movePreview(pos);
        verify(initialState).movePreview(pos);
    }

    @Test
    public void testClickOwnMap() {
        final IntPoint pos = new IntPoint(42, 42);
        logic.clickOwnMap(pos);
        verify(initialState).clickOwnMap(pos);
    }

    @Test
    public void testClickHarbor() {
        final IntPoint pos = new IntPoint(42, 42);
        logic.clickHarbor(pos);
        verify(initialState).clickHarbor(pos);
    }

    @Test
    public void testClickOpponentMap() {
        final IntPoint pos = new IntPoint(42, 42);
        logic.clickOpponentMap(pos);
        verify(initialState).clickOpponentMap(pos);
    }

    @Test
    public void testRotateShip() {
        logic.rotateShip();
        verify(initialState).rotateShip();
    }

    @Test
    public void testMapFinished() {
        logic.mapFinished();
        verify(initialState).mapFinished();
    }

    @Test
    public void testIsMapComplete() {
        logic.setState(editorState);
        when(editorState.isMapComplete()).thenReturn(true);
        assertTrue(logic.isMapComplete());
        when(editorState.isMapComplete()).thenReturn(false);
        assertFalse(logic.isMapComplete());
    }

    @Test
    public void testMovingShip() {
        logic.setState(editorState);
        when(editorState.movingShip()).thenReturn(true);
        assertTrue(logic.movingShip());
        when(editorState.movingShip()).thenReturn(false);
        assertFalse(logic.movingShip());
    }

    @Test
    public void testReceivedStartShooting() {
        final StartBattleMessage msg = mock(StartBattleMessage.class);
        logic.received(msg);
        verify(initialState).receivedStartBattle(msg);
    }

    @Test
    public void testReceivedEffectMessage() {
        final EffectMessage msg = mock(EffectMessage.class);
        logic.received(msg);
        verify(initialState).receivedEffect(msg);
    }

    @Test
    public void testLoadMap() throws IOException {
        final File file = mock(File.class);
        logic.loadMap(file);
        verify(initialState).loadMap(file);
    }

    @Test
    public void testMayLoadMap() {
        logic.setState(editorState);
        when(editorState.mayLoadMap()).thenReturn(true);
        assertTrue(logic.mayLoadMap());
        when(editorState.mayLoadMap()).thenReturn(false);
        assertFalse(logic.mayLoadMap());
    }

    @Test
    public void testMaySaveMap() {
        logic.setState(editorState);
        when(editorState.maySaveMap()).thenReturn(true);
        assertTrue(logic.maySaveMap());
        when(editorState.maySaveMap()).thenReturn(false);
        assertFalse(logic.maySaveMap());
    }

    @Test(expected = IOException.class)
    public void testSaveMap() throws IOException {
        final File file = mock(File.class);
        logic.setState(editorState);
        when(editorState.maySaveMap()).thenReturn(false);
        logic.saveMap(file);
    }

    @Test
    public void testSend() {
        final ClientMessage msg = mock(ClientMessage.class);
        logic.send(msg);
        verify(clientSender).send(msg);
    }
}
