package lu.tudor.santec.gecamed.esante.utils;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import lu.tudor.santec.gecamed.esante.ejb.entity.beans.CdaDocument;
import lu.tudor.santec.gecamed.esante.ejb.entity.beans.ESanteProperty;
import lu.tudor.santec.gecamed.esante.ejb.session.beans.ESanteConfigManagerBean;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

/**
 * @author jens.ferring(at)tudor.lu
 * 
 * @version
 * <br>$Log: ESanteUtils.java,v $
 */

public class ESanteUtils
{
	/* ======================================== */
	// CONSTANTS
	/* ======================================== */
	
	
	public static final int	CDA_DOWNLOAD_SITE_SERVER = 1;
	
	public static final int	CDA_DOWNLOAD_SITE_CLIENT = 2;
	
	
	
	/* ======================================== */
	// MEMBERS
	/* ======================================== */
	
	private static int			cdaDownloadSite	= -1;
	
	private static XPathFactory	factory				= null;
	
	private static XPath		xpath				= null;
	
	
	
	/* ======================================== */
	// CLASS BODY
	/* ======================================== */
	
	/**
	 * @param eHealthId The eHealth ID to get the short version of
	 * @return The eHealth ID without checksum or the given eHealth ID, it is shorter than 10 chars. 
	 */
	public static String getShortEHealthId (String eHealthId)
	{
			return eHealthId.substring(0, eHealthId.length()-2);
	}
	
	
	public static boolean containsDownloadableDocument (Collection<CdaDocument> documents)
	{
		if (documents == null)
			return false;
		
		for (CdaDocument document : documents)
		{
			if (!document.isDownloaded())
				return true;
		}
		
		return false;
	}
	
	
	public static boolean containsIntegratableDocument (Collection<CdaDocument> documents)
	{
		if (documents == null)
			return false;
		
		for (CdaDocument document : documents)
		{
			if (document.canBeIntegrated())
				return true;
		}
		
		return false;
	}
	
	
	public static boolean containsDisintegratableDocument (Collection<CdaDocument> documents)
	{
		if (documents == null)
			return false;
		
		for (CdaDocument document : documents)
		{
			if (document.canBeDisintegrated())
				return true;
		}
		
		return false;
	}
	
	
	public static boolean containsDeletableDocument (Collection<CdaDocument> documents)
	{
		if (documents == null)
			return false;
		
		for (CdaDocument document : documents)
		{
			if (document.isDownloaded() && document.getIncidentEntryId() == null)
				return true;
		}
		
		return false;
	}
	
	
	public static int getCdaDownloadSite ()
	{
		if (cdaDownloadSite < 0)
		{
			String site = ESanteConfigManagerBean.getInstance()
					.getESantePropertyValue(ESanteProperty.PROP_CDA_DOWNLOAD_SITE);
			
			if (site == null || site.toLowerCase().equals(ESanteProperty.DOWNLOAD_SITE_CLIENT))
				cdaDownloadSite = CDA_DOWNLOAD_SITE_CLIENT;
			else
				cdaDownloadSite = CDA_DOWNLOAD_SITE_SERVER;
		}
		
		return cdaDownloadSite;
	}
	
	
	public static void resetCdaDownloadSite ()
	{
		cdaDownloadSite = -1;
	}
	
	
	public static Object parseXPath (String xpathExpression, String xmldata, Boolean returnAsString) 
			throws XPathExpressionException, SAXException, IOException, ParserConfigurationException
	{
		Document xmldoc;
		try {
			xmldoc = DocumentBuilderFactory
					.newInstance()
					.newDocumentBuilder()
					.parse(new InputSource(new ByteArrayInputStream(xmldata
							.getBytes("utf-8"))));
		} catch (SAXParseException saxe) {
			throw new SAXException("The response is no valid xml:\n"+saxe.getMessage(), saxe);
		}
		return parseXPath(xpathExpression, xmldoc, returnAsString);
	}
	
	
	public static Object parseXPath (String xpathExpression, Node xmldoc, Boolean returnAsString) 
			throws XPathExpressionException, SAXException, IOException, ParserConfigurationException
	{
		if (factory == null)
		{
			factory = XPathFactory.newInstance();
			xpath = factory.newXPath();
		}
		
		XPathExpression expr = xpath.compile(xpathExpression);
		if (returnAsString)
		{
			return expr.evaluate(xmldoc, XPathConstants.STRING);
		}
		else
		{
			return expr.evaluate(xmldoc, XPathConstants.NODESET);
		}
	}
	
	
	/**
	 * @param dateString The date string in the format yyyyMMddHHmmssSSS
	 * @return The date represented by the string.
	 */
	public static Date parseDate (String dateString)
	{
		int	digits				= dateString.length();
		int	offsetMultiplicator	= 1;
		int timeZoneIndex;
		int start;
		int end;
		int field;
		int value;
		
		
		if (dateString.contains("-"))
		{
			timeZoneIndex = dateString.indexOf('-');
		}
		else if (dateString.contains("+"))
		{
			timeZoneIndex		= dateString.indexOf('+');
			offsetMultiplicator	= -1;
		}
		else
		{
			timeZoneIndex = digits;
		}
		
		int[][] DATE_FORMAT_SETTINGS = new int [][] 
		{ 
			{ Calendar.YEAR,		 0,  4 },
			{ Calendar.MONTH,		 4,  6 },
			{ Calendar.DAY_OF_MONTH, 6,  8 },
			{ Calendar.HOUR_OF_DAY,	 8, 10 },
			{ Calendar.MINUTE,		10, 12 },
			{ Calendar.SECOND,		12, 14 },
			{ Calendar.MILLISECOND,	14, 17 }
		};
		
		Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
		for (int index = 0; index < DATE_FORMAT_SETTINGS.length; index++)
		{
			field	= DATE_FORMAT_SETTINGS[index][0];
			start	= DATE_FORMAT_SETTINGS[index][1];
			end		= DATE_FORMAT_SETTINGS[index][2];
			
			if (end > digits || end > timeZoneIndex)
				break;
			
			value = Integer.parseInt(dateString.substring(start, end));
			
			if (field == Calendar.MONTH)
				value--;
			
			cal.set(field, value);
		}
		
		if (timeZoneIndex < digits - 1)
		{
			String	offset = dateString.substring(timeZoneIndex+1);
			
			if (offset.length() >= 2)
			{
				cal.add(Calendar.HOUR_OF_DAY, offsetMultiplicator * Integer.parseInt(offset.substring(0, 2)));
				
				if (offset.length() >= 4)
					cal.add(Calendar.MINUTE, offsetMultiplicator * Integer.parseInt(offset.substring(2, 4)));
			}
		}
		
		return cal.getTime();
	}
}
