package eu.osci.messageparts;

import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;

import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;
import javax.xml.parsers.ParserConfigurationException;

import org.w3._2000._09.xmldsig.X509DataType;
import org.w3._2005._08.addressing.AttributedURIType;
import org.w3._2005._08.addressing.EndpointReferenceType;
import org.w3._2005._08.addressing.ReferenceParametersType;
import org.w3._2005._08.addressing.RelatesToType;

import com.sun.xml.ws.api.SOAPVersion;
import com.sun.xml.ws.api.message.Header;
import com.sun.xml.ws.api.message.HeaderList;
import com.sun.xml.ws.api.message.Headers;

import eu.osci.AddressingHeaderTag;
import eu.osci.OSCIConstants;
import eu.osci.OSCIHeaderTag;
import eu.osci.OSCIRequestAction;
import eu.osci.messagetypes.OSCIRequest;
import eu.osci.ws._2008._05.transport.DeliveryReceiptType;
import eu.osci.ws._2008._05.transport.MsgTimeStampsType;
import eu.osci.ws._2008._05.transport.ObjectFactory;
import eu.osci.ws._2008._05.transport.ReceiptInfoType;
import eu.osci.ws._2008._05.transport.TypeOfBusinessScenarioType;
import eu.osci.ws._2008._05.transport.X509TokenContainerType;
import eu.osci.ws._2008._05.transport.X509TokenInfo;


/**
 * Helper class to create parts of an OSCI message. All OSCI message parts and a lot of the addressing parts
 * could be created easily with the help of this class.<br>
 * Copyright © 2010 Freie Hansestadt Bremen. <br>
 * Created by bremen online services Entwicklungs- und Betriebsgesellschaft mbH & Co. KG. <br>
 * This library (Starter Kit) can be used by anybody according to the Bremer Lizenz für freie
 * Softwarebibliotheken. The license terms can be viewed at www.osci.de or can be ordered at the
 * OSCI-Leitstelle (Die Senatorin für Finanzen, Freie Hansestadt Bremen, Postfach 10 15 40, 28015 Bremen).
 * 
 * @author R. Lindemann
 */
public class OSCIFragmentCreator
{

  /**
   * Creates an EndpointReference with default values.
   * 
   * @return An EndpointReference with default values.
   */
  public static EndpointReferenceType createAnonymousEndpointRef()
  {
    return createEndpointRef(OSCIConstants.ANONYMOUS_ADDRESS);
  }

  /**
   * Creates an EndpointReference with the given URI for the address.
   * 
   * @param uri The URI that will be included into the EndpointReference element.
   * @return An EndpointReference with the given values.
   */
  public static EndpointReferenceType createEndpointRef(String uri)
  {
    EndpointReferenceType ref = new EndpointReferenceType();
    AttributedURIType attUri = new AttributedURIType();
    attUri.setValue(uri);
    ref.setAddress(attUri);
    return ref;
  }

  /**
   * Creates a DeliveryReceiptType object with the given parameter out of the SOAP message (HeaderList). The
   * created object will be returned for further processing. The created DeliveryReceipt is not complete, for
   * example the signature, the encryption and the timestamp is missing. The JAXB object could be used to fill
   * the missing information.
   * 
   * @param headerList Header list of the OSCI message that will be receipt.
   * @return The created DeliveryReceipt object.
   * @throws DatatypeConfigurationException
   * @throws JAXBException
   * @throws ParserConfigurationException
   */
  public static DeliveryReceiptType createReceptionReceiptObject(HeaderList headerList)
    throws DatatypeConfigurationException, JAXBException, ParserConfigurationException
  {
    DelOSCIRequest req = new DelOSCIRequest();
    req.interpretHeadersPublic(headerList);
    return createDeliveryReceiptObject(req);
  }

  /**
   * Creates a DeliveryReceipt object with the given OSCI message. The created object will be returned for
   * further processing. The created DeliveryReceipt is not complete, for example the signature, the
   * encryption and the timestamp is missing. The JAXB object could be used to fill the missing information.
   * 
   * @param msg The OSCI message that will be receipt.
   * @return The created DeliveryReceipt object.
   * @throws DatatypeConfigurationException
   * @throws JAXBException
   * @throws ParserConfigurationException
   */
  public static DeliveryReceiptType createDeliveryReceiptObject(OSCIRequest msg)
    throws DatatypeConfigurationException, ParserConfigurationException, JAXBException
  {
    DeliveryReceiptType receReceipt = new DeliveryReceiptType();
    ReceiptInfoType info = new ReceiptInfoType();
    receReceipt.setReceiptInfo(info);
    MsgTimeStampsType msgTimeStamp = null;
    if (msg.getMsgTimeStamps() == null)
      msgTimeStamp = new MsgTimeStampsType();
    else
      msgTimeStamp = msg.getMsgTimeStamps();
    msgTimeStamp.setReception(OSCIFragmentCreator.createXMLGregorianCalendar(new Date()));
    info.setId("ReceiptInfo");
    info.setReceiptIssuerRole(OSCIConstants.RECIPIENT_ISSUER_ROLE_RECIPIENT);
    info.setFrom(msg.getFrom());
    if (msg.getRelatesTo() != null && msg.getRelatesTo().size() > 0)
    {
      Iterator<RelatesToType> list = msg.getRelatesTo().iterator();
      while (list.hasNext())
      {
        info.getRelatesTo().add(list.next());
      }
    }
    if (msg.getRelatesTo() != null && msg.getRelatesTo().size() > 0)
    {
      Iterator<RelatesToType> relatesList = msg.getRelatesTo().iterator();
      while (relatesList.hasNext())
      {
        info.getRelatesTo().add(relatesList.next());
      }
    }
    info.setReplyTo(msg.getReplyTo());
    info.setMessageID(msg.getMessageId());
    info.setMsgTimeStamps(msgTimeStamp);
    if (msg.getTo() != null)
      info.setTo(OSCIFragmentCreator.createEndpointReferenceParameter(msg.getTo().getValue(),
                                                                      msg.getTypeOfBusinessScenario()));
    return receReceipt;
  }

  /**
   * Creates an EndpointReference with the given URI for the address and the typeOfBusinessScenario.
   * 
   * @param uri The URI that will be included in the EndpointReference element.
   * @param businessScenario Value of the typeOfBusinessScenario that is to be included in the
   *          EndpointReference.
   * @return EndpointReference with the given values.
   */
  public static EndpointReferenceType createEndpointReferenceParameter(String uri,
                                                                       OSCIRequestAction businessScenario)
  {
    return createEndpointReferenceParameter(uri, businessScenario.getValue());
  }

  /**
   * Creates an EndpointReference with the given URI for the address and the typeOfBusinessScenario.
   * 
   * @param uri The URI that will be included in the EndpointReference element.
   * @param businessScenario Value of the typeOfBusinessScenario that is to be included in the
   *          EndpointReference.
   * @return EndpointReference with the given values.
   */
  public static EndpointReferenceType createEndpointReferenceParameter(String uri, String businessScenario)
  {
    EndpointReferenceType ref = createEndpointRef(uri);
    ReferenceParametersType parameter = new ReferenceParametersType();
    TypeOfBusinessScenarioType typeOf = new TypeOfBusinessScenarioType();
    typeOf.setIsReferenceParameter(true);
    typeOf.setValue(businessScenario);
    JAXBElement<TypeOfBusinessScenarioType> typeOfBus = new ObjectFactory().createTypeOfBusinessScenario(typeOf);
    parameter.getAny().add(typeOfBus);
    ref.setReferenceParameters(parameter);
    return ref;
  }

  /**
   * Creates an OSCI X509TokenContainer with the given certificates.
   * 
   * @param x509CertificateList A list of certificates that is to be included.
   * @return The created OSCI X509TokenContainer
   * @throws CertificateEncodingException In case of encoding problems with the certificates.
   */
  public static X509TokenContainerType createOSCITokenContainer(List<X509Certificate> x509CertificateList)
    throws CertificateEncodingException
  {
    X509TokenContainerType x509Container = new X509TokenContainerType();
    for ( X509Certificate certificate : x509CertificateList )
    {
      X509TokenInfo info = new X509TokenInfo();
      X509DataType data = new X509DataType();

      data.getX509IssuerSerialOrX509SKIOrX509SubjectName()
          .add(new com.sun.xml.security.core.dsig.ObjectFactory().createX509DataTypeX509Certificate(certificate.getEncoded()));
      info.setX509Data(data);
      x509Container.getX509TokenInfo().add(info);
    }
    return x509Container;
  }

  /**
   * Adds a certificate to the given X509TokenContainer.
   * 
   * @param x509Container The already created X509TokenContainer.
   * @param x509Certificate The new certificate that is to be included.
   */
  public static void addX509CertificateToTokenContainer(X509TokenContainerType x509Container,
                                                        X509Certificate x509Certificate)
  {
    X509TokenInfo info = new X509TokenInfo();
    X509DataType data = new X509DataType();
    data.getX509IssuerSerialOrX509SKIOrX509SubjectName().add(x509Certificate);
    info.setX509Data(data);
    x509Container.getX509TokenInfo().add(info);
  }

  /**
   * Creates an OSCI MsgTimeStamps object with the given parameter.
   * 
   * @param obsoleteAfter The date/time when the message will expire.
   * @return The created MsgTimeStamps object.
   * @throws DatatypeConfigurationException If there are converting errors with the date objects.
   */
  public static MsgTimeStampsType createOSCIMsgTimeStamps(int obsoleteAfter)
    throws DatatypeConfigurationException
  {
    MsgTimeStampsType msgtime = new MsgTimeStampsType();
    if (obsoleteAfter > -1)
    {
      msgtime.setObsoleteAfter(createXMLGregorianCalendar(obsoleteAfter));
    }
    return msgtime;
  }

  /**
   * Creates an XMLGregorianCalendar with the given date object.
   * 
   * @param date The date/time that is to be included in the XMLGregorianCalendar.
   * @return The created XMLGregorianCalendar object.
   * @throws DatatypeConfigurationException If there are converting errors with the date objects.
   */
  public static XMLGregorianCalendar createXMLGregorianCalendar(Date date)
    throws DatatypeConfigurationException
  {
    GregorianCalendar cal = new GregorianCalendar();
    cal.setTime(date);
    XMLGregorianCalendar xmlCal = DatatypeFactory.newInstance().newXMLGregorianCalendar(cal);
    return xmlCal;
  }

  /**
   * Creates a XMLGregorianCalendar with the date/time from now plus the given days.
   * 
   * @param deltaDays The count of days to add to the current date/time.
   * @return The created XMLGregorianCalendar object.
   * @throws DatatypeConfigurationException If there are converting errors with the date objects.
   */
  public static XMLGregorianCalendar createXMLGregorianCalendar(int deltaDays)
    throws DatatypeConfigurationException
  {
    GregorianCalendar cal = new GregorianCalendar();
    cal.set(Calendar.DAY_OF_MONTH, cal.get(Calendar.DAY_OF_MONTH) + deltaDays);
    XMLGregorianCalendar xmlCal = DatatypeFactory.newInstance().newXMLGregorianCalendar(cal);
    return xmlCal;
  }

  /**
   * Internal helper method to create SOAP headers.
   * 
   * @param headerTag The type of the header that will be created.
   * @param osciObject The OSCI object to serialise into the header.
   * @return The created Header.
   * @throws JAXBException In case of serialisation problems.
   */
  public static Header createOSCIHeader(OSCIHeaderTag headerTag, Object osciObject) throws JAXBException
  {
    return Headers.create(SOAPVersion.SOAP_12,
                          JaxbContextHolder.getOSCIJaxBContext().createMarshaller(),
                          headerTag.getValue(),
                          osciObject);
  }

  /**
   * Internal helper method to create XKMS headers.
   * 
   * @param qName The QName of the Header that will be created.
   * @param xkmsObject The XKMS object to serialise into the header.
   * @return The created Header.
   * @throws JAXBException In case of serialisation problems.
   */
  public static Header createXKMSHeader(QName qName, Object xkmsObject) throws JAXBException
  {
    return Headers.create(SOAPVersion.SOAP_12,
                          JaxbContextHolder.getXKMSJaxBContext().createMarshaller(),
                          qName,
                          xkmsObject);
  }

  /**
   * Internal helper method to create addressing headers.
   * 
   * @param addressingHeaderTag The type of the header that will be created.
   * @param addressingObj The addressing object to serialise into the header.
   * @return The created Header.
   * @throws JAXBException In case of serialisation problems.
   */
  public static Header createAddressingHeader(AddressingHeaderTag addressingHeaderTag, Object addressingObj)
    throws JAXBException
  {
    return Headers.create(SOAPVersion.SOAP_12,
                          JaxbContextHolder.getAddressingJaxBContext().createMarshaller(),
                          addressingHeaderTag.getQName(),
                          addressingObj);
  }

  /**
   * Creates an AttributedURI element.
   * 
   * @param value The value to be added into the AttributedURI element.
   * @return The created AttributedURI object.
   */
  public static AttributedURIType createAttributedURI(String value)
  {
    AttributedURIType att = new AttributedURIType();
    att.setValue(value);
    return att;
  }

  /**
   * Internal method to create a UUID (for example the messageID).
   * 
   * @return The created UUID.
   */
  public static String createUUID()
  {
    return "uuid-" + UUID.randomUUID().toString();
  }

  /**
   * Creates an addressing 'RelatesTo' object.
   * 
   * @param messageId The Id that is to be included in the RelatesToType object.
   * @return The created RelatesToType object.
   */
  public static RelatesToType createRelatesTo(String messageId)
  {
    RelatesToType relates = new RelatesToType();
    relates.setValue(messageId);
    return relates;
  }

  /**
   * Copyright © 2010 Freie Hansestadt Bremen. <br>
   * Created by bremen online services Entwicklungs- und Betriebsgesellschaft mbH & Co. KG. <br>
   * This library (Starter Kit) can be used by anybody according to the Bremer Lizenz für freie
   * Softwarebibliotheken. The license terms can be viewed at www.osci.de or can be ordered at the
   * OSCI-Leitstelle (Die Senatorin für Finanzen, Freie Hansestadt Bremen, Postfach 10 15 40, 28015 Bremen).
   * 
   * @author R. Lindemann
   */
  private static class DelOSCIRequest extends OSCIRequest
  {

    public void interpretHeadersPublic(HeaderList headerList) throws JAXBException
    {
      super.interpretHeaders(headerList);
    }

  }

}
