/**
 * 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.console.command;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import javax.management.MBeanServerInvocationHandler;
import javax.management.ObjectInstance;
import javax.management.ObjectName;

import org.apache.activemq.broker.jmx.QueueView;
import org.apache.activemq.broker.jmx.QueueViewMBean;
import org.apache.activemq.broker.jmx.TopicView;
import org.apache.activemq.broker.jmx.TopicViewMBean;
import org.apache.activemq.console.util.JmxMBeansUtil;

public class DstatCommand extends AbstractJmxCommand {

    private static final String queryString =
        "type=Broker,brokerName=*,destinationType=%1,destinationName=*,*";

    protected String[] helpFile = new String[] {
        "Task Usage: activemq-admin dstat [dstat-options] [destination-type]",
        "Description: Performs a predefined query that displays useful statistics regarding the specified .",
        "             destination type (Queues or Topics) and displays those results in a tabular format.",
        "             If no broker name is specified, it will try and select from all registered brokers.",
        "",
        "dstat Options:",
        "    --jmxurl <url>                Set the JMX URL to connect to.",
        "    --pid <pid>                   Set the pid to connect to (only on Sun JVM).",
        "    --jmxuser <user>              Set the JMX user used for authenticating.",
        "    --jmxpassword <password>      Set the JMX password used for authenticating.",
        "    --jmxlocal                    Use the local JMX server instead of a remote one.",
        "    --version                     Display the version information.",
        "    -h,-?,--help                  Display the query broker help information.",
        "",
        "Examples:",
        "    activemq-admin dstat queues",
        "        - Display a tabular summary of statistics for the queues on the broker.",
        "    activemq-admin dstat topics",
        "        - Display a tabular summary of statistics for the queues on the broker."
    };

    /**
     * Execute the dstat command, which allows you to display information for topics or queue in
     * a tabular format.
     *
     * @param tokens - command arguments
     * @throws Exception
     */
    @Override
    protected void runTask(List<String> tokens) throws Exception {
        if (tokens.contains("topics")) {
            displayTopicStats();
        } else if (tokens.contains("queues")) {
            displayQueueStats();
        } else {
            displayAllDestinations();
        }
    }

    @SuppressWarnings("unchecked")
    private void displayAllDestinations() throws Exception {

        String query = JmxMBeansUtil.createQueryString(queryString, "*");
        List queueList = JmxMBeansUtil.queryMBeans(createJmxConnection(), query);

        final String header = "%-50s  %10s  %10s  %10s  %10s  %10s  %10s";
        final String tableRow = "%-50s  %10d  %10d  %10d  %10d  %10d  %10d";

        // sort list so the names is A..Z
        Collections.sort(queueList, new ObjectInstanceComparator());

        context.print(String.format(Locale.US, header, "Name", "Queue Size", "Producer #", "Consumer #", "Enqueue #", "Dequeue #", "Forward #", "Memory %"));

        // Iterate through the queue result
        for (Object view : queueList) {
            ObjectInstance obj = (ObjectInstance) view;
            if (!filterMBeans(obj)) {
                continue;
            }
            ObjectName queueName = obj.getObjectName();

            QueueViewMBean queueView = MBeanServerInvocationHandler.
                newProxyInstance(createJmxConnection(), queueName, QueueViewMBean.class, true);

            context.print(String.format(Locale.US, tableRow,
                    queueView.getName(),
                    queueView.getQueueSize(),
                    queueView.getProducerCount(),
                    queueView.getConsumerCount(),
                    queueView.getEnqueueCount(),
                    queueView.getDequeueCount(),
                    queueView.getForwardCount(),
                    queueView.getMemoryPercentUsage()));
        }
    }

    @SuppressWarnings("unchecked")
    private void displayQueueStats() throws Exception {

        String query = JmxMBeansUtil.createQueryString(queryString, "Queue");
        List queueList = JmxMBeansUtil.queryMBeans(createJmxConnection(), query);

        final String header = "%-50s  %10s  %10s  %10s  %10s  %10s  %10s";
        final String tableRow = "%-50s  %10d  %10d  %10d  %10d  %10d  %10d";

        context.print(String.format(Locale.US, header, "Name", "Queue Size", "Producer #", "Consumer #", "Enqueue #", "Dequeue #", "Forward #", "Memory %"));

        Collections.sort(queueList, new ObjectInstanceComparator());

        // Iterate through the queue result
        for (Object view : queueList) {
            ObjectInstance obj = (ObjectInstance) view;
            if (!filterMBeans(obj)) {
                continue;
            }
            ObjectName queueName = obj.getObjectName();

            QueueViewMBean queueView = MBeanServerInvocationHandler.
                newProxyInstance(createJmxConnection(), queueName, QueueViewMBean.class, true);

            context.print(String.format(Locale.US, tableRow,
                    queueView.getName(),
                    queueView.getQueueSize(),
                    queueView.getProducerCount(),
                    queueView.getConsumerCount(),
                    queueView.getEnqueueCount(),
                    queueView.getDequeueCount(),
                    queueView.getForwardCount(),
                    queueView.getMemoryPercentUsage()));
        }
    }

    @SuppressWarnings("unchecked")
    private void displayTopicStats() throws Exception {

        String query = JmxMBeansUtil.createQueryString(queryString, "Topic");
        List topicsList = JmxMBeansUtil.queryMBeans(createJmxConnection(), query);

        final String header = "%-50s  %10s  %10s  %10s  %10s  %10s  %10s";
        final String tableRow = "%-50s  %10d  %10d  %10d  %10d  %10d  %10d";

        // sort list so the names is A..Z
        Collections.sort(topicsList, new ObjectInstanceComparator());

        context.print(String.format(Locale.US, header, "Name", "Queue Size", "Producer #", "Consumer #", "Enqueue #", "Dequeue #", "Forward #", "Memory %"));

        // Iterate through the topics result
        for (Object view : topicsList) {
            ObjectInstance obj = (ObjectInstance) view;
            if (!filterMBeans(obj)) {
                continue;
            }
            ObjectName topicName = obj.getObjectName();

            TopicViewMBean topicView = MBeanServerInvocationHandler.
                newProxyInstance(createJmxConnection(), topicName, TopicViewMBean.class, true);

            context.print(String.format(Locale.US, tableRow,
                    topicView.getName(),
                    topicView.getQueueSize(),
                    topicView.getProducerCount(),
                    topicView.getConsumerCount(),
                    topicView.getEnqueueCount(),
                    topicView.getDequeueCount(),
                    topicView.getForwardCount(),
                    topicView.getMemoryPercentUsage()));
        }
    }

    @Override
    public String getName() {
        return "dstat";
    }

    @Override
    public String getOneLineDescription() {
        return "Performs a predefined query that displays useful tabular statistics regarding the specified destination type";
    }

    /**
     * Print the help messages for this command
     */
    @Override
    protected void printHelp() {
        context.printHelp(helpFile);
    }

    protected boolean filterMBeans(ObjectInstance obj) {
        String className = obj.getClassName();
        return className.equals(QueueView.class.getName()) || className.equals(TopicView.class.getName());
    }

    private static class ObjectInstanceComparator implements Comparator<ObjectInstance> {

        @Override
        public int compare(ObjectInstance o1, ObjectInstance o2) {
            return o1.getObjectName().compareTo(o2.getObjectName());
        }
    }

}