package eu.osci.messagetypes;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.logging.Logger;

import javax.xml.bind.JAXBException;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.namespace.QName;
import javax.xml.transform.stream.StreamSource;
import javax.xml.ws.WebServiceFeature;

import org.w3._2005._08.addressing.AttributedURIType;
import org.w3._2005._08.addressing.EndpointReferenceType;
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.Message;
import com.sun.xml.ws.api.message.Messages;

import eu.osci.ErrorTextTranslation;
import eu.osci.OSCIException;
import eu.osci.helper.EndpointLocator;
import eu.osci.messageparts.JaxbContextHolder;
import eu.osci.messageparts.OSCIFragmentCreator;
import eu.osci.ws._2008._05.transport.MsgBoxStatusListRequestType;
import eu.osci.ws._2008._05.transport.MsgSelector;
import eu.osci.ws._2008._05.transport.ObjectFactory;


/**
 * This class gets the status list of OSCI messages from the requested OSCI message box. The selection rules
 * must be set and the next period of the list can be fetched by the {@link OSCIMsgBoxGetNextRequest} message.<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 OSCIMsgBoxStatusListRequest extends OSCIRequestParent
{

  /** Logger to log the messages. */
  protected static final Logger LOG = Logger.getLogger(OSCIMsgBoxStatusListRequest.class.getName());

  private MsgBoxStatusListRequestType msgBoxStatusListRequestType = null;

  /**
   * <b>For internal usage only!</b>
   */
  protected OSCIMsgBoxStatusListRequest()
  {
    super();
  }

  /**
   * Constructor with the necessary parameters. The URL to WSDL is given and the service and port out of it.
   * 
   * @param _wsdlURL The URL to the WSDL.
   * @param _serviceQName The service that will be used.
   * @param _portQName The port name that will be used
   * @param _features To add other features for the endpoint (see Metro Api-Doc).
   */
  public OSCIMsgBoxStatusListRequest(URL _wsdlURL,
                                     QName _serviceQName,
                                     QName _portQName,
                                     WebServiceFeature... _features)
  {
    super(_wsdlURL, _serviceQName, _portQName, _features);
  }

  /**
   * This constructor is normally used by the client. The previous request will be given as parameter.
   * 
   * @param parent The previous request.
   */
  public OSCIMsgBoxStatusListRequest(OSCIRequestParent parent)
  {
    super(parent);
    LOG.fine("Entry constructor with OSCIRequestParent");
  }

  /**
   * Constructor for endpointLocator parameter with all necessary information.
   * 
   * @param endpointLocator The configured endpointLocator object.
   * @param _features To add other features for the endpoint (see Metro Api-Doc).
   */
  public OSCIMsgBoxStatusListRequest(EndpointLocator endpointLocator, WebServiceFeature... _features)
  {
    this(endpointLocator.getWsdlFile(),
         endpointLocator.getServiceForTypeOfBusinessScenario(endpointLocator.getTypeOfBusinessScenario()),
         endpointLocator.getPortForTypeOfBusinessScenario(endpointLocator.getTypeOfBusinessScenario()),
         _features);
  }

  /**
   * Returns the created selections for the request.
   */
  public MsgBoxStatusListRequestType getMsgBoxStatusListRequestType()
  {
    return msgBoxStatusListRequestType;
  }

  /**
   * Internal message.
   * 
   * @throws JAXBException In the case of serialisation errors.
   */
  protected void prepareSendMessage() throws JAXBException
  {
    ArrayList<Header> headerList = new ArrayList<Header>();
    super.prepareSendMessage(headerList);
    bindingProvider.setOutboundHeaders(headerList);
  }

  /**
   * Please use the setSelection* methods to configure the fetch status list request. In special cases it is
   * necessary to build the selection configuration on its own. In this case this method will be used to set
   * the search configuration.
   * 
   * @param _msgBoxStatusListRequestType The configured search configuration.
   */
  public void setMsgBoxStatusListRequestType(MsgBoxStatusListRequestType _msgBoxStatusListRequestType)
  {
    msgBoxStatusListRequestType = _msgBoxStatusListRequestType;
  }

  /**
   * Sends the request to present server. A SOAP message will be sent and the response message with the
   * fetched status list will be created. Before this method can be used a 'setSelection*' method must be
   * called.
   * 
   * @return An OSCI response message including the status list.
   * @throws JAXBException In the case of parsing or serialisation errors.
   * @throws OSCIException In the case of OSCI SOAP faults.
   */
  public OSCIMsgBoxStatusListResponse sendRequest() throws JAXBException, OSCIException
  {
    LOG.fine("Entry sendRequest");
    if (msgBoxStatusListRequestType == null)
    {
      throw new IllegalStateException(
                                      OSCIMessage.text.getString(ErrorTextTranslation.NO_SELECTION_PARAMETER.getConstant()));
    }
    prepareSendMessage();
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    JaxbContextHolder.getOSCIJaxBContext()
                     .createMarshaller()
                     .marshal(new ObjectFactory().createMsgBoxStatusListRequest(msgBoxStatusListRequestType),
                              out);
    Message sendMsg = Messages.createUsingPayload(new StreamSource(
                                                                   new ByteArrayInputStream(out.toByteArray())),
                                                  SOAPVersion.SOAP_12);
    OSCIMsgBoxStatusListResponse rsp = new OSCIMsgBoxStatusListResponse(sendMessage(sendMsg));
    interpretWSAHeaders(sendMsg.getHeaders());
    fetchIteratorID = rsp.getFetchIteratorID();
    return rsp;
  }


  /**
   * Selects a list of OSCI status lists for the given endpoint by using the the given messageIds of the
   * stored messages.
   * 
   * @param messageIDList A list of messageIds that will be fetched.
   * @param endpoint The endpoint (Address and TypeOfBusinessScenario) to select.
   * @param maxListItems The limit for the search result list.
   */
  public void setSelectionByMessageId(String[] messageIDList, EndpointReferenceType endpoint, int maxListItems)
  {
    LOG.info("Entry setSelectionByMessageId: " + messageIDList + ", " + endpoint + ", " + maxListItems);
    msgBoxStatusListRequestType = new MsgBoxStatusListRequestType();
    msgBoxStatusListRequestType.setEndpointReference(endpoint);
    MsgSelector selector = new MsgSelector();
    if (messageIDList != null)
    {
      for ( String id : messageIDList )
      {
        AttributedURIType msgIDAtt = new AttributedURIType();
        msgIDAtt.setValue(id);
        selector.getMessageID().add(msgIDAtt);
      }
    }
    msgBoxStatusListRequestType.setMsgSelector(selector);
    if (maxListItems > 0)
    {
      msgBoxStatusListRequestType.setMaxListItems(BigInteger.valueOf(maxListItems));
    }
  }

  /**
   * Selects a list of OSCI status lists for the given endpoint by using addressing 'RelatesTo' element.
   * 
   * @param relatesToList A list of addressing 'RelatesToList' Ids that will be fetched for.
   * @param endpoint The endpoint (Address and TypeOfBusinessScenario) to select.
   * @param maxListItems The limit for the search result list.
   */
  public void setSelectionByRelatesTo(String[] relatesToList, EndpointReferenceType endpoint, int maxListItems)
  {
    LOG.info("Entry setSelectionByRelatesTo: " + relatesToList + ", " + endpoint + ", " + maxListItems);
    msgBoxStatusListRequestType = new MsgBoxStatusListRequestType();
    msgBoxStatusListRequestType.setEndpointReference(endpoint);
    MsgSelector selector = new MsgSelector();
    if (relatesToList != null)
    {
      for ( String id : relatesToList )
      {
        RelatesToType relates = new RelatesToType();
        relates.setValue(id);
        selector.getRelatesTo().add(relates);
      }
    }
    msgBoxStatusListRequestType.setMsgSelector(selector);
    if (maxListItems > 0)
    {
      msgBoxStatusListRequestType.setMaxListItems(BigInteger.valueOf(maxListItems));
    }
  }

  /**
   * Sets the addressing 'ReplyTo' attribute on the OSCI message. Including the URL for reply messages.<br>
   * Be careful with this setter because if the framework will put a 'ReplyTo' header into the SOAP message
   * there will be two headers available.
   * 
   * @param _replyTo The 'ReplyTo' attribute.
   */
  public void setReplyTo(EndpointReferenceType _replyTo)
  {
    enableSetWSAReplyTo = true;
    replyTo = _replyTo;
  }

  /**
   * Selects a list of OSCI status lists for the given endpoint.
   * 
   * @param onlyNewElements Indicates that only new (unfetched) status lists will be returned, in the case of
   *          'true'.
   * @param endpoint The endpoint (Address and TypeOfBusinessScenario) to select.
   * @param maxListItems The limit for the search result list.
   */
  public void setSelectionAll(boolean onlyNewElements, EndpointReferenceType endpoint, int maxListItems)
  {
    LOG.info("Entry setSelectionAll: " + onlyNewElements + ", " + endpoint + ", " + maxListItems);
    msgBoxStatusListRequestType = new MsgBoxStatusListRequestType();
    msgBoxStatusListRequestType.setEndpointReference(endpoint);

    MsgSelector selector = new MsgSelector();
    selector.setNewEntry(Boolean.valueOf(onlyNewElements));
    msgBoxStatusListRequestType.setMsgSelector(selector);
    if (maxListItems > 0)
    {
      msgBoxStatusListRequestType.setMaxListItems(BigInteger.valueOf(maxListItems));
    }
  }

  /**
   * Selects a list of OSCI status lists for the given endpoint by using the creation time of the stored
   * messages.
   * 
   * @param from Starting date/time for the selection.
   * @param to End date/time for the selection.
   * @param onlyNewElements Indicates that only new (unfetched) status lists will be returned, in the case of
   *          'true'.
   * @param endpoint The endpoint (Address and TypeOfBusinessScenario) to select.
   * @param maxListItems The limit for the search result list.
   * @throws DatatypeConfigurationException If there are converting errors with the date objects.
   */
  public void setSelectionByDate(Date from,
                                 Date to,
                                 boolean onlyNewElements,
                                 EndpointReferenceType endpoint,
                                 int maxListItems) throws DatatypeConfigurationException
  {
    LOG.info("Entry setSelectionByDate: " + from + ", " + to + ", " + onlyNewElements + ", " + endpoint
             + ", " + maxListItems);
    msgBoxStatusListRequestType = new MsgBoxStatusListRequestType();
    msgBoxStatusListRequestType.setEndpointReference(endpoint);

    MsgSelector selector = new MsgSelector();
    selector.setNewEntry(Boolean.valueOf(onlyNewElements));
    selector.setMsgBoxEntryTimeFrom(OSCIFragmentCreator.createXMLGregorianCalendar(from));
    selector.setMsgBoxEntryTimeTo(OSCIFragmentCreator.createXMLGregorianCalendar(to));
    msgBoxStatusListRequestType.setMsgSelector(selector);
    if (maxListItems > 0)
    {
      msgBoxStatusListRequestType.setMaxListItems(BigInteger.valueOf(maxListItems));
    }
  }
}
