package smash.appointment.parse;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import org.apache.log4j.Logger;

public class RedcapCalendarParser {
	Logger						 logger				 = Logger.getLogger(RedcapCalendarParser.class);

	private SubjectDao subjectDao;

	DateTimeFormatter	 formatter		 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
	SimpleDateFormat	 dateFormatter = new SimpleDateFormat("yyyy-MM-dd");

	public List<AppointmentEntry> parse(String filename, Calendar minDate) throws FileNotFoundException, IOException {
		List<AppointmentEntry> result = new ArrayList<>();
		try (BufferedReader br = new BufferedReader(new FileReader(filename))) {
			String line;
			while ((line = br.readLine()) != null) {
				if (!line.startsWith("INSERT INTO")) {
					continue;
				}
				String tmp[] = line.substring(line.indexOf("(")).split("\\),\\(", -1);
				for (String string : tmp) {
					AppointmentEntry entry = processEntry(string, minDate);
					if (entry != null) {
						result.add(entry);
					}
				}
			}
		}
		return result;
	}

	private AppointmentEntry processEntry(String string, Calendar minDate) {
		AppointmentEntry result = new AppointmentEntry();
		if (string.startsWith("(")) {
			string = string.substring(1);
		}
		if (string.endsWith(")")) {
			string = string.substring(0, string.length() - 1);
		}
		string = string.replaceAll("\\\\'", "__quota__");
		string = string.replaceAll("'", "\"");
		string = string.replaceAll("__quota__", "'");
		String fields[] = string.split(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)", -1);
		String ndNumber = fields[1].replaceAll("\"", "");
		String day = fields[6].replaceAll("\"", "");
		String time = fields[7].replaceAll("\"", "");

		String query = fields[10].replaceAll("\"", "");

		if (query.equals("") || query.equals("NULL")) {
			return null;
		}

		if (minDate != null) {
			if (day.compareTo(dateFormatter.format(minDate.getTime())) < 0) {
				return null;
			}
		}
		if (ndNumber.equals("NDtest_internal") || ndNumber.equals("NDtest_external")) {
			return null;
		}

		Subject subject = null;
		if (!ndNumber.equalsIgnoreCase("NULL")) {
			subject = subjectDao.getByNdNumber(ndNumber);
			if (subject == null) {
				logger.warn("Cannot find subject with nd number: " + ndNumber);
			}
		}
		if (subject == null) {
			subject = findSubject(query);
		}
		if (subject != null && !subject.getToBeSeenAt().toLowerCase().startsWith("l")) {
			return null;
		}
		result.setLocation("LIH");
		result.setDay(day);
		result.setTime(time);
		result.setSource("From redcap: " + query);
		result.setSubject(subject);
		result.addTypes(getTypes(query));
		if (result.getTypes().contains(AppointmentType.OTHER)) {
			logger.warn("Cannot find types for: " + query);
		}
		if (result.getTypes().contains(AppointmentType.LEVEL_ASAMP)) {
			return result;
		} else {
			logger.debug("Skipping LIH appointment: " + query);
			return null;
		}
	}

	CellParser cellParser = new CellParser();

	private List<AppointmentType> getTypes(String query) {
		List<AppointmentType> result = new ArrayList<>();
		AppointmentTypeCollection collection = cellParser.extractType(query);
		if (collection == null) {
			int index = query.indexOf("_");
			if (index >= 0) {
				query = query.substring(index);
				if (query.startsWith("_lev a_")) {
					collection = AppointmentTypeCollection.LEVEL_A;
				} else {
					index = query.indexOf("_", 1);
					if (index >= 0) {
						query = query.substring(index);
						if (query.startsWith("_lev a_")) {
							collection = AppointmentTypeCollection.LEVEL_A;
						}
					}
				}
			}
			if (collection == null) {
				collection = AppointmentTypeCollection.OTHER;
			}
		}
		for (AppointmentType appointmentType : collection.getTypes()) {
			result.add(appointmentType);
		}

		return result;
	}

	private Subject findSubject(String query) {
		String id = query.split("_")[0];
		id = "L-" + id;
		Subject result = subjectDao.getByScreeningNumber(id);
		if (result == null) {
			logger.warn("Cannot find subject for query: " + query);
		}
		return result;
	}

	/**
	 * @return the subjectDao
	 * @see #subjectDao
	 */
	public SubjectDao getSubjectDao() {
		return subjectDao;
	}

	/**
	 * @param subjectDao
	 *          the subjectDao to set
	 * @see #subjectDao
	 */
	public void setSubjectDao(SubjectDao subjectDao) {
		this.subjectDao = subjectDao;
	}
}