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

package pp.battleship.game.server;

import org.junit.Test;
import pp.battleship.message.client.MapMessage;
import pp.battleship.message.client.ShootMessage;
import pp.battleship.message.server.EffectMessage;
import pp.battleship.message.server.GameDetails;
import pp.battleship.message.server.StartBattleMessage;
import pp.battleship.model.Battleship;
import pp.battleship.model.Shot;

import java.util.List;
import java.util.Properties;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static pp.battleship.model.Battleship.Status.NORMAL;
import static pp.battleship.model.Rotation.DOWN;
import static pp.battleship.model.Rotation.RIGHT;

/**
 * Integration test cases for the server game logic in a small game, called "Game 1", where Player 2 (with id 1) wins.
 * The test cases for the players are contained in {@linkplain pp.battleship.game.client.ClientGame1Player1Test}
 * and {@linkplain pp.battleship.game.client.ClientGame1Player2Test}.
 *
 * @see pp.battleship.game.client.ClientGame1Player1Test
 * @see pp.battleship.game.client.ClientGame1Player2Test
 */
public class ServerGame1Test extends AbstractServerGameTest {
    static {
        final Properties props = new Properties();
        props.setProperty("map.width", "6");
        props.setProperty("map.height", "6");
        props.setProperty("ship.nums", "1,1");
        CONFIG.readFrom(props);
    }

    @Test
    public void testRun() {
        EffectMessage effectMsg;
        GameDetails details;
        StartBattleMessage startShooting;

        serverLogic.addPlayer(0); // Player 1
        serverLogic.addPlayer(1); // Player 2
        assertEquals(0, messages.peek().id());
        details = (GameDetails) messages.poll().msg();
        assertEquals(6, details.getWidth());
        assertEquals(6, details.getHeight());
        assertEquals(2, details.getShipNums().size());
        assertEquals(Integer.valueOf(1), details.getShipNums().get(1));
        assertEquals(Integer.valueOf(1), details.getShipNums().get(2));
        assertEquals(1, messages.peek().id());
        details = (GameDetails) messages.poll().msg();
        assertEquals(6, details.getWidth());
        assertEquals(6, details.getHeight());
        assertEquals(2, details.getShipNums().size());
        assertEquals(Integer.valueOf(1), details.getShipNums().get(1));
        assertEquals(Integer.valueOf(1), details.getShipNums().get(2));
        serverLogic.received(new MapMessage(List.of(new Battleship(1, 1, 2, RIGHT), new Battleship(2, 1, 5, DOWN))), 1);
        serverLogic.received(new MapMessage(List.of(new Battleship(2, 2, 1, RIGHT), new Battleship(1, 5, 1, RIGHT))), 0);
        assertEquals(0, messages.peek().id());
        startShooting = (StartBattleMessage) messages.poll().msg();
        assertTrue(startShooting.isMyTurn());
        assertEquals(1, messages.peek().id());
        startShooting = (StartBattleMessage) messages.poll().msg();
        assertFalse(startShooting.isMyTurn());
        serverLogic.received(new ShootMessage(p(3, 3)), 0);
        assertEquals(0, messages.peek().id());
        effectMsg = (EffectMessage) messages.poll().msg();
        assertTrue(effectMsg.isOwnShot());
        assertEquals(new Shot(3, 3, false), effectMsg.getShot());
        assertNull(effectMsg.getDestroyedShip());
        assertNull(effectMsg.getRemainingOpponentShips());
        assertEquals(1, messages.peek().id());
        effectMsg = (EffectMessage) messages.poll().msg();
        assertFalse(effectMsg.isOwnShot());
        assertEquals(new Shot(3, 3, false), effectMsg.getShot());
        assertNull(effectMsg.getDestroyedShip());
        assertNull(effectMsg.getRemainingOpponentShips());
        serverLogic.received(new ShootMessage(p(2, 2)), 1);
        assertEquals(1, messages.peek().id());
        effectMsg = (EffectMessage) messages.poll().msg();
        assertTrue(effectMsg.isOwnShot());
        assertEquals(new Shot(2, 2, false), effectMsg.getShot());
        assertNull(effectMsg.getDestroyedShip());
        assertNull(effectMsg.getRemainingOpponentShips());
        assertEquals(0, messages.peek().id());
        effectMsg = (EffectMessage) messages.poll().msg();
        assertFalse(effectMsg.isOwnShot());
        assertEquals(new Shot(2, 2, false), effectMsg.getShot());
        assertNull(effectMsg.getDestroyedShip());
        assertNull(effectMsg.getRemainingOpponentShips());
        serverLogic.received(new ShootMessage(p(2, 2)), 1);
        serverLogic.received(new ShootMessage(p(1, 3)), 0);
        assertEquals(0, messages.peek().id());
        effectMsg = (EffectMessage) messages.poll().msg();
        assertTrue(effectMsg.isOwnShot());
        assertEquals(new Shot(1, 3, false), effectMsg.getShot());
        assertNull(effectMsg.getDestroyedShip());
        assertNull(effectMsg.getRemainingOpponentShips());
        assertEquals(1, messages.peek().id());
        effectMsg = (EffectMessage) messages.poll().msg();
        assertFalse(effectMsg.isOwnShot());
        assertEquals(new Shot(1, 3, false), effectMsg.getShot());
        assertNull(effectMsg.getDestroyedShip());
        assertNull(effectMsg.getRemainingOpponentShips());
        serverLogic.received(new ShootMessage(p(1, 3)), 0);
        serverLogic.received(new ShootMessage(p(2, 1)), 1);
        assertEquals(1, messages.peek().id());
        effectMsg = (EffectMessage) messages.poll().msg();
        assertTrue(effectMsg.isOwnShot());
        assertEquals(new Shot(2, 1, true), effectMsg.getShot());
        assertNull(effectMsg.getDestroyedShip());
        assertNull(effectMsg.getRemainingOpponentShips());
        assertEquals(0, messages.peek().id());
        effectMsg = (EffectMessage) messages.poll().msg();
        assertFalse(effectMsg.isOwnShot());
        assertEquals(new Shot(2, 1, true), effectMsg.getShot());
        assertNull(effectMsg.getDestroyedShip());
        assertNull(effectMsg.getRemainingOpponentShips());
        serverLogic.received(new ShootMessage(p(3, 1)), 1);
        assertEquals(1, messages.peek().id());
        effectMsg = (EffectMessage) messages.poll().msg();
        assertTrue(effectMsg.isOwnShot());
        assertEquals(new Shot(3, 1, true), effectMsg.getShot());
        checkShip(effectMsg.getDestroyedShip(), 2, 2, 1, RIGHT, NORMAL);
        assertNull(effectMsg.getRemainingOpponentShips());
        assertEquals(0, messages.peek().id());
        effectMsg = (EffectMessage) messages.poll().msg();
        assertFalse(effectMsg.isOwnShot());
        assertEquals(new Shot(3, 1, true), effectMsg.getShot());
        checkShip(effectMsg.getDestroyedShip(), 2, 2, 1, RIGHT, NORMAL);
        assertNull(effectMsg.getRemainingOpponentShips());
        serverLogic.received(new ShootMessage(p(5, 3)), 1);
        assertEquals(1, messages.peek().id());
        effectMsg = (EffectMessage) messages.poll().msg();
        assertTrue(effectMsg.isOwnShot());
        assertEquals(new Shot(5, 3, false), effectMsg.getShot());
        assertNull(effectMsg.getDestroyedShip());
        assertNull(effectMsg.getRemainingOpponentShips());
        assertEquals(0, messages.peek().id());
        effectMsg = (EffectMessage) messages.poll().msg();
        assertFalse(effectMsg.isOwnShot());
        assertEquals(new Shot(5, 3, false), effectMsg.getShot());
        assertNull(effectMsg.getDestroyedShip());
        assertNull(effectMsg.getRemainingOpponentShips());
        serverLogic.received(new ShootMessage(p(2, 3)), 0);
        assertEquals(0, messages.peek().id());
        effectMsg = (EffectMessage) messages.poll().msg();
        assertTrue(effectMsg.isOwnShot());
        assertEquals(new Shot(2, 3, false), effectMsg.getShot());
        assertNull(effectMsg.getDestroyedShip());
        assertNull(effectMsg.getRemainingOpponentShips());
        assertEquals(1, messages.peek().id());
        effectMsg = (EffectMessage) messages.poll().msg();
        assertFalse(effectMsg.isOwnShot());
        assertEquals(new Shot(2, 3, false), effectMsg.getShot());
        assertNull(effectMsg.getDestroyedShip());
        assertNull(effectMsg.getRemainingOpponentShips());
        serverLogic.received(new ShootMessage(p(2, 3)), 0);
        serverLogic.received(new ShootMessage(p(3, 3)), 1);
        assertEquals(1, messages.peek().id());
        effectMsg = (EffectMessage) messages.poll().msg();
        assertTrue(effectMsg.isOwnShot());
        assertEquals(new Shot(3, 3, false), effectMsg.getShot());
        assertNull(effectMsg.getDestroyedShip());
        assertNull(effectMsg.getRemainingOpponentShips());
        assertEquals(0, messages.peek().id());
        effectMsg = (EffectMessage) messages.poll().msg();
        assertFalse(effectMsg.isOwnShot());
        assertEquals(new Shot(3, 3, false), effectMsg.getShot());
        assertNull(effectMsg.getDestroyedShip());
        assertNull(effectMsg.getRemainingOpponentShips());
        serverLogic.received(new ShootMessage(p(1, 4)), 0);
        assertEquals(0, messages.peek().id());
        effectMsg = (EffectMessage) messages.poll().msg();
        assertTrue(effectMsg.isOwnShot());
        assertEquals(new Shot(1, 4, true), effectMsg.getShot());
        assertNull(effectMsg.getDestroyedShip());
        assertNull(effectMsg.getRemainingOpponentShips());
        assertEquals(1, messages.peek().id());
        effectMsg = (EffectMessage) messages.poll().msg();
        assertFalse(effectMsg.isOwnShot());
        assertEquals(new Shot(1, 4, true), effectMsg.getShot());
        assertNull(effectMsg.getDestroyedShip());
        assertNull(effectMsg.getRemainingOpponentShips());
        serverLogic.received(new ShootMessage(p(1, 4)), 0);
        assertEquals(0, messages.peek().id());
        effectMsg = (EffectMessage) messages.poll().msg();
        assertTrue(effectMsg.isOwnShot());
        assertEquals(new Shot(1, 4, true), effectMsg.getShot());
        assertNull(effectMsg.getDestroyedShip());
        assertNull(effectMsg.getRemainingOpponentShips());
        assertEquals(1, messages.peek().id());
        effectMsg = (EffectMessage) messages.poll().msg();
        assertFalse(effectMsg.isOwnShot());
        assertEquals(new Shot(1, 4, true), effectMsg.getShot());
        assertNull(effectMsg.getDestroyedShip());
        assertNull(effectMsg.getRemainingOpponentShips());
        serverLogic.received(new ShootMessage(p(1, 5)), 0);
        assertEquals(0, messages.peek().id());
        effectMsg = (EffectMessage) messages.poll().msg();
        assertTrue(effectMsg.isOwnShot());
        assertEquals(new Shot(1, 5, true), effectMsg.getShot());
        checkShip(effectMsg.getDestroyedShip(), 2, 1, 5, DOWN, NORMAL);
        assertNull(effectMsg.getRemainingOpponentShips());
        assertEquals(1, messages.peek().id());
        effectMsg = (EffectMessage) messages.poll().msg();
        assertFalse(effectMsg.isOwnShot());
        assertEquals(new Shot(1, 5, true), effectMsg.getShot());
        checkShip(effectMsg.getDestroyedShip(), 2, 1, 5, DOWN, NORMAL);
        assertNull(effectMsg.getRemainingOpponentShips());
        serverLogic.received(new ShootMessage(p(2, 2)), 0);
        assertEquals(0, messages.peek().id());
        effectMsg = (EffectMessage) messages.poll().msg();
        assertTrue(effectMsg.isOwnShot());
        assertEquals(new Shot(2, 2, false), effectMsg.getShot());
        assertNull(effectMsg.getDestroyedShip());
        assertNull(effectMsg.getRemainingOpponentShips());
        assertEquals(1, messages.peek().id());
        effectMsg = (EffectMessage) messages.poll().msg();
        assertFalse(effectMsg.isOwnShot());
        assertEquals(new Shot(2, 2, false), effectMsg.getShot());
        assertNull(effectMsg.getDestroyedShip());
        assertNull(effectMsg.getRemainingOpponentShips());
        serverLogic.received(new ShootMessage(p(5, 1)), 1);
        assertEquals(1, messages.peek().id());
        effectMsg = (EffectMessage) messages.poll().msg();
        assertTrue(effectMsg.isOwnShot());
        assertEquals(new Shot(5, 1, true), effectMsg.getShot());
        checkShip(effectMsg.getDestroyedShip(), 1, 5, 1, RIGHT, NORMAL);
        assertEquals(0, effectMsg.getRemainingOpponentShips().size());
        assertEquals(0, messages.peek().id());
        effectMsg = (EffectMessage) messages.poll().msg();
        assertFalse(effectMsg.isOwnShot());
        assertEquals(new Shot(5, 1, true), effectMsg.getShot());
        checkShip(effectMsg.getDestroyedShip(), 1, 5, 1, RIGHT, NORMAL);
        assertEquals(1, effectMsg.getRemainingOpponentShips().size());
        checkShip(effectMsg.getRemainingOpponentShips().get(0), 1, 1, 2, RIGHT, NORMAL);
        // Player 2 wins

        assertEquals(0, messages.size());
    }
}
