/*
 *
 * Copyright (c) 2006 The Apache Software Foundation
 *
 * Licensed 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.qpid.server.exchange;

import org.apache.qpid.AMQException;
import org.apache.qpid.server.queue.NoConsumersException;
import org.apache.qpid.server.util.TimedRun;
import org.apache.qpid.server.util.AveragedRun;
import org.apache.qpid.framing.BasicPublishBody;
import org.apache.qpid.framing.ContentHeaderBody;
import org.apache.qpid.framing.ContentBody;

import java.util.List;

/**
 * Want to vary the number of regsitrations, messages and matches and measure
 * the corresponding variance in execution time.
 * <p/>
 * Each registration will contain the 'All' header, even registrations will
 * contain the 'Even' header and odd headers will contain the 'Odd' header.
 * In additions each regsitration will have a unique value for the 'Specific'
 * header as well.
 * <p/>
 * Messages can then be routed to all registrations, to even- or odd- registrations
 * or to a specific registration.
 *
 */
public class HeadersExchangePerformanceTest extends AbstractHeadersExchangeTest
{
    private static enum Mode {ALL, ODD_OR_EVEN, SPECIFIC}

    private final TestQueue[] queues;
    private final Mode mode;

    public HeadersExchangePerformanceTest(Mode mode, int registrations) throws AMQException
    {
        this.mode = mode;
        queues = new TestQueue[registrations];
        for (int i = 0; i < queues.length; i++)
        {
            switch(mode)
            {
                case ALL:
                    queues[i] = bind(new FastQueue("Queue" + i), "All");
                    break;
                case ODD_OR_EVEN:
                    queues[i] = bind(new FastQueue("Queue" + i), "All", oddOrEven(i));
                    break;
                case SPECIFIC:
                    queues[i] = bind(new FastQueue("Queue" + i), "All", oddOrEven(i), "Specific"+ i);
                    break;
            }
        }
    }

    void sendToAll(int count) throws AMQException
    {
        send(count, "All=True");
    }

    void sendToOdd(int count) throws AMQException
    {
        send(count, "All=True", "Odd=True");
    }

    void sendToEven(int count) throws AMQException
    {
        send(count, "All=True", "Even=True");
    }

    void sendToAllSpecifically(int count) throws AMQException
    {
        for (int i = 0; i < queues.length; i++)
        {
            sendToSpecific(count, i);
        }
    }

    void sendToSpecific(int count, int index) throws AMQException
    {
        send(count, "All=True", oddOrEven(index) + "=True", "Specific=" + index);
    }

    private void send(int count, String... headers) throws AMQException
    {
        for (int i = 0; i < count; i++)
        {
            route(new Message("Message" + i, headers));
        }
    }

    private static String oddOrEven(int i)
    {
        return (i % 2 == 0 ? "Even" : "Odd");
    }

    static class FastQueue extends TestQueue
    {

        public FastQueue(String name) throws AMQException
        {
            super(name);
        }

        public void deliver(BasicPublishBody publishBody, ContentHeaderBody contentHeaderBody, List<ContentBody> contentBodies) throws NoConsumersException
        {
            //just discard as we are not testing routing functionality here
        }
    }

    static class Test extends TimedRun
    {
        private final Mode mode;
        private final int registrations;
        private final int count;
        private HeadersExchangePerformanceTest test;

        Test(Mode mode, int registrations, int count)
        {
            super(mode + ", registrations=" + registrations + ", count=" + count);
            this.mode = mode;
            this.registrations = registrations;
            this.count = count;
        }

        protected void setup() throws Exception
        {
            test = new HeadersExchangePerformanceTest(mode, registrations);
            run(100); //do a warm up run before times start
        }

        protected void teardown() throws Exception
        {
            test = null;
            System.gc();
        }

        protected void run() throws Exception
        {
            run(count);
        }

        private void run(int count) throws Exception
        {
            switch(mode)
            {
                case ALL:
                    test.sendToAll(count);
                    break;
                default:
                    System.out.println("Test for " + mode + " not yet implemented.");
            }
        }
    }

    public static void main(String[] argv) throws Exception
    {
        int registrations = Integer.parseInt(argv[0]);
        int messages = Integer.parseInt(argv[1]);
        int iterations = Integer.parseInt(argv[2]);
        TimedRun test = new Test(Mode.ALL, registrations, messages);
        AveragedRun tests = new AveragedRun(test, iterations);
        System.out.println(tests.call());
    }
}

