package gov.va.vinci.dart.common;

import gov.va.vinci.common.RequestBase;
import gov.va.vinci.common.ResponseBase;
import gov.va.vinci.common.ResponseException;

import java.io.Serializable;
import java.util.GregorianCalendar;
import java.util.TimeZone;

import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;

import org.springframework.web.context.support.SpringBeanAutowiringSupport;

@SuppressWarnings("serial")
public abstract class ServiceEndpoint extends SpringBeanAutowiringSupport implements Serializable {

	public static final boolean RESPONSE_STATUS_OK = true;
	public static final boolean RESPONSE_STATUS_EXCEPTION = false;
	
	private static final TimeZone GMT_TIMEZONE = TimeZone.getTimeZone("GMT");
	
	protected DatatypeFactory datatypeFactory;
	private MessageIdGenerator messageIdGenerator;
	protected String requestingUserId;
	
	protected ServiceEndpoint() throws DatatypeConfigurationException {
		datatypeFactory = DatatypeFactory.newInstance();
		messageIdGenerator = new MessageIdGenerator(this);
	} 
	
	protected void setException(final ResponseBase response, final Exception e) {     		
		
		ResponseException re = new ResponseException();
		re.setResponseExceptionCode(e.getClass().getName());
		re.setResponseExceptionText(e.getMessage());
		response.getResponseException().add(re);
		response.setResponseStatus(RESPONSE_STATUS_EXCEPTION);
	}

	protected void setResponseHeader(final ResponseBase response, final RequestBase request) {     
		response.setMessageResponseDateTime(toXMLGregorianCalendar((java.sql.Timestamp) null));
		response.setRequestMessageID(request.getRequestMessageID());
		response.setResponseMessageID(messageIdGenerator.nextMessageId());
		
		// don't wipe out a pre-existing status (especially if it says "ERROR!!")
		if (response.isResponseStatus() != RESPONSE_STATUS_EXCEPTION) {
			response.setResponseStatus(RESPONSE_STATUS_OK);
		}
	}
	
	// TODO- throws a more expressive exception
	protected void setServiceContext(final RequestBase request) throws Exception {
		if (request == null) {
			throw new Exception("Service request is required.");
		}
		
		if (request.getUser() == null) {
			throw new Exception("Service request user is required.");
		}
		
		requestingUserId = request.getUser().getUsername();
		
		if (requestingUserId == null || requestingUserId.length() < 1) {
			throw new Exception("Service request user ID is required.");
		}
		
		// TODO- contact user authentication service and validate user
	}
	
	public String getRequestingUserId() {
		return requestingUserId;
	}

	protected XMLGregorianCalendar toXMLGregorianCalendar(final java.sql.Date date) {

    	// Note: assuming the same timezone in the date as the current system.
    	GregorianCalendar gc = new GregorianCalendar();
    	gc.setTimeInMillis(date.getTime());
    	gc.setTimeZone(GMT_TIMEZONE);
    	
    	return datatypeFactory.newXMLGregorianCalendar(gc);
    }

    protected java.sql.Date toDate(final XMLGregorianCalendar cal) {
    	if (cal == null) {
    		return null;
    	}
    	
    	return new java.sql.Date(cal.toGregorianCalendar().getTimeInMillis());
    }

    protected XMLGregorianCalendar toXMLGregorianCalendar(final java.sql.Timestamp timestamp) {
    	return toXMLGregorianCalendar(timestamp, true);
    }
    
    protected XMLGregorianCalendar toXMLGregorianCalendar(final java.sql.Timestamp timestamp, final boolean defaultToNow) {

    	// Note: assuming the same timezone in the date as the current system.
    	GregorianCalendar gc = new GregorianCalendar();
    	if (timestamp != null) {
    		gc.setTimeInMillis(timestamp.getTime());
    	} else if (defaultToNow == false) {
    		return null;
    	} else {
        	gc.setTimeInMillis(System.currentTimeMillis());
    	}
    	gc.setTimeZone(GMT_TIMEZONE);

    	return datatypeFactory.newXMLGregorianCalendar(gc);
    }

    protected java.sql.Timestamp toTimestamp(final XMLGregorianCalendar cal) {
    	if (cal == null) {
    		return null;
    	}
    	
    	return new java.sql.Timestamp(cal.toGregorianCalendar().getTimeInMillis());
    }
    
}
