////////////////////////////////////////
// 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.Test;
import pp.battleship.BattleshipConfig;
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 java.util.List;
import java.util.Properties;

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

/**
 * Integration test cases for the client game logic of Player 1 in a small game, called "Game 1".
 * The test cases for Player 2 are contained in {@linkplain ClientGame1Player2Test}
 * and the test cases for the server in {@linkplain pp.battleship.game.server.ServerGame1Test}.
 *
 * @see ClientGame1Player2Test
 * @see pp.battleship.game.server.ServerGame1Test
 */
public class ClientGame1Player1Test extends AbstractClientGameTest {
    @Test
    public void testClient() {
        BattleshipConfig config;
        Properties props;
        List<Battleship> ships;
        MapMessage mapMsg;
        ShootMessage shootMsg;

        config = new BattleshipConfig();
        props = new Properties();
        props.setProperty("map.width", "6");
        props.setProperty("map.height", "6");
        props.setProperty("harbor.width", "6");
        props.setProperty("harbor.height", "6");
        props.setProperty("ship.nums", "1,1");
        config.readFrom(props);
        clientLogic.received(new GameDetails(config));
        assertEquals("place.ships.in.your.map", infoTexts.poll());
        clientLogic.clickHarbor(p(0, 1));
        ships = clientLogic.getOwnMap().getRemainingShips();
        assertEquals(1, ships.size());
        checkShip(ships.get(0), 2, 0, 0, RIGHT, VALID_PREVIEW);
        ships = clientLogic.getHarbor().getRemainingShips();
        assertEquals(2, ships.size());
        checkShip(ships.get(0), 1, 0, 0, RIGHT, NORMAL);
        checkShip(ships.get(1), 2, 0, 1, RIGHT, VALID_PREVIEW);
        ships = clientLogic.getOwnMap().getRemainingShips();
        assertEquals(1, ships.size());
        checkShip(ships.get(0), 2, 0, 0, RIGHT, VALID_PREVIEW);
        ships = clientLogic.getHarbor().getRemainingShips();
        assertEquals(2, ships.size());
        checkShip(ships.get(0), 1, 0, 0, RIGHT, NORMAL);
        checkShip(ships.get(1), 2, 0, 1, RIGHT, VALID_PREVIEW);
        clientLogic.movePreview(p(5, 1));
        clientLogic.movePreview(p(4, 1));
        clientLogic.movePreview(p(4, 1));
        clientLogic.movePreview(p(3, 1));
        clientLogic.movePreview(p(2, 1));
        clientLogic.movePreview(p(2, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(0, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(1, 1));
        clientLogic.movePreview(p(2, 1));
        clientLogic.movePreview(p(2, 1));
        clientLogic.movePreview(p(2, 1));
        clientLogic.movePreview(p(2, 1));
        clientLogic.movePreview(p(2, 1));
        clientLogic.movePreview(p(2, 1));
        ships = clientLogic.getOwnMap().getRemainingShips();
        assertEquals(1, ships.size());
        checkShip(ships.get(0), 2, 2, 1, RIGHT, VALID_PREVIEW);
        ships = clientLogic.getHarbor().getRemainingShips();
        assertEquals(2, ships.size());
        checkShip(ships.get(0), 1, 0, 0, RIGHT, NORMAL);
        checkShip(ships.get(1), 2, 0, 1, RIGHT, VALID_PREVIEW);
        clientLogic.clickOwnMap(p(2, 1));
        ships = clientLogic.getOwnMap().getRemainingShips();
        assertEquals(1, ships.size());
        checkShip(ships.get(0), 2, 2, 1, RIGHT, NORMAL);
        ships = clientLogic.getHarbor().getRemainingShips();
        assertEquals(1, ships.size());
        checkShip(ships.get(0), 1, 0, 0, RIGHT, NORMAL);
        clientLogic.clickHarbor(p(0, 0));
        ships = clientLogic.getOwnMap().getRemainingShips();
        assertEquals(2, ships.size());
        checkShip(ships.get(0), 2, 2, 1, RIGHT, NORMAL);
        checkShip(ships.get(1), 1, 0, 0, RIGHT, VALID_PREVIEW);
        ships = clientLogic.getHarbor().getRemainingShips();
        assertEquals(1, ships.size());
        checkShip(ships.get(0), 1, 0, 0, RIGHT, VALID_PREVIEW);
        ships = clientLogic.getOwnMap().getRemainingShips();
        assertEquals(2, ships.size());
        checkShip(ships.get(0), 2, 2, 1, RIGHT, NORMAL);
        checkShip(ships.get(1), 1, 0, 0, RIGHT, VALID_PREVIEW);
        ships = clientLogic.getHarbor().getRemainingShips();
        assertEquals(1, ships.size());
        checkShip(ships.get(0), 1, 0, 0, RIGHT, VALID_PREVIEW);
        clientLogic.movePreview(p(5, 1));
        clientLogic.movePreview(p(5, 1));
        clientLogic.movePreview(p(5, 1));
        clientLogic.movePreview(p(5, 1));
        clientLogic.movePreview(p(5, 1));
        clientLogic.movePreview(p(5, 1));
        clientLogic.movePreview(p(5, 1));
        clientLogic.movePreview(p(5, 1));
        clientLogic.movePreview(p(5, 1));
        clientLogic.movePreview(p(5, 1));
        clientLogic.movePreview(p(5, 1));
        clientLogic.movePreview(p(5, 1));
        clientLogic.movePreview(p(5, 1));
        ships = clientLogic.getOwnMap().getRemainingShips();
        assertEquals(2, ships.size());
        checkShip(ships.get(0), 2, 2, 1, RIGHT, NORMAL);
        checkShip(ships.get(1), 1, 5, 1, RIGHT, VALID_PREVIEW);
        ships = clientLogic.getHarbor().getRemainingShips();
        assertEquals(1, ships.size());
        checkShip(ships.get(0), 1, 0, 0, RIGHT, VALID_PREVIEW);
        clientLogic.clickOwnMap(p(5, 1));
        ships = clientLogic.getOwnMap().getRemainingShips();
        assertEquals(2, ships.size());
        checkShip(ships.get(0), 2, 2, 1, RIGHT, NORMAL);
        checkShip(ships.get(1), 1, 5, 1, RIGHT, NORMAL);
        ships = clientLogic.getHarbor().getRemainingShips();
        assertEquals(0, ships.size());
        clientLogic.mapFinished();
        mapMsg = (MapMessage) messages.poll();
        assertEquals(2, mapMsg.getShips().size());
        checkShip(mapMsg.getShips().get(0), 2, 2, 1, RIGHT, NORMAL);
        checkShip(mapMsg.getShips().get(1), 1, 5, 1, RIGHT, NORMAL);
        assertEquals("wait.for.opponent", infoTexts.poll());
        clientLogic.received(new StartBattleMessage(true));
        assertEquals("its.your.turn", infoTexts.poll());
        clientLogic.clickOpponentMap(p(3, 3));
        shootMsg = (ShootMessage) messages.poll();
        assertEquals(p(3, 3), shootMsg.getPosition());
        clientLogic.received(EffectMessage.miss(true, p(3, 3)));
        assertEquals("wait.for.opponent", infoTexts.poll());
        clientLogic.received(EffectMessage.miss(false, p(2, 2)));
        assertEquals("its.your.turn", infoTexts.poll());
        clientLogic.clickOpponentMap(p(1, 3));
        shootMsg = (ShootMessage) messages.poll();
        assertEquals(p(1, 3), shootMsg.getPosition());
        clientLogic.clickOpponentMap(p(1, 3));
        shootMsg = (ShootMessage) messages.poll();
        assertEquals(p(1, 3), shootMsg.getPosition());
        clientLogic.received(EffectMessage.miss(true, p(1, 3)));
        assertEquals("wait.for.opponent", infoTexts.poll());
        clientLogic.received(EffectMessage.hit(false, p(2, 1)));
        assertEquals("wait.for.opponent", infoTexts.poll());
        clientLogic.received(EffectMessage.shipDestroyed(false, p(3, 1), new Battleship(2, 2, 1, RIGHT)));
        assertEquals("wait.for.opponent", infoTexts.poll());
        clientLogic.received(EffectMessage.miss(false, p(5, 3)));
        assertEquals("its.your.turn", infoTexts.poll());
        clientLogic.clickOpponentMap(p(2, 3));
        shootMsg = (ShootMessage) messages.poll();
        assertEquals(p(2, 3), shootMsg.getPosition());
        clientLogic.clickOpponentMap(p(2, 3));
        shootMsg = (ShootMessage) messages.poll();
        assertEquals(p(2, 3), shootMsg.getPosition());
        clientLogic.received(EffectMessage.miss(true, p(2, 3)));
        assertEquals("wait.for.opponent", infoTexts.poll());
        clientLogic.received(EffectMessage.miss(false, p(3, 3)));
        assertEquals("its.your.turn", infoTexts.poll());
        clientLogic.clickOpponentMap(p(1, 4));
        shootMsg = (ShootMessage) messages.poll();
        assertEquals(p(1, 4), shootMsg.getPosition());
        clientLogic.clickOpponentMap(p(1, 4));
        shootMsg = (ShootMessage) messages.poll();
        assertEquals(p(1, 4), shootMsg.getPosition());
        clientLogic.received(EffectMessage.hit(true, p(1, 4)));
        assertEquals("its.your.turn", infoTexts.poll());
        clientLogic.received(EffectMessage.hit(true, p(1, 4)));
        assertEquals("its.your.turn", infoTexts.poll());
        clientLogic.clickOpponentMap(p(1, 5));
        shootMsg = (ShootMessage) messages.poll();
        assertEquals(p(1, 5), shootMsg.getPosition());
        clientLogic.received(EffectMessage.shipDestroyed(true, p(1, 5), new Battleship(2, 1, 5, DOWN)));
        assertEquals("its.your.turn", infoTexts.poll());
        ships = clientLogic.getOpponentMap().getShips().toList();
        assertEquals(1, ships.size());
        checkShip(ships.get(0), 2, 1, 5, DOWN, NORMAL);
        clientLogic.clickOpponentMap(p(2, 2));
        shootMsg = (ShootMessage) messages.poll();
        assertEquals(p(2, 2), shootMsg.getPosition());
        clientLogic.received(EffectMessage.miss(true, p(2, 2)));
        assertEquals("wait.for.opponent", infoTexts.poll());
        clientLogic.received(EffectMessage.lost(p(5, 1), new Battleship(1, 5, 1, RIGHT), List.of(new Battleship(1, 1, 2, RIGHT))));
        assertEquals("you.lost.the.game", infoTexts.poll());
        ships = clientLogic.getOpponentMap().getShips().toList();
        assertEquals(2, ships.size());
        checkShip(ships.get(0), 2, 1, 5, DOWN, NORMAL);
        checkShip(ships.get(1), 1, 1, 2, RIGHT, NORMAL);

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