package org.activemq.ws.notification.impl;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Calendar;

import javax.jms.BytesMessage;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.Topic;

import org.activemq.ActiveMQConnection;
import org.activemq.advisories.ProducerDemandAdvisor;
import org.activemq.advisories.ProducerDemandEvent;
import org.activemq.advisories.ProducerDemandListener;
import org.activemq.message.ActiveMQTopic;
import org.activemq.ws.notification.NotificationProducer;
import org.activemq.ws.xmlbeans.addressing.v2003_03.EndpointReferenceType;
import org.activemq.ws.xmlbeans.eventing.UnsubscribeDocument;
import org.activemq.ws.xmlbeans.eventing.UnsubscribeDocument.Unsubscribe;
import org.activemq.ws.xmlbeans.notification.base.GetCurrentMessageDocument;
import org.activemq.ws.xmlbeans.notification.base.GetCurrentMessageResponseDocument;
import org.activemq.ws.xmlbeans.notification.base.SubscribeDocument;
import org.activemq.ws.xmlbeans.notification.base.SubscribeResponseDocument;
import org.activemq.ws.xmlbeans.notification.base.SubscribeDocument.Subscribe;
import org.activemq.ws.xmlbeans.resource.properties.GetResourcePropertyDocument;
import org.activemq.ws.xmlbeans.resource.properties.GetResourcePropertyResponseDocument;
import org.apache.xmlbeans.XmlObject;


public class ActiveMQPublisherRegistration {

    private Calendar terminationTime;
    private boolean demand;
    private EndpointReferenceType publisherReference;
    private EndpointReferenceType endpointReference;
    private ActiveMQTopic topic;
    private final ActiveMQConnection connection;
    private ProducerDemandAdvisor advisor;

    public ActiveMQPublisherRegistration(ActiveMQConnection connection) throws JMSException {
        this.connection = connection;
    }

    public void setTerminationTime(Calendar terminationTime) {
        this.terminationTime = terminationTime;
    }

    public void setDemand(boolean demand) {
        this.demand = demand;
    }

    public void setEndpointReference(EndpointReferenceType endpointReference) {
        this.endpointReference = endpointReference;
    }

    public void setTopic(ActiveMQTopic topic) {
        this.topic = topic;
    }

    public void notify(Topic topic, XmlObject message) throws IOException, JMSException {
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        try {            
            MessageProducer producer = session.createProducer(topic);
            
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            message.save(os);
            os.close();
            byte[] bs = os.toByteArray();
            
            BytesMessage bytesMessage = session.createBytesMessage();
            bytesMessage.writeBytes(bs);
            producer.send(bytesMessage);
            
        } finally {
            session.close();
        }
    }

    public void setPublisherReference(EndpointReferenceType publisherReference) {
        this.publisherReference = publisherReference;        
    }

    public void start() throws JMSException {
        if( demand ) {
            advisor = new ProducerDemandAdvisor(connection, topic);
            advisor.setDemandListener(new ProducerDemandListener(){
                public void onEvent(ProducerDemandEvent event) {
                    fireDemandChangeEvent(event.isInDemand());
                }
            });
            advisor.start();
        }
    }
    
    protected void fireDemandChangeEvent(boolean inDemand) {
        NotificationProducer p = createPublisherNotificationProducer();
        if( inDemand ) {
            SubscribeDocument requestDoc = SubscribeDocument.Factory.newInstance();
            Subscribe subscribe = requestDoc.addNewSubscribe();
            subscribe.setTopicExpression( TopicExpressionConverter.toTopicExpression(topic) );
            subscribe.setUseNotify(true);
            subscribe.setConsumerReference(endpointReference);
            SubscribeResponseDocument reponse = p.Subscribe(requestDoc);
        } else {
            UnsubscribeDocument requestDoc = UnsubscribeDocument.Factory.newInstance();
            Unsubscribe unsubscribe = requestDoc.addNewUnsubscribe();            
        }
    }

    protected NotificationProducer createPublisherNotificationProducer() {
        return new NotificationProducer() {
            public SubscribeResponseDocument Subscribe(SubscribeDocument request) {
                throw new RuntimeException("Not implemented");
            }
            public GetCurrentMessageResponseDocument getCurrentMessage(GetCurrentMessageDocument request) {
                throw new RuntimeException("Not implemented");
            }
            public GetResourcePropertyResponseDocument getResourceProperty(EndpointReferenceType resource, GetResourcePropertyDocument request) {
                throw new RuntimeException("Not implemented");
            }};
    }

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