package org.mockejb.jms;

import java.io.Serializable;
import org.mockejb.MethodNotImplementedException;
import javax.jms.*;
import java.util.*;

/**
 * Represents session with MockEjb JMS provider.
 * Currently contains common methods for <code>QueueSession</code> and
 * <code>TopicSession</code>
 * @author Dimitar Gospodinov
 */
abstract class MockSession implements Session {

    private boolean closed = false;
    private boolean transacted;
    private int acknowledgeMode;

    private MockConnection connection;
    private final List consumers = new ArrayList();
    private final List producers = new ArrayList();

    /**
     * Creates new session with the specified attributes, for connection <code>connection</code>
     * @param transacted
     * @param acknowledgeMode
     * @param connection
     */
    public MockSession(boolean transacted, int acknowledgeMode, MockConnection connection) {
        this.transacted = transacted;
        this.acknowledgeMode = acknowledgeMode;
        this.connection = connection;
    }

    /**
     * Gets session transacted property.
     * @return <code>true</code> if session is transacted or <code>false</code>
     * if not
     * @throws JMSException
     * @see javax.jms.Session#getTransacted()
     */
    public boolean getTransacted() throws JMSException {
        checkClosed();
        return transacted;
    }

    /**
     * Gets session acknowledge mode.
     * @return session acknowledge mode
     * @throws JMSException
     */
    public int getAcknowledgeMode() throws JMSException {
        checkClosed();
        return acknowledgeMode;
    }

    /**
     * Closes this session. Any further operation, except <code>close</code>, on
     * this session will result in <code>IllegalStateException</code>
     * @throws JMSException
     * @see javax.jms.Session#close()
     */
    public void close() throws JMSException {
        closed = true;
        Iterator it = consumers.iterator();
        while (it.hasNext()) {
            ((MockConsumer)it.next()).close();
        }
        it = producers.iterator();
        while (it.hasNext()) {
            ((MockProducer)it.next()).close();
        }
    }

    /**
     * Gets session closed status.
     * @return <code>true</code> if this session has been closed, or <code>false</code>
     * if not.
     */
    protected boolean isClosed() {
        return closed;
    }

    /**
     * Checks if this session has been closed and if yes throws
     * <code>IllegalStateException</code>
     * @throws javax.jms.IllegalStateException if this session has been closed
     */
    protected void checkClosed() throws javax.jms.IllegalStateException {
        if (isClosed()) {
            throw new javax.jms.IllegalStateException(
                "Can not invoke methods on closed session!");
        }
    }

    /**
     * @return
     * @throws JMSException
     * @see javax.jms.Session#createBytesMessage()
     */
    public BytesMessage createBytesMessage() throws JMSException {
        checkClosed();
        return new BytesMessageImpl();
    }

    /**
     * @return
     * @throws JMSException
     * @see javax.jms.Session#createMapMessage()
     */
    public MapMessage createMapMessage() throws JMSException {
        checkClosed();
        return new MapMessageImpl();
    }

    /**
     * @return
     * @throws JMSException
     * @see javax.jms.Session#createMessage()
     */
    public Message createMessage() throws JMSException {
        checkClosed();
        return new MessageImpl();
    }

    /**
     * @return
     * @throws JMSException
     * @see javax.jms.Session#createObjectMessage()
     */
    public ObjectMessage createObjectMessage() throws JMSException {
        checkClosed();
        return new ObjectMessageImpl();
    }

    /**
     * Creates <code>ObjectMessage</code> initialized with the
     * specified serialized object.
     * @throws JMSException
     */
    public ObjectMessage createObjectMessage(Serializable object)
        throws JMSException {

        checkClosed();
        return new ObjectMessageImpl(object);
    }

    /**
     * @return
     * @throws JMSException
     * @see javax.jms.Session#createStreamMessage()
     */
    public StreamMessage createStreamMessage() throws JMSException {
        checkClosed();
        return new StreamMessageImpl();
    }

    /**
     * @return
     * @throws JMSException
     * @see javax.jms.Session#createTextMessage()
     */
    public TextMessage createTextMessage() throws JMSException {
        checkClosed();
        return new TextMessageImpl();
    }

    /**
     * @return
     * @throws JMSException
     * @see javax.jms.Session#createTextMessage(String)
     */
    public TextMessage createTextMessage(String text) throws JMSException {
        checkClosed();
        return new TextMessageImpl(text);
    }

    /**
     * Not implemented.
     * @throws JMSException
     * @see javax.jms.Session#commit()
     */
    public void commit() throws JMSException {
        throw new MethodNotImplementedException("commit", "MockSession");
    }

    /**
     * Not implemented.
     * @throws JMSException
     * @see javax.jms.Session#rollback()
     */
    public void rollback() throws JMSException {
        throw new MethodNotImplementedException("rollback", "MockSession");
    }

    /**
     * Not implemented.
     * @throws JMSException
     * @see javax.jms.Session#recover()
     */
    public void recover() throws JMSException {
        throw new MethodNotImplementedException("recover", "MockSession");
    }

    /**
     * Not implemented.
     * @return
     * @throws JMSException
     * @see javax.jms.Session#getMessageListener()
     */
    public MessageListener getMessageListener() throws JMSException {
        throw new MethodNotImplementedException(
            "getMessageListener",
            "MockSession");
    }

    /**
     * Not implemented.
     * @throws JMSException
     * @see javax.jms.Session#setMessageListener(javax.jms.MessageListener)
     */
    public void setMessageListener(MessageListener listener)
        throws JMSException {
        throw new MethodNotImplementedException(
            "setMessageListener",
            "MockSession");
    }

    /**
     * Not implemented.
     */
    public void run() {
        throw new MethodNotImplementedException("run", "MockSession");
    }

    /**
     * Creates message consumer for the specified destination.
     * @param destination
     * @throws JMSException
     */
    public MessageConsumer createConsumer(Destination destination)
        throws JMSException {
            
        checkClosed();

        if (destination instanceof MockDestination) {
            MockDestination dest = (MockDestination)destination;
            MockConsumer consumer = createMockConsumer(dest);
            dest.registerConsumer(consumer);
            consumers.add(consumer);
            return consumer;
        }
        throw new InvalidDestinationException("Unsupported destination!");
    }

    /**
     * Not implemented.
     * @return
     * @throws JMSException
     */
    public MessageConsumer createConsumer(
        Destination destination,
        java.lang.String messageSelector)
        throws JMSException {

        throw new MethodNotImplementedException(
            "createConsumer",
            "MockSession");
    }

    /**
     * Not implemented.
     * @throws JMSException
     */
    public MessageConsumer createConsumer(
        Destination destination,
        java.lang.String messageSelector,
        boolean NoLocal)
        throws JMSException {

        throw new MethodNotImplementedException(
            "createConsumer",
            "MockSession");
    }

    /**
     * Creates message producer for the specified destination.
     */
    public MessageProducer createProducer(Destination destination)
            throws JMSException {
            
        checkClosed();
        if (destination instanceof MockDestination) {
            MockProducer result = createMockProducer((MockDestination) destination); 
            producers.add(result);
            return result;
        }
        throw new InvalidDestinationException("Unsupported destination!");
    }

    // Non-standard methods

    abstract MockConsumer createMockConsumer(MockDestination destination) throws JMSException;    
    abstract MockProducer createMockProducer(MockDestination destination) throws JMSException;

    MockConnection getConnection() {
        return connection;
    }
    
    void consumeMessages() throws JMSException {
        Iterator it = consumers.iterator();
        while (it.hasNext()) {
            ((MockConsumer)it.next()).consume();
        }
    }

}