package scheduler;

import java.io.IOException;
import java.util.logging.Logger;

import common.AbstractReceiver;
import common.LoggerUtil;
import communication.MessageUtil;
import communication.Port;
import communication.messages.ElevatorMoveMessage;
import communication.messages.ElevatorRequestMessage;
import communication.messages.ElevatorSensorMessage;
import communication.messages.ForceActionConfirmedMessage;
import communication.messages.ConfirmInitializeMessage;
import communication.messages.ElevatorErrorOccuredMessage;
import communication.messages.Message;
import error.ExceptionUtil;
import error.UnexpectedMessageTypeException;

/**
 * This class receives messages from the floor subsystem and creates
 * SchedulerTask objects to append to the Scheduler
 */
public class SchedulerReceiver extends AbstractReceiver {
	private static final Logger logger = LoggerUtil.getLoggerForClass(SchedulerReceiver.class);
	private final Scheduler scheduler;

	/**
	 * Create a scheduler receiver with specified number of floors and elevators
	 *
	 * @throws IOException
	 */
	public SchedulerReceiver() throws IOException {
		super(Port.SCHEDULER);
		scheduler = Scheduler.getInstance();
		scheduler.initializeSubsystems();

		// make the scheduler execute on a separate thread
		new Thread(scheduler).start();
	}

	@Override
	public synchronized void handleMessage(Message msg) {
		if (msg instanceof ElevatorRequestMessage) {
			scheduler.addTask(new ElevatorDispatchTask((ElevatorRequestMessage) msg));
		} else if (msg instanceof ElevatorMoveMessage) {
			scheduler.addTask(new ElevatorDispatchTask((ElevatorMoveMessage) msg));
		} else if (msg instanceof ElevatorSensorMessage) {
			scheduler.updateMetaElevatorPosition((ElevatorSensorMessage) msg);
			scheduler.addTask(new ElevatorMovedTask((ElevatorSensorMessage) msg));
		} else if (msg instanceof ElevatorErrorOccuredMessage) {
			logger.severe("Error has occured in the system: " + msg);
			scheduler.handleElevatorDoorError((ElevatorErrorOccuredMessage) msg);
		} else if (msg instanceof ForceActionConfirmedMessage) {
			ForceActionConfirmedMessage actionConfirmedMsg = (ForceActionConfirmedMessage) msg;
			logger.info("Elevator " + actionConfirmedMsg.getElevatorId() + " is back up for service!");
			scheduler.handleForcedDoorsActionConfirmed(actionConfirmedMsg);
		} else if (msg instanceof ConfirmInitializeMessage) {
			scheduler.handleConfirmInitialize((ConfirmInitializeMessage) msg);
		} else {
			ExceptionUtil.handleRecoverableException(new UnexpectedMessageTypeException(),
					"Received a message not meant for this subsystem", logger);
		}
	}

	public static void main(String[] args) {
		MessageUtil.setIPAddress(Port.FLOOR, args, 0);
		MessageUtil.setIPAddress(Port.ELEVATOR, args, 1);

		try {
			new SchedulerReceiver().receiveAndHandlePackets(logger);
		} catch (IOException e) {
			ExceptionUtil.handleCriticalException(e, "An IO Exception occured while operating scheduler receiver",
					logger);
		}
	}
}
