package org.activemq.ws.notification.impl;

import java.io.ByteArrayInputStream;
import java.io.StringReader;
import java.util.Calendar;

import javax.jms.BytesMessage;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.activemq.ActiveMQConnection;
import org.activemq.ActiveMQMessageConsumer;
import org.activemq.message.ActiveMQTopic;
import org.activemq.service.Service;
import org.activemq.ws.notification.NotificationConsumer;
import org.activemq.ws.resource.ResourceProperties;
import org.activemq.ws.xmlbeans.addressing.v2003_03.EndpointReferenceType;
import org.activemq.ws.xmlbeans.notification.base.NotificationMessageHolderType;
import org.activemq.ws.xmlbeans.notification.base.NotifyDocument;
import org.activemq.ws.xmlbeans.notification.base.TopicExpressionType;
import org.activemq.ws.xmlbeans.notification.base.NotifyDocument.Notify;
import org.activemq.ws.xmlbeans.resource.properties.GetResourcePropertyDocument;
import org.activemq.ws.xmlbeans.resource.properties.GetResourcePropertyResponseDocument;
import org.activemq.ws.xmlbeans.resource.properties.QueryExpressionType;
import org.apache.xmlbeans.XmlObject;

public class ActiveMQSubscription implements Service, ResourceProperties {
    
    private final ActiveMQConnection connection;
    private Session session;
    private ActiveMQMessageConsumer consumer;

    private QueryExpressionType selector;
    private QueryExpressionType precondition;
    private XmlObject subscriptionPolicy;
    private TopicExpressionType topicExpression;
    private boolean useNotify;
    private EndpointReferenceType consumerReference;
    private EndpointReferenceType endpointReference;
    private NotificationConsumer notificationConsumer;
    private EndpointReferenceType producerReference;
    private Calendar terminationTime;
    
    public ActiveMQSubscription(ActiveMQConnection connection) throws JMSException {
        this.connection = connection;
    }

    public void setSelector(QueryExpressionType selector) {
        this.selector = selector;        
    }

    public void setPrecondition(QueryExpressionType precondition) {
        this.precondition = precondition;
    }

    public void setSubscriptionPolicy(XmlObject subscriptionPolicy) {
        this.subscriptionPolicy = subscriptionPolicy;
    }

    public void setTopicExpression(TopicExpressionType topicExpression) {
        this.topicExpression = topicExpression;
    }

    public void setUseNotify(boolean useNotify) {
        this.useNotify = useNotify;
    }

    public void setConsumerReference(EndpointReferenceType consumerReference) {
        this.consumerReference = consumerReference;
    }
    
    public void setEndpointReference(EndpointReferenceType endpointReference) {
        this.endpointReference = endpointReference;
    }
    
    public void setConsumer(NotificationConsumer consumer) {
        notificationConsumer = consumer;
        
    }

    private void dispatch(TopicExpressionType topic, XmlObject xml) {
        NotifyDocument request = NotifyDocument.Factory.newInstance();
        Notify notify = request.addNewNotify();
        NotificationMessageHolderType messageHolder = notify.addNewNotificationMessage();
        if( producerReference!=null )
            messageHolder.setProducerReference((EndpointReferenceType) producerReference.copy());
        messageHolder.setTopic(topic);
        messageHolder.setMessage(xml);

        notificationConsumer.notify(request);
    }

    static private String toJMSSelector(QueryExpressionType selector) {
        return null;
    }

    public void start() throws JMSException {
        if( session==null ) {
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            consumer = (ActiveMQMessageConsumer) session.createConsumer(TopicExpressionConverter.toActiveMQTopic(topicExpression), toJMSSelector(selector));
            consumer.setMessageListener(new MessageListener(){
                public void onMessage(Message msg) {
                    try {                        
                        TopicExpressionType topic = TopicExpressionConverter.toTopicExpression((ActiveMQTopic) msg.getJMSDestination());
                        if( msg instanceof BytesMessage  ) {
                            BytesMessage bm = (BytesMessage) msg;
                            byte data[] = new byte[(int)bm.getBodyLength()];
                            bm.readBytes(data);
                            XmlObject xml = XmlObject.Factory.parse(new ByteArrayInputStream(data));                            
                            dispatch(topic, xml);                            
                        } else if( msg instanceof TextMessage ) {
                            TextMessage tm = (TextMessage) msg;
                            XmlObject xml = XmlObject.Factory.parse(new StringReader(tm.getText()));                            
                            dispatch(topic, xml);                            
                        }
                    } catch (Throwable e) {
                        e.printStackTrace();
                    }
                }
            });
        } else {
            consumer.start();
        }
    }

    public void stop() throws JMSException {
        if( session!=null ) {
            consumer.stop();
        }
    }

    public void close() throws JMSException {
        if( session!=null ) {
            session.close();
            session=null;
        }
    }

    public EndpointReferenceType getProducerReference() {
        return producerReference;
    }
    
    public void setProducerReference(EndpointReferenceType producerReference) {
        this.producerReference = producerReference;
    }

    public Calendar setTerminationTime(Calendar requestedTerminationTime) {
        Calendar rc = terminationTime;
        this.terminationTime = requestedTerminationTime;
        return rc;
    }

    public GetResourcePropertyResponseDocument getResourceProperty(EndpointReferenceType resource, GetResourcePropertyDocument request) {
        // TODO Auto-generated method stub
        return null;
    }

    
}
