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

package pp.battleship.client;

import org.junit.Before;
import org.junit.Test;
import pp.battleship.BattleshipConfig;
import pp.battleship.game.client.ClientGameLogic;
import pp.battleship.message.server.GameDetails;
import pp.battleship.model.Battleship;
import pp.battleship.model.Battleship.Status;
import pp.battleship.model.IntPoint;
import pp.battleship.model.Rotation;

import java.util.Properties;

import static java.util.Collections.emptyList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

public class EditorTest {
    private static final BattleshipConfig CONFIG = new BattleshipConfig();

    static {
        final Properties props = new Properties();
        props.setProperty("map.width", "10");
        props.setProperty("map.height", "10");
        props.setProperty("harbor.width", "6");
        props.setProperty("harbor.height", "10");
        props.setProperty("ship.nums", "1,1");
        CONFIG.readFrom(props);
    }

    private ClientGameLogic logic;

    private static IntPoint p(int x, int y) {
        return new IntPoint(x, y);
    }

    @Before
    public void setUp() {
        logic = new ClientGameLogic(null);
        logic.received(new GameDetails(CONFIG));
    }

    @Test
    public void testInit() {
        assertEquals(emptyList(), logic.getOwnMap().getItems());
        assertEquals(2, logic.getHarbor().getItems().size());

        final Battleship ship1 = (Battleship) logic.getHarbor().getItems().get(0);
        checkShip(ship1, 1, 0, 0, Rotation.RIGHT, Status.NORMAL);

        final Battleship ship2 = (Battleship) logic.getHarbor().getItems().get(1);
        checkShip(ship2, 2, 0, 1, Rotation.RIGHT, Status.NORMAL);
    }

    private void checkShip(Battleship ship, int length, int x, int y, Rotation rot, Status status) {
        assertEquals(length, ship.getLength());
        assertEquals(x, ship.getX());
        assertEquals(y, ship.getY());
        assertEquals(rot, ship.getRot());
        assertEquals(status, ship.getStatus());
    }

    @Test
    public void testRun1() {
        logic.clickHarbor(p(0, 0));
        logic.clickOwnMap(p(0, 0)); // place ship 1 at (0,0)

        assertEquals(1, logic.getOwnMap().getShips().count());
        assertEquals(1, logic.getHarbor().getShips().count());
        checkShip((Battleship) logic.getHarbor().getItems().get(0),
                  2, 0, 1, Rotation.RIGHT, Status.NORMAL);
        checkShip((Battleship) logic.getOwnMap().getItems().get(0),
                  1, 0, 0, Rotation.RIGHT, Status.NORMAL);
        assertEquals(logic.getOwnMap().findShipAt(0, 0), logic.getOwnMap().getShips().findFirst().get());
        assertNull(logic.getOwnMap().findShipAt(1, 0));
        assertNull(logic.getOwnMap().findShipAt(0, 1));

        logic.clickOwnMap(p(0, 0)); // select ship1
        logic.clickHarbor(p(0, 0)); // return it to harbor

        assertEquals(0, logic.getOwnMap().getShips().count());
        assertEquals(2, logic.getHarbor().getShips().count());

        logic.clickHarbor(p(0, 0)); // select ship 1 in harbor
        logic.clickOwnMap(p(0, 0)); // place it at (0,0)

        checkShip((Battleship) logic.getHarbor().getItems().get(0),
                  2, 0, 1, Rotation.RIGHT, Status.NORMAL);
        checkShip((Battleship) logic.getOwnMap().getItems().get(0),
                  1, 0, 0, Rotation.RIGHT, Status.NORMAL);

        logic.clickHarbor(p(0, 1)); // select ship 2 in harbor
        logic.clickOwnMap(p(0, 0)); // try to place it at (0,0); this is too close to ship 1

        assertEquals(2, logic.getOwnMap().getShips().count());
        assertEquals(1, logic.getHarbor().getShips().count());
        checkShip((Battleship) logic.getHarbor().getItems().get(0),
                  2, 0, 1, Rotation.RIGHT, Status.VALID_PREVIEW);
        checkShip((Battleship) logic.getOwnMap().getItems().get(0),
                  1, 0, 0, Rotation.RIGHT, Status.NORMAL);
        checkShip((Battleship) logic.getOwnMap().getItems().get(1),
                  2, 0, 0, Rotation.RIGHT, Status.INVALID_PREVIEW);

        logic.rotateShip(); // rotate selected ship 2
        logic.clickOwnMap(p(5, 5)); // place it at (5,5)

        assertEquals(2, logic.getOwnMap().getShips().count());
        assertEquals(0, logic.getHarbor().getShips().count());
        checkShip((Battleship) logic.getOwnMap().getItems().get(0),
                  1, 0, 0, Rotation.RIGHT, Status.NORMAL);
        checkShip((Battleship) logic.getOwnMap().getItems().get(1),
                  2, 5, 5, Rotation.DOWN, Status.NORMAL);

        assertEquals(logic.getOwnMap().getShips().toList().get(1), logic.getOwnMap().findShipAt(5, 5));
        assertEquals(logic.getOwnMap().getShips().toList().get(1), logic.getOwnMap().findShipAt(5, 4));
        assertTrue(logic.isMapComplete());
    }

    @Test
    public void testRun2() {
        logic.clickHarbor(p(0, 0));
        logic.clickOwnMap(p(9, 9)); // place ship 1 at (9,9)

        assertEquals(1, logic.getOwnMap().getShips().count());
        assertEquals(1, logic.getHarbor().getShips().count());
        checkShip((Battleship) logic.getHarbor().getItems().get(0),
                  2, 0, 1, Rotation.RIGHT, Status.NORMAL);
        checkShip((Battleship) logic.getOwnMap().getItems().get(0),
                  1, 9, 9, Rotation.RIGHT, Status.NORMAL);

        assertEquals(logic.getOwnMap().findShipAt(9, 9), logic.getOwnMap().getShips().findFirst().get());
        assertNull(logic.getOwnMap().findShipAt(10, 9));
        assertNull(logic.getOwnMap().findShipAt(9, 10));

        assertFalse(logic.isMapComplete());

        logic.clickHarbor(p(0, 1)); // select ship 2 in harbor of player 2
        logic.clickOwnMap(p(0, 0)); // place it at (0,0)

        assertEquals(2, logic.getOwnMap().getShips().count());
        assertEquals(0, logic.getHarbor().getShips().count());
        assertEquals(logic.getOwnMap().getShips().toList().get(0), logic.getOwnMap().findShipAt(9, 9));
        assertEquals(logic.getOwnMap().getShips().toList().get(1), logic.getOwnMap().findShipAt(0, 0));
        assertEquals(logic.getOwnMap().getShips().toList().get(1), logic.getOwnMap().findShipAt(1, 0));
        assertTrue(logic.isMapComplete());
    }
}
