package gov.va.med.cds.request.vista.write;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.dom4j.Element;

import gov.va.med.cds.common.person.correlation.PersonIdentifier;
import gov.va.med.cds.common.person.correlation.PersonIdentifierInterface;
import gov.va.med.cds.exception.ErrorCodeEnum;
import gov.va.med.cds.filter.EntryFilterInterface;
import gov.va.med.cds.filter.ParameterMapInterface;
import gov.va.med.cds.filter.QueryParameter;
import gov.va.med.cds.request.ValidationException;

public class CdsWriteData implements EntryFilterInterface {

	private Long timeout = new Long(600);
	private boolean isPatientCentricFilter = true;
	private String facilityId = null;
	private String templateId = null;
	private String requestId = null;
	private String domainEntryPoint = null;
	private Element domainEntryPointElement = null;
	List<PersonIdentifierInterface> personIdentifiers = null;
	private final static String XS_DATE_FORMAT_PATTERN = "yyyy-MM-dd";
	private Map<String, Object> dataMap;

	public CdsWriteData(Element domainEntryPointElement, String templateId, String requestId, String domainEntryPoint,
			List<PersonIdentifierInterface> personIdentifiers) {
		this.templateId = templateId;
		this.domainEntryPoint = domainEntryPoint;
		this.requestId = requestId;
		this.domainEntryPointElement = domainEntryPointElement;
		this.personIdentifiers = personIdentifiers;
		extractAdditionalParameters();
	}

	public Map<String, Object> getDataMap() {
		return dataMap;
	}

	@SuppressWarnings("unchecked")

	protected void extractAdditionalParameters() {
		List<Element> dataList = domainEntryPointElement.elements();
		String name = null;
		String value = null;
		dataMap = new HashMap<String, Object>();
		for (Element data : dataList) {
			if (data.getName().equals("patient")) {
				PersonIdentifierInterface patientIdentifier = new PersonIdentifier();
				Element identitifier = data.element("identifier");

				patientIdentifier.setAssigningAuthority(identitifier.element("assigningAuthority").getTextTrim());
				if (patientIdentifier.getAssigningAuthority() == null
						|| patientIdentifier.getAssigningAuthority().isEmpty()) {
					throw new ValidationException(ErrorCodeEnum.PATIENT_ID_MISSING_ASSIGNING_AUTHORITY);
				}
				patientIdentifier.setAssigningFacility(identitifier.element("assigningFacility").getTextTrim());
				if (patientIdentifier.getAssigningFacility() == null
						|| patientIdentifier.getAssigningFacility().isEmpty()) {
					throw new ValidationException(ErrorCodeEnum.PATIENT_ID_MISSING_ASSIGNING_FACILITY);
				}
				patientIdentifier.setIdentity(identitifier.element("identity").getTextTrim());
				if (patientIdentifier.getIdentity() == null || patientIdentifier.getIdentity().isEmpty()) {
					throw new ValidationException(ErrorCodeEnum.PATIENT_ID_MISSING_IDENTITY);
				}
				dataMap.put("pidInfo", patientIdentifier);
			} else {
				name = data.getName();
				value = data.getTextTrim();
				dataMap.put(name, value);
			}
		}
	}

	@Override
	public boolean isPatientCentricFilter() {
		return this.isPatientCentricFilter;
	}

	public EntryFilterInterface getEntryFilter(String queryName) {
		return null;
	}

	@Override
	public boolean isSiteCentricFilter() {
		boolean siteCentric = false;
		if (!isPatientCentricFilter()) {
			siteCentric = (facilityId != null) ? true : false;
		}
		return siteCentric;
	}

	public Calendar getStartDate() {
		return null;
	}

	/**
	 * Gets the value of the endDate property.
	 * 
	 * @return possible object is {@link Calendar }
	 * 
	 */
	public Calendar getEndDate() {
		return null;
	}

	public List<String> getRecordIdentifiers() {
		return null;
	}

	/**
	 * Gets the value of the xpathQuery property.
	 * 
	 * @return possible object is {@link XpathQuery }
	 * 
	 */
	public String getXpathQuery() {
		return null;
	}

	public String getJsonPathQuery() {
		return null;
	}

	/**
	 * Gets the value of the queryName property.
	 * 
	 * @return possible object is {@link String }
	 * 
	 */
	public String getQueryName() {
		return null;
	}

	/**
	 * Return an indicator to determine whether to use dates in the query
	 * 
	 * @return
	 */
	public boolean useDates() {
		return false;
	}

	/**
	 * Return the time out for the filter in seconds
	 * 
	 * @return
	 */
	public Long getTimeout() {
		return this.timeout;
	}

	public String getFacilityId() {
		return null;
	}

	public String getXpathFilterExpression() {
		return null;
	}

	public String getXpathExtractExpression() {
		return null;
	}

	/**
	 * @return True if entry filter is to return the count of records only;
	 *         otherwise false.
	 */
	public boolean isCountOnly() {
		return false;
	}

	/**
	 * Gets the number of the page specified by the client, if a paging filter
	 * has been specified by the client.
	 * 
	 * @return The page number from the filter.
	 */
	public int getPageNumber() {
		return 0;
	}

	/**
	 * Gets the page size (records per page) specified by the client, if a
	 * paging filter has been specified by the client.
	 * 
	 * @return The number of records per page.
	 */
	public int getPageSize() {
		return 0;
	}

	/**
	 * @return Returns true if this filter is a paging query; otherwise returns
	 *         false.
	 */
	public boolean isPagingFilter() {
		return false;
	}

	/**
	 * @returns True if the filter contains record identifiers to query by;
	 *          otherwise false.
	 */
	public boolean hasRecordIdentifiers() {
		return false;
	}

	/**
	 * @return True if the filter contains an XPath filter expression; otherwise
	 *         false.
	 */
	public boolean containsXpathFilterExpression() {
		return false;
	}

	/**
	 * @return Gets name space of the extract expression if one was provided in
	 *         the filter.
	 */
	public String getXpathExtractExpressionNamespace() {
		return null;
	}

	/**
	 * @return Gets name space of the filter expression if one was provided in
	 *         the filter.
	 */
	public String getXpathFilterExpressionNamespace() {
		return null;
	}

	private QueryParameter<Map<String, Calendar>> extractDateRangeParameter(Element additionalParameter) {
		QueryParameter<Map<String, Calendar>> queryParameter = null;
		@SuppressWarnings("unchecked")
		List<Element> elements = (List<Element>) additionalParameter.elements();

		if (elements != null && elements.size() > 0 && elements.size() < 3) {
			Element beginDateElement = elements.get(0);
			Element endDateElement = elements.get(1);
			Map<String, Calendar> valueMap = new HashMap<String, Calendar>();
			queryParameter = new QueryParameter<Map<String, Calendar>>();
			queryParameter.setName(additionalParameter.attributeValue("name"));
			queryParameter.setValue(valueMap);

			Calendar c = null;

			if (beginDateElement != null) {
				c = extractCalendar(beginDateElement, -1);
				valueMap.put("begin", c);
			}

			if (endDateElement != null) {
				c = extractCalendar(endDateElement, 1);
				valueMap.put("end", c);
			}
		}

		return queryParameter;
	}

	private Calendar extractCalendar(Element dateElement, int dayOffset) {
		DateFormat xmlDateFormat = new SimpleDateFormat(XS_DATE_FORMAT_PATTERN);
		Calendar c = null;

		if (dateElement != null) {
			String dateString = dateElement.getTextTrim();

			c = extractDate(dateString, xmlDateFormat);
			c.set(Calendar.HOUR_OF_DAY, 0);
			c.set(Calendar.MINUTE, 0);
			c.set(Calendar.SECOND, 0);
			c.add(Calendar.DATE, dayOffset);
		}

		return c;
	}

	private Calendar extractDate(String dateString, DateFormat dateFormatter) {
		try {
			Date dateFromString = dateFormatter.parse(dateString);
			Calendar cal = new GregorianCalendar();
			cal.setTime(dateFromString);
			return cal;
		} catch (ParseException e) {
			throw new ValidationException(ErrorCodeEnum.CANNOT_PARSE_DATE, dateString);
		}
	}

	private QueryParameter<List<String>> extractListTypeParameter(List<Element> valueElements) {
		List<String> values = new ArrayList<String>();
		QueryParameter<List<String>> queryParameter = new QueryParameter<List<String>>();
		queryParameter.setValue(values);

		for (Element valueElement : valueElements) {
			values.add(valueElement.getTextTrim());
		}
		return queryParameter;
	}

	public Set<String> getFilterParameterNames() {
		Set<String> parameterNames = new HashSet<String>();
		if (dataMap != null && dataMap.size() > 0) {
			parameterNames.addAll(dataMap.keySet());
		}
		return parameterNames;
	}

	public Object getParameterValue(String parameterName) {
		return this.dataMap.get(parameterName);
	}

	public List<PersonIdentifierInterface> getPersonIdentifiers() {
		List<PersonIdentifierInterface> pidsList = new ArrayList();
		pidsList.add((PersonIdentifierInterface) this.dataMap.get("pidInfo"));
		return pidsList;
	}

	public String getDomainEntryPoint() {
		return domainEntryPoint;
	}

	public String getRequestId() {
		return requestId;
	}

	/**
	 * Gets the template identifier for the filter request.
	 * 
	 * @return The template identifier.
	 */
	public String getTemplateId() {
		return templateId;
	}

	public ParameterMapInterface getAdditionalParametersMap() {
		return null;
	}
}
