package com.agilex.healthcare.schedulingmanager.mdws.translator;


import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;

import com.agilex.healthcare.schedulingmanager.domain.AppointmentSlotFilter;
import com.agilex.healthcare.schedulingmanager.domain.AppointmentTimeSlot;
import com.agilex.healthcare.schedulingmanager.domain.AppointmentTimeSlots;
import com.agilex.healthcare.schedulingmanager.domain.BookAppointment;
import com.agilex.healthcare.schedulingmanager.mdws.MdwsResponseException;
import com.agilex.healthcare.schedulingmanager.mdws.generatedwsdl.schedulingservice.AppointmentTO;
import com.agilex.healthcare.schedulingmanager.mdws.generatedwsdl.schedulingservice.HospitalLocationTO;
import com.agilex.healthcare.schedulingmanager.mdws.generatedwsdl.schedulingservice.TaggedText;
import com.agilex.healthcare.schedulingmanager.mdws.generatedwsdl.schedulingservice.TimeSlotArray;
import com.agilex.healthcare.schedulingmanager.mdws.generatedwsdl.schedulingservice.TimeSlotTO;
import com.agilex.healthcare.utility.DateHelper;

public class AppointmentTimeSlotTranslator {

	public AppointmentTimeSlots translateFromHospitalLocationTO(HospitalLocationTO hospitalLocationTO, AppointmentSlotFilter appointmentSlotFilter){
		AppointmentTimeSlots retValue = new AppointmentTimeSlots();
		if (hospitalLocationTO == null)
			throw new MdwsResponseException(MdwsResponseException.UserMessages.INVALID_DATA_RETURNED, "HospitalLocationTO was null");
		if (hospitalLocationTO.getFault() != null)
			throw new MdwsResponseException(hospitalLocationTO.getFault());
		
		String clinicId = appointmentSlotFilter.getClinicId();
		String name = hospitalLocationTO.getName();
		String appointmentLength = hospitalLocationTO.getAppointmentLength();
		String clinicDisplayStartTime = hospitalLocationTO.getClinicDisplayStartTime();
		String displayIncrements = hospitalLocationTO.getDisplayIncrements();
		boolean askForCheckIn = hospitalLocationTO.isAskForCheckIn();
		String maxOverbooksPerDay = hospitalLocationTO.getMaxOverbooksPerDay();
		
		setAppointmentTimeSlots(retValue, hospitalLocationTO, appointmentSlotFilter);
		
		retValue.setClinicId(clinicId);
		retValue.setClinicName(name);
		retValue.setAppointmentLength(appointmentLength);
		retValue.setClinicDisplayStartTime(clinicDisplayStartTime);
		retValue.setDisplayIncrements(displayIncrements);
		retValue.setAskForCheckIn(askForCheckIn);
		retValue.setMaxOverbooksPerDay(maxOverbooksPerDay);

		TaggedText stopCodeTag = hospitalLocationTO.getStopCode();
		TaggedText creditStopCodeTag = hospitalLocationTO.getCreditStopCode();
		TaggedText specialtyTag = hospitalLocationTO.getSpecialty();
		if (stopCodeTag != null) {
			//Perform Error Checking
			if (stopCodeTag.getFault() != null)
				throw new MdwsResponseException(stopCodeTag.getFault());
			String stopCode = stopCodeTag.getTag();
			retValue.setStopCode(stopCode);
		}
		if (creditStopCodeTag != null) {
			//Perform Error Checking
			if (creditStopCodeTag.getFault() != null)
				throw new MdwsResponseException(creditStopCodeTag.getFault());
			String creditStopCode = creditStopCodeTag.getTag();
			retValue.setCreditCode(creditStopCode);
		}
		if (specialtyTag != null) {
			//Perform Error Checking
			if (specialtyTag.getFault() != null)
				throw new MdwsResponseException(specialtyTag.getFault());
			String specialty = specialtyTag.getTag();
			retValue.setSpecialty(specialty);
		}

		calculateRemainingOverbookingsPerDay(retValue);

		return retValue;
	}
	
	private void setAppointmentTimeSlots(AppointmentTimeSlots appointmentTimeSlots, HospitalLocationTO hospitalLocationTO, AppointmentSlotFilter appointmentSlotFilter) {
		TimeSlotArray availability = hospitalLocationTO.getAvailability();
		if (availability == null)
			return;
		//Perform Error Checking
		if (availability.getFault() != null)
			throw new MdwsResponseException(availability.getFault());
		if (availability.getSlots() == null || availability.getSlots().getTimeSlotTO() == null)
			return;

		Iterator<TimeSlotTO> itTimeSlotTO = availability.getSlots().getTimeSlotTO().iterator();
		while (itTimeSlotTO.hasNext()) {
			TimeSlotTO timeSlotTO = itTimeSlotTO.next();
			//Perform Error Checking
			if (timeSlotTO.getFault() != null)
				throw new MdwsResponseException(timeSlotTO.getFault());

			Date startDate = DateHelper.toDate(timeSlotTO.getStart());
			Date endDate = DateHelper.toDate(timeSlotTO.getEnd());
			Date filterStartDate = appointmentSlotFilter.getStartDate();
			Date filterEndDate = appointmentSlotFilter.getEndDate();
			
			if(isTimeSlotWithinSpecifiedDateRange(startDate, endDate,filterStartDate, filterEndDate)) {
//				if(timeSlotTO.isAvailable()){
					String text = timeSlotTO.getText();
					boolean available = timeSlotTO.isAvailable();
					
					AppointmentTimeSlot	   appointmentTimeSlot = new AppointmentTimeSlot();
					appointmentTimeSlot.setStartDateTime(startDate);
					appointmentTimeSlot.setEndDateTime(endDate);
					appointmentTimeSlot.setBookingStatus(text);
					appointmentTimeSlot.setAvailability(available);
					appointmentTimeSlots.add(appointmentTimeSlot);
//				}
			}
		}
	}

	private boolean isTimeSlotWithinSpecifiedDateRange(Date startDate, Date endDate, Date desiredStartDate, Date desiredEndDate) {
		return (startDate.equals(desiredStartDate) || startDate.after(desiredStartDate)) && (endDate.equals(desiredEndDate) || endDate.before(desiredEndDate));
	}

	protected void calculateRemainingOverbookingsPerDay(AppointmentTimeSlots appointmentTimeSlots) {
		int remainingOverBookings;
		HashMap<String, Integer> dayMap = new HashMap<String, Integer>();
		SimpleDateFormat df = new SimpleDateFormat("MM/dd/yyyy");

		//
		// What we are doing is taking the maximum allowed over bookings per day for a clinic
		// and calculating the remaining allowed over bookings by looping through all the time slots,
		// adding up the current number of over bookings and subtracting that number from the maximum
		// allowed over bookings to determine the remaining allowed over bookings.
		//

		//
		// Calculate the number of remaining allowed over bookings for each time slot by day.
		//
		int maximumAllowedOverBookings = Integer.parseInt(appointmentTimeSlots.getMaxOverbooksPerDay());
		Iterator<AppointmentTimeSlot> timeSlotIterator = appointmentTimeSlots.iterator();
		while(timeSlotIterator.hasNext()) {
			AppointmentTimeSlot timeSlot = timeSlotIterator.next();

			String date = df.format(timeSlot.getStartDateTime());

			if(dayMap.containsKey(date) == false) {
				remainingOverBookings = new Integer(maximumAllowedOverBookings);
				dayMap.put(date, remainingOverBookings);
			}
			else {
				remainingOverBookings = dayMap.get(date).intValue();
			}

			remainingOverBookings -= calculateOverbookings(timeSlot);
			if (remainingOverBookings < 0)
				remainingOverBookings = 0;
			
			dayMap.put(date, new Integer(remainingOverBookings));
		}

		//
		// Set the remaining allowed over bookings for the day based on what
		// was calculated above.
		//
		timeSlotIterator = appointmentTimeSlots.iterator();
		while(timeSlotIterator.hasNext()) {
			AppointmentTimeSlot timeSlot = timeSlotIterator.next();

			String date = df.format(timeSlot.getStartDateTime());

			String remainingAllowed = dayMap.get(date).toString();
			timeSlot.setRemainingAllowedOverBookings(remainingAllowed);
		}


	}

	private int calculateOverbookings(AppointmentTimeSlot  timeSlot) {
		String	  textFieldValue;
		char		c;

		textFieldValue = timeSlot.getBookingStatus();

		if(textFieldValue.length() == 1) {
			c = textFieldValue.charAt(0);
			//
			// "X" means "Don't book anything in this time slot, including overbookings.
			//
			if( c >= 'A' && c < 'X') {
				//
				// An "A" means that there was one overbooking.  "A" is 65 in ascii, so if we
				// subtract 64 from the character value, we will get the number of overbookings
				// booked for this slot.
				//
				return c - 64;
			}
		}

		return 0;
	}
	
//----------------------------------------------------------------------------------------------------------------------

	public BookAppointment translate(AppointmentTO appointmentTO) {
		BookAppointment retValue = new BookAppointment();
		if (appointmentTO == null)
			throw new MdwsResponseException(MdwsResponseException.UserMessages.INVALID_DATA_RETURNED, "AppointmentTO was null");
		if (appointmentTO.getFault() != null)
			throw new MdwsResponseException(appointmentTO.getFault());
		
		HospitalLocationTO clinic = appointmentTO.getClinic();
		String timestamp = appointmentTO.getTimestamp();
		Date dtDateTime = DateHelper.parseVistaDateTime(timestamp);
		String purpose = appointmentTO.getPurpose();
		
		if (clinic != null) {
			//Perform Error Checking
			if (clinic.getFault() != null)
				throw new MdwsResponseException(clinic.getFault());
			String id = clinic.getId();
			retValue.setClinicId(id);
		}
				
		retValue.setDateTime(dtDateTime);
		retValue.setPurpose(purpose);
		return retValue;
	}
}
