package floor;

import java.io.IOException;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import common.LoggerUtil;
import communication.MessageSender;
import communication.Port;
import communication.messages.ConfirmInitializeMessage;
import communication.messages.ElevatorRequestMessage;
import communication.messages.FloorArrivalMessage;
import communication.messages.InitializeMessage;
import communication.messages.ConfirmInitializeMessage.Subsystem;
import error.ExceptionUtil;
import error.UnknownAddressException;
import floor.input.InputEvent;

/**
 * Central floor subsystem. Handles sending messages and inputs to the proper
 * floors
 */
public class FloorSubsystem {
	private static final Logger logger = LoggerUtil.getLoggerForClass(FloorSubsystem.class);
	private List<Floor> floorList;
	private MessageSender sender;

	/**
	 * Construct a floor subsystem
	 */
	public FloorSubsystem() {
		try {
			sender = new MessageSender();
		} catch (SocketException e) {
			ExceptionUtil.handleCriticalException(e, "Could not create sending socket", logger);
		}
	}

	/**
	 * Set up the system
	 *
	 * @param msg initialize message containing number of floors
	 */
	public void initializeSystem(InitializeMessage msg) {
		floorList = new ArrayList<>();
		for (int i = 0; i < msg.getNumFloors(); i++) {
			floorList.add(new Floor(i + 1));
		}

		sendConfirmInitializeMessage();
	}

	private void sendConfirmInitializeMessage() {
		try {
			sender.sendMessage(new ConfirmInitializeMessage(Subsystem.FLOOR),
					Port.SCHEDULER);
		} catch (IOException | UnknownAddressException e) {
			ExceptionUtil.handleRecoverableException(e, "Could not send confirm initialize message to scheduler",
					logger);
		}
	}

	/**
	 * Handles input event
	 *
	 * @param inputEvent
	 * @throws IOException
	 * @throws UnknownAddressException
	 */
	public void handleInputEvent(InputEvent inputEvent) throws IOException, UnknownAddressException {
		logger.info("Received input: " + inputEvent);
		Floor floor = getFloor(inputEvent.getInputFloor());
		floor.handleInputEvent(inputEvent);
		sender.sendMessage(new ElevatorRequestMessage(inputEvent), Port.SCHEDULER);
		printFloor(floor);
	}

	/**
	 * Handle the receipt of a floor arrival message. Perform the the necessary
	 * floor operations
	 *
	 * @param message
	 */
	public synchronized void handleMessage(FloorArrivalMessage message) {
		Floor floor = getFloor(message.getFloorNumber());
		floor.handleFloorMessage(message);
		printFloor(floor);
	}

	private void printFloor(Floor floor) {
		logger.info(floor.toString());
	}

	private Floor getFloor(int inputFloor) {
		return floorList.get(inputFloor - 1);
	}

	protected List<Floor> getFloorList() {
		return floorList;
	}
}
