package floor.input;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Logger;

import common.Direction;
import common.ErrorType;
import common.LoggerUtil;
import error.ExceptionUtil;
import error.InputFormatException;
import error.UnknownAddressException;
import floor.FloorSubsystem;

/**
 * Responsible for parsing inputs and directing them to the floorsubsystem when
 * they occur
 */
public class InputParser {
	private static final Logger logger = LoggerUtil.getLoggerForClass(InputParser.class);
	private static final String INPUT_FILE_LOCATION = "data/floorEvents";
	private static final int INPUT_SEGMENT_COUNT = 6;
	private static FloorSubsystem floorSubsystem;

	private InputParser() {
		throw new UnsupportedOperationException("Utility class");
	}

	/**
	 * Begin reading the input file into the system and setup the events
	 *
	 * @param floorSubsystem the consumer of the events
	 * @throws InputFormatException if an exception occurs while parsing the events
	 * @throws IOException          if an I/O error occurs Ex.the config file cannot
	 *                              be found
	 */
	public static void setupInputs(FloorSubsystem floorSubsystem) throws IOException, InputFormatException {
		InputParser.floorSubsystem = floorSubsystem;

		try (BufferedReader br = new BufferedReader(new FileReader(new File(INPUT_FILE_LOCATION)))) {
			parseInputEvents(br).forEach(InputParser::setupInputEvent);
		}
	}

	private static List<InputEvent> parseInputEvents(BufferedReader br) throws IOException, InputFormatException {
		List<InputEvent> inputEvents = new ArrayList<>();
		String st;

		while ((st = br.readLine()) != null) {
			String[] inputParts = st.split(",");

			if (inputParts.length != INPUT_SEGMENT_COUNT) {
				throw new InputFormatException("Invalid input string format " + Arrays.toString(inputParts));
			}

			logger.config("Will be simulating input event: " + Arrays.toString(inputParts));

			inputEvents.add(constructInputEvent(inputParts));
		}

		return inputEvents;
	}

	private static InputEvent constructInputEvent(String[] inputParts) {
		return new InputEvent(Long.valueOf(inputParts[0]), Integer.valueOf(inputParts[1]),
				Direction.getDirection(inputParts[2]), Integer.valueOf(inputParts[3]),
				ErrorType.getErrorForValue(Integer.valueOf(inputParts[4])), Integer.valueOf(inputParts[5]));
	}

	private static void setupInputEvent(InputEvent event) {
		new Thread(() -> {
			try {
				Thread.sleep(event.getInputTimeMs());
			} catch (InterruptedException e) {
				ExceptionUtil.handleRecoverableException(e, "Thread was interrupted while waiting to simulate event",
						logger);
				Thread.currentThread().interrupt();
			}
			try {
				floorSubsystem.handleInputEvent(event);
			} catch (IOException | UnknownAddressException e) {
				ExceptionUtil.handleCriticalException(e, "Could not send message to scheduler subsystem", logger);
			}
		}).start();
	}
}
