/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.activemq.usecases;

import java.net.URI;
import java.util.Arrays;

import jakarta.jms.Connection;
import jakarta.jms.ConnectionFactory;
import jakarta.jms.Destination;
import jakarta.jms.JMSException;
import jakarta.jms.MessageConsumer;
import jakarta.jms.MessageProducer;
import jakarta.jms.Session;
import jakarta.jms.TextMessage;

import junit.framework.Test;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.CombinationTestSupport;
import org.apache.activemq.broker.BrokerService;
import org.apache.activemq.command.ActiveMQQueue;
import org.apache.activemq.util.MessageIdList;
import org.apache.activemq.xbean.XBeanBrokerFactory;

public class TwoBrokerMulticastQueueTest extends CombinationTestSupport {

    public static final int MESSAGE_COUNT = 100;
    public static final int BROKER_COUNT = 2;
    public static final int CONSUMER_COUNT = 20;

    public String sendUri;
    public String recvUri;
    private BrokerService[] brokers;
    private String groupId;

    public static Test suite() {
        return suite(TwoBrokerMulticastQueueTest.class);
    }

    public static void main(String[] args) {
        junit.textui.TestRunner.run(suite());
    }

    public void setUp() throws Exception {
    	groupId = getClass().getName()+"-"+System.currentTimeMillis();
    	System.setProperty("groupId", groupId);
        super.setAutoFail(true);
        super.setUp();
    }

    public void tearDown() throws Exception {
        if (brokers != null) {
            for (int i = 0; i < BROKER_COUNT; i++) {
                if (brokers[i] != null) {
                    brokers[i].stop();
                }
            }
            super.tearDown();
        }
    }

    private void doSendReceiveTest() throws Exception {
        Destination dest = new ActiveMQQueue("TEST.FOO");

        ConnectionFactory sendFactory = createConnectionFactory(sendUri);

        Connection conn = createConnection(sendFactory);
        sendMessages(conn, dest, MESSAGE_COUNT);

        Thread.sleep(500);

        ConnectionFactory recvFactory = createConnectionFactory(recvUri);
        assertEquals(MESSAGE_COUNT, receiveMessages(createConnection(recvFactory), dest, 0));
    }

    private void doMultipleConsumersConnectTest() throws Exception {
        Destination dest = new ActiveMQQueue("TEST.FOO");

        ConnectionFactory sendFactory = createConnectionFactory(sendUri);

        Connection conn = createConnection(sendFactory);
        sendMessages(conn, dest, MESSAGE_COUNT);

        Thread.sleep(500);

        ConnectionFactory recvFactory = createConnectionFactory(recvUri);
        assertEquals(MESSAGE_COUNT, receiveMessages(createConnection(recvFactory), dest, 0));

        for (int i = 0; i < (CONSUMER_COUNT - 1); i++) {
            assertEquals(0, receiveMessages(createConnection(recvFactory), dest, 200));
        }
    }

    public void initCombosForTestSendReceive() {
        addCombinationValues("sendUri", new Object[] {"tcp://localhost:61616", "tcp://localhost:61617"});
        addCombinationValues("recvUri", new Object[] {"tcp://localhost:61616", "tcp://localhost:61617"});
    }

    public void testSendReceive() throws Exception {
        createMulticastBrokerNetwork();
        doSendReceiveTest();
    }

    public void initCombosForTestMultipleConsumersConnect() {
        addCombinationValues("sendUri", new Object[] {"tcp://localhost:61616", "tcp://localhost:61617"});
        addCombinationValues("recvUri", new Object[] {"tcp://localhost:61616", "tcp://localhost:61617"});
    }

    public void testMultipleConsumersConnect() throws Exception {
        createMulticastBrokerNetwork();
        doMultipleConsumersConnectTest();
    }

    public void testSendReceiveUsingFailover() throws Exception {
        sendUri = "failover:(tcp://localhost:61616,tcp://localhost:61617)";
        recvUri = "failover:(tcp://localhost:61616,tcp://localhost:61617)";
        createMulticastBrokerNetwork();
        doSendReceiveTest();
    }

    public void testMultipleConsumersConnectUsingFailover() throws Exception {
        sendUri = "failover:(tcp://localhost:61616,tcp://localhost:61617)";
        recvUri = "failover:(tcp://localhost:61616,tcp://localhost:61617)";
        createMulticastBrokerNetwork();
        doMultipleConsumersConnectTest();
    }

    public void testSendReceiveUsingDiscovery() throws Exception {
        sendUri = "discovery:multicast://default?group="+groupId;
        recvUri = "discovery:multicast://default?group="+groupId;
        createMulticastBrokerNetwork();
        doSendReceiveTest();
    }

    public void testMultipleConsumersConnectUsingDiscovery() throws Exception {
        sendUri = "discovery:multicast://default?group="+groupId;
        recvUri = "discovery:multicast://default?group="+groupId;
        createMulticastBrokerNetwork();
        doMultipleConsumersConnectTest();
    }

    public void testSendReceiveUsingAutoAssignFailover() throws Exception {
        sendUri = "failover:(discovery:multicast:default?group=//"+groupId+")";
        recvUri = "failover:(discovery:multicast:default?group=//"+groupId+")";
        createAutoAssignMulticastBrokerNetwork();
        doSendReceiveTest();
    }

    public void testMultipleConsumersConnectUsingAutoAssignFailover() throws Exception {
        sendUri = "failover:(discovery:multicast:default?group=//"+groupId+")";
        recvUri = "failover:(discovery:multicast:default?group=//"+groupId+")";
        createAutoAssignMulticastBrokerNetwork();
        doMultipleConsumersConnectTest();
    }

    public void testSendReceiveUsingAutoAssignDiscovery() throws Exception {
        sendUri = "discovery:multicast://default?group="+groupId;
        recvUri = "discovery:multicast://default?group="+groupId;
        createAutoAssignMulticastBrokerNetwork();
        doSendReceiveTest();
    }

    public void testMultipleConsumersConnectUsingAutoAssignDiscovery() throws Exception {
        sendUri = "discovery:multicast://default?group="+groupId;
        recvUri = "discovery:multicast://default?group="+groupId;
        createAutoAssignMulticastBrokerNetwork();
        doMultipleConsumersConnectTest();
    }

    protected void createMulticastBrokerNetwork() throws Exception {
        brokers = new BrokerService[BROKER_COUNT];
        for (int i = 0; i < BROKER_COUNT; i++) {
            brokers[i] = createBroker("org/apache/activemq/usecases/multicast-broker-" + (i + 1) + ".xml");
            brokers[i].start();
        }

        // Let the brokers discover each other first
        Thread.sleep(1000);
    }

    protected void createAutoAssignMulticastBrokerNetwork() throws Exception {
        brokers = new BrokerService[BROKER_COUNT];
        for (int i = 0; i < BROKER_COUNT; i++) {
            brokers[i] = createBroker("org/apache/activemq/usecases/multicast-broker-auto.xml");
            brokers[i].start();
        }

        // Let the brokers discover each other first
        Thread.sleep(1000);
    }

    protected BrokerService createBroker(String uri) throws Exception {
        return (new XBeanBrokerFactory()).createBroker(new URI(uri));
    }

    protected ConnectionFactory createConnectionFactory(String uri) {
        return new ActiveMQConnectionFactory(uri);
    }

    protected Connection createConnection(ConnectionFactory factory) throws JMSException {
        Connection conn = factory.createConnection();
        return conn;
    }

    protected int receiveMessages(Connection conn, Destination dest, int waitTime) throws JMSException, InterruptedException {
        conn.start();
        MessageIdList list = new MessageIdList();
        Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
        MessageConsumer consumer = sess.createConsumer(dest);
        consumer.setMessageListener(list);

        if (waitTime > 0) {
            Thread.sleep(waitTime);
        } else {
            list.waitForMessagesToArrive(MESSAGE_COUNT);
        }

        conn.close();

        return list.getMessageCount();
    }

    protected void sendMessages(Connection conn, Destination dest, int count) throws JMSException {
        conn.start();
        Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
        MessageProducer prod = sess.createProducer(dest);

        for (int i = 0; i < count; i++) {
            prod.send(createTextMessage(sess, "Message " + i, 1024));
        }

        conn.close();
    }

    protected TextMessage createTextMessage(Session session, String initText, int messageSize) throws JMSException {
        TextMessage msg = session.createTextMessage();

        // Pad message text
        if (initText.length() < messageSize) {
            char[] data = new char[messageSize - initText.length()];
            Arrays.fill(data, '*');
            String str = new String(data);
            msg.setText(initText + str);

            // Do not pad message text
        } else {
            msg.setText(initText);
        }

        return msg;
    }

}
