package elevator;

import static org.junit.Assert.*;

import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;

import org.junit.Before;
import org.junit.Test;

import common.Direction;
import common.Motor;
import communication.messages.ElevatorActionMessage;
import communication.messages.InitializeMessage;
import elevator.Elevator.ElevatorAction;
import elevator.Elevator.Light;
import floor.input.InputEvent;

public class ElevatorSubsystemTest {

	private ElevatorSubsystem elevatorSubsystem;
	private final int numFloors = 5;
	private final int numElevators = 6;
	private int floorMoveTime = 0;
	private int doorOperationDelay = 0;

	@Before
	public void init() throws SocketException {
		// create subsystem and set delays to 0 for tests, set numFloors and numElevators
		elevatorSubsystem = new ElevatorSubsystem();
		InitializeMessage initializeMessage = new InitializeMessage(numFloors, numElevators, floorMoveTime, doorOperationDelay);
		elevatorSubsystem.initializeSystem(initializeMessage);
	}

	@Test
	public void testArrivedAtDestination() {
		int elevatorId = 3;
		int floor = 2;
		Elevator elevator = elevatorSubsystem.getElevatorById(elevatorId);
		elevator.addDestinationFloor(floor, Direction.UP);
		ElevatorActionMessage actionMessage = new ElevatorActionMessage(elevatorId,
			ElevatorAction.ARRIVED_AT_DESTINATION, floor);

		elevatorSubsystem.handleMessage(actionMessage);

		assertEquals(Motor.STOPPED, elevator.getMotorStatus());
		assertFalse(elevator.isDoorClosed());
		assertFalse(elevator.getDestinationFloors(Direction.UP).contains(floor));
	}

	@Test
	public void testMoveUp() {
		int elevatorId = 1;
		Elevator elevator = elevatorSubsystem.getElevatorById(elevatorId);
		elevator.openDoors();
		ElevatorActionMessage actionMessage = new ElevatorActionMessage(elevatorId, ElevatorAction.MOVE_UP, 2);

		elevatorSubsystem.handleMessage(actionMessage);

		assertEquals(Motor.UP, elevator.getMotorStatus());
		assertTrue(elevator.isDoorClosed());
	}

	@Test
	public void testMoveDown() {
		int elevatorId = 1;
		Elevator elevator = elevatorSubsystem.getElevatorById(elevatorId);
		elevator.openDoors();
		ElevatorActionMessage actionMessage = new ElevatorActionMessage(elevatorId, ElevatorAction.MOVE_DOWN, 2);

		elevatorSubsystem.handleMessage(actionMessage);

		assertEquals(Motor.DOWN, elevator.getMotorStatus());
		assertTrue(elevator.isDoorClosed());
	}

	@Test
	public void testAddDestinationFloorWithCarButton() {
		int elevatorId = 1;
		int floor = 2;
		Elevator elevator = elevatorSubsystem.getElevatorById(elevatorId);
		elevator.setCurrentFloor(3);
		ElevatorActionMessage actionMessage = new ElevatorActionMessage(elevatorId, ElevatorAction.ADD_DESTINATION_FLOOR, floor);

		elevatorSubsystem.handleMessage(actionMessage);

		assertFalse(elevator.containsDestinationFloor(Direction.UP, floor));
		assertTrue(elevator.containsDestinationFloor(Direction.DOWN, floor));
		assertEquals(Light.ON, elevator.getElevatorButton(floor));
	}

	@Test
	public void testAddDestinationFloorNoCarButton() {
		int elevatorId = 1;
		int floor = 2;
		Direction directionButton = Direction.UP;

		Elevator elevator = elevatorSubsystem.getElevatorById(elevatorId);
		elevator.setCurrentFloor(1);
		InputEvent input = new InputEvent(1000, 2, directionButton, floor, null, 0);
		ElevatorActionMessage actionMessage = new ElevatorActionMessage(elevatorId, ElevatorAction.ADD_DESTINATION_FLOOR, floor, input, null);

		elevatorSubsystem.handleMessage(actionMessage);

		assertTrue(elevator.containsDestinationFloor(Direction.UP, floor));
		assertFalse(elevator.containsDestinationFloor(Direction.DOWN, floor));
		assertEquals(Light.OFF, elevator.getElevatorButton(floor));
	}

	@Test
	public void testContinueMoving() {
		int elevatorId = 1;
		int floor = 2;
		Elevator elevator = elevatorSubsystem.getElevatorById(elevatorId);
		ElevatorActionMessage actionMessage = new ElevatorActionMessage(elevatorId, ElevatorAction.CONTINUE_MOVING, floor);
		elevatorSubsystem.handleMessage(actionMessage);

		assertEquals(floor, elevator.getCurrentFloor());
	}

	@Test
	public void testContinueMovingDestination() {
		int elevatorId = 1;
		int floor = 2;
		Elevator elevator = elevatorSubsystem.getElevatorById(elevatorId);
		ElevatorActionMessage actionMessage = new ElevatorActionMessage(elevatorId, ElevatorAction.CONTINUE_MOVING, floor);

		elevatorSubsystem.handleMessage(actionMessage);

		assertEquals(floor, elevator.getCurrentFloor());
	}

	@Test
	public void testInit() throws Exception {
		List<Elevator> elevators = new ArrayList<>();
		for (int i = 1; i <= numElevators; i++) {
			Elevator elevator = elevatorSubsystem.getElevatorById(i);
			assertNotNull(elevator);
			assertTrue(!elevators.contains(elevator));
			elevators.add(elevator);
			assertEquals(1, elevator.getCurrentFloor());
			assertEquals(i, elevator.getElevatorId());
		}
	}

}
