package scheduler;

import static org.junit.Assert.assertEquals;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

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

import common.Direction;
import common.Motor;

/**
 * This class consists of unit tests for the scheduler's static helper methods
 *
 * @author Keyan
 *
 */
public class SchedulerUtilTest {

	private MetaElevator e1, e2 ,e3;
	private List<MetaElevator> lst;

	@Before
	public void setUp() throws Exception {
		SystemGUI.createSystemGUI(3, 15, false);
		
		e1 = new MetaElevator(1, 1);
		e2 = new MetaElevator(2, 1);
		e3 = new MetaElevator(3, 1);

		lst = new ArrayList<>();
		lst.add(e1);
		lst.add(e2);
		lst.add(e3);
	}

	@Test
	public void TestGetClosestElevatorToFloor() {
		e1.setCurrentFloor(1);
		e2.setCurrentFloor(3);
		e3.setCurrentFloor(8);

		assertEquals(e1, SchedulerUtil.getClosestElevatorToFloor(lst, 1));
		assertEquals(e1, SchedulerUtil.getClosestElevatorToFloor(lst, 2));
		assertEquals(e2, SchedulerUtil.getClosestElevatorToFloor(lst, 4));
		assertEquals(e3, SchedulerUtil.getClosestElevatorToFloor(lst, 9));
		assertEquals(e3, SchedulerUtil.getClosestElevatorToFloor(lst, 10));

	}

	@Test
	public void TestGetElevatorWithLeastTravelDistanceToFloor() {
		e1.setCurrentFloor(1);
		e2.setCurrentFloor(5);
		e3.setCurrentFloor(10);

		e1.setMotorStatus(Motor.UP);
		e1.addDestinationFloor(2, Direction.UP);
		e1.addDestinationFloor(4, Direction.UP);
		e1.addDestinationFloor(7, Direction.UP);

		e2.setMotorStatus(Motor.UP);
		e2.addDestinationFloor(6, Direction.UP);
		e2.addDestinationFloor(3, Direction.DOWN);

		e3.setMotorStatus(Motor.DOWN);
		e3.addDestinationFloor(8, Direction.DOWN);
		e3.addDestinationFloor(6, Direction.DOWN);


		assertEquals(e2, SchedulerUtil.getElevatorWithLeastTravelDistanceToFloor(lst, 4, Direction.DOWN));
		assertEquals(e2, SchedulerUtil.getElevatorWithLeastTravelDistanceToFloor(lst, 9, Direction.UP));

		e2.setMotorStatus(Motor.DOWN);
		assertEquals(e3, SchedulerUtil.getElevatorWithLeastTravelDistanceToFloor(lst, 7, Direction.UP));

		e1.clearDestinations();
		e1.addDestinationFloor(2, Direction.UP);
		assertEquals(e1, SchedulerUtil.getElevatorWithLeastTravelDistanceToFloor(lst, 3, Direction.UP));
	}

	@Test
	public void TestGetElevatorsPassingByFloor() {
		e1.setCurrentFloor(1);
		e1.setMotorStatus(Motor.UP);
		e1.addDestinationFloor(5, Direction.UP);

		e2.setCurrentFloor(8);
		e2.setMotorStatus(Motor.DOWN);
		e2.addDestinationFloor(2, Direction.DOWN);

		e3.setCurrentFloor(10);
		e3.setMotorStatus(Motor.UP);
		e3.addDestinationFloor(15, Direction.UP);

		List<MetaElevator> expected = new ArrayList<>();
		List<MetaElevator> actual;

		expected.add(e1);
		expected.add(e2);
		actual = SchedulerUtil.getElevatorsPassingByFloor(lst, 4, Direction.DOWN);
		assertEquals(true, expected.contains(e2) && actual.contains(e2));

		expected = new ArrayList<>();
		expected.add(e2);
		actual = SchedulerUtil.getElevatorsPassingByFloor(lst, 5, Direction.DOWN);
		assertEquals(true, expected.containsAll(actual) && actual.containsAll(expected));

		expected = new ArrayList<>();
		expected.add(e3);
		actual = SchedulerUtil.getElevatorsPassingByFloor(lst, 12, Direction.UP);
		assertEquals(true, expected.containsAll(actual) && actual.containsAll(expected));

		expected = new ArrayList<>();
		expected.add(e1);
		actual = SchedulerUtil.getElevatorsPassingByFloor(lst, 2, Direction.UP);
		assertEquals(true, expected.containsAll(actual) && actual.containsAll(expected));

		expected = new ArrayList<>();
		actual = SchedulerUtil.getElevatorsPassingByFloor(lst, 9, Direction.UP);
		assertEquals(true, expected.containsAll(actual) && actual.containsAll(expected));

		expected = new ArrayList<>();
		actual = SchedulerUtil.getElevatorsPassingByFloor(lst, 1, Direction.UP);
		assertEquals(true, expected.containsAll(actual) && actual.containsAll(expected));

		expected = new ArrayList<>();
		actual = SchedulerUtil.getElevatorsPassingByFloor(lst, 20, Direction.UP);
		assertEquals(true, expected.containsAll(actual) && actual.containsAll(expected));
	}

	@Test
	public void TestIsElevatorPassingByFloor() {
		e1.setCurrentFloor(2);
		e1.addDestinationFloor(5, Direction.UP);
		e1.setMotorStatus(Motor.UP);

		// normal cases
		assertEquals(true, SchedulerUtil.isElevatorPassingByFloor(e1, 3, Direction.UP));
		assertEquals(true, SchedulerUtil.isElevatorPassingByFloor(e1, 4, Direction.UP));
		assertEquals(false, SchedulerUtil.isElevatorPassingByFloor(e1, 1, Direction.UP));
		assertEquals(false, SchedulerUtil.isElevatorPassingByFloor(e1, 6, Direction.UP));

		// edge cases
		assertEquals(false, SchedulerUtil.isElevatorPassingByFloor(e1, 2, Direction.UP));
		assertEquals(false, SchedulerUtil.isElevatorPassingByFloor(e1, 5, Direction.UP));
	}

	@Test
	public void testGetElevatorDistanceToTravelToFloor() {
		e1.setMotorStatus(Motor.UP);
		e1.setCurrentFloor(1);
		e1.addDestinationFloor(2, Direction.UP);
		e1.addDestinationFloor(7, Direction.UP);
		assertEquals(7, SchedulerUtil.getElevatorDistanceToTravelToFloor(e1, 8, Direction.UP));

		e1.clearDestinations();
		e1.setCurrentFloor(2);
		e1.addDestinationFloor(7, Direction.UP);
		e1.addDestinationFloor(4, Direction.UP);
		assertEquals(11, SchedulerUtil.getElevatorDistanceToTravelToFloor(e1, 1, Direction.UP));

		e1.clearDestinations();
		e1.setMotorStatus(Motor.DOWN);
		e1.setCurrentFloor(8);
		e1.addDestinationFloor(2, Direction.DOWN);
		e1.addDestinationFloor(7, Direction.DOWN);
		assertEquals(10, SchedulerUtil.getElevatorDistanceToTravelToFloor(e1, 6, Direction.UP));

		e1.clearDestinations();
		e1.setCurrentFloor(5);
		e1.addDestinationFloor(7, Direction.UP);
		e1.addDestinationFloor(4, Direction.DOWN);
		e1.addDestinationFloor(2, Direction.DOWN);
		e1.addDestinationFloor(10, Direction.UP);
		// elevator going down from 5 so order is 4 2 7 10 then 12
		assertEquals(13, SchedulerUtil.getElevatorDistanceToTravelToFloor(e1, 12, Direction.UP));

		e1.clearDestinations();
		e1.setMotorStatus(Motor.UP);
		e1.setCurrentFloor(5);
		e1.addDestinationFloor(7, Direction.UP);
		e1.addDestinationFloor(4, Direction.DOWN);
		e1.addDestinationFloor(2, Direction.DOWN);
		e1.addDestinationFloor(10, Direction.UP);
		// elevator going up from 5 so order is 7 10 4 2 then 12
		assertEquals(23, SchedulerUtil.getElevatorDistanceToTravelToFloor(e1, 12, Direction.DOWN));

		// justin tests
		e1.clearDestinations();
		e1.setMotorStatus(Motor.UP);
		e1.setCurrentFloor(1);
		e1.addDestinationFloor(2, Direction.UP);
		e1.addDestinationFloor(7, Direction.UP);
		e1.addDestinationFloor(6, Direction.DOWN);
		e1.addDestinationFloor(15, Direction.DOWN);
		e1.addDestinationFloor(5, Direction.UP);

		assertEquals(Arrays.asList(2, 5, 7), e1.getDestinations(Direction.UP));
		assertEquals(Arrays.asList(15, 6), e1.getDestinations(Direction.DOWN));
		assertEquals(8, SchedulerUtil.getElevatorDistanceToTravelToFloor(e1, 9, Direction.UP));
	}
}
