#!/usr/bin/env python

#
# 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.
#

import os
from optparse import OptionParser, OptionGroup
import sys
import locale
import socket
import re
from proton import Messenger, Message

home = os.environ.get("QD_TOOLS_HOME", os.path.normpath("/usr/share/qd-tools"))
sys.path.append(os.path.join(home, "python"))

from qdtoollibs import Display, Header, Sorter, YN, Commas, TimeLong


class Config:
    def __init__(self):
        self._host = "0.0.0.0"
        self._connTimeout = 10
        self._types = ""
        self._limit = 50
        self._increasing = False
        self._sortcol = None

config = Config()
conn_options = {}

def OptionsAndArguments(argv):
    """ Set global variables for options, return arguments """

    global config
    global conn_options

    usage = \
"""%prog -g [options]
       %prog -c [options]
       %prog -l [options]
       %prog -n [options]
       %prog -a [options]
       %prog -m [options]"""

    parser = OptionParser(usage=usage)

    group1 = OptionGroup(parser, "General Options")
    group1.add_option("-b", "--bus",  action="store", type="string", default="0.0.0.0", metavar="<url>",
                      help="URL of the messaging bus to connect to")
    group1.add_option("-t", "--timeout", action="store", type="int", default=10, metavar="<secs>",
                      help="Maximum time to wait for connection (in seconds)")
    group1.add_option("--sasl-mechanism", action="store", type="string", metavar="<mech>",
                      help="SASL mechanism for authentication (e.g. EXTERNAL, ANONYMOUS, PLAIN, CRAM-MD5, DIGEST-MD5, GSSAPI). SASL automatically picks the most secure available mechanism - use this option to override.")
    group1.add_option("--ssl-certificate", action="store", type="string", metavar="<cert>", help="Client SSL certificate (PEM Format)")
    group1.add_option("--ssl-key", action="store", type="string", metavar="<key>", help="Client SSL private key (PEM Format)")
    parser.add_option_group(group1)

    group2 = OptionGroup(parser, "Command Options")
    group2.add_option("-g", "--general", help="Show General Broker Stats",  action="store_const", const="g",   dest="show")
    group2.add_option("-c", "--connections", help="Show Connections",       action="store_const", const="c",   dest="show")
    group2.add_option("-l", "--links", help="Show Router Links",            action="store_const", const="l",   dest="show")
    group2.add_option("-n", "--nodes", help="Show Router Nodes",            action="store_const", const="n",   dest="show")
    group2.add_option("-a", "--address", help="Show Router Addresses",      action="store_const", const="a",   dest="show")
    group2.add_option("-m", "--memory", help="Show Broker Memory Stats",    action="store_const", const="m",   dest="show")
    parser.add_option_group(group2)

    opts, args = parser.parse_args(args=argv)

    if not opts.show:
        parser.error("You must specify one of these options: -g, -c, -l, -n, -a, or -m. For details, try $ qdstat --help")

    config._types = opts.show
    config._host = opts.bus
    config._connTimeout = opts.timeout

    return args


class BusManager:
    def __init__(self):
        pass

    def SetHost(self, host):
        self.M = Messenger()
        self.M.start()
        self.M.route("amqp:/*", "amqp://%s/$1" % host)
        self.address = "amqp:/_local/agent"
        self.reply   = "amqp:/reply-address/0001"    # FIX THIS!
        self.M.subscribe(self.reply)

    def Disconnect(self):
        self.M.stop()

    def displayConn(self):
        pass

    def _addr_class(self, addr):
        if not addr:
            return "-"
        if addr[0] == 'M' : return "global"
        if addr[0] == 'R' : return "router"
        if addr[0] == 'A' : return "area"
        if addr[0] == 'L' : return "local"
        return "unknown: %s" % addr[0]

    def _addr_text(self, addr):
        if not addr:
            return "-"
        return addr[1:]

    def displayRouterLinks(self):
        disp = Display(prefix="  ")
        heads = []
        heads.append(Header("type"))
        heads.append(Header("dir"))
        heads.append(Header("rindex"))
        heads.append(Header("class"))
        heads.append(Header("addr"))
        rows = []

        request = Message()
        response = Message()

        request.address = self.address
        request.reply_to = self.reply
        request.correlation_id = 1
        request.properties = {u'operation':u'GET', u'type':u'org.apache.qpid.dispatch.router.link'}

        self.M.put(request)
        self.M.send()

        self.M.recv()
        self.M.get(response)

        for link in response.body:
            row = []
            row.append(link['link-type'])
            row.append(link['link-dir'])
            if link['link-type'] == "router":
                row.append(link['index'])
            else:
                row.append('-')
            row.append(self._addr_class(link['owning-addr']))
            row.append(self._addr_text(link['owning-addr']))
            rows.append(row)
        title = "Router Links"
        dispRows = rows
        disp.formattedTable(title, heads, dispRows)

    def displayAddresses(self):
        disp = Display(prefix="  ")
        heads = []
        heads.append(Header("class"))
        heads.append(Header("address"))
        heads.append(Header("in-proc", Header.Y))
        heads.append(Header("local", Header.COMMAS))
        heads.append(Header("remote", Header.COMMAS))
        heads.append(Header("in", Header.COMMAS))
        heads.append(Header("out", Header.COMMAS))
        heads.append(Header("thru", Header.COMMAS))
        rows = []

        request = Message()
        response = Message()

        request.address = self.address
        request.reply_to = self.reply
        request.correlation_id = 1
        request.properties = {u'operation':u'GET', u'type':u'org.apache.qpid.dispatch.router.address'}

        self.M.put(request)
        self.M.send()

        self.M.recv()
        self.M.get(response)

        for addr in response.body:
            row = []
            row.append(self._addr_class(addr['addr']))
            row.append(self._addr_text(addr['addr']))
            row.append(addr['in-process'])
            row.append(addr['subscriber-count'])
            row.append(addr['remote-count'])
            row.append(addr['deliveries-ingress'])
            row.append(addr['deliveries-egress'])
            row.append(addr['deliveries-transit'])
            rows.append(row)
        title = "Router Addresses"
        sorter = Sorter(heads, rows, 'address', 0, True)
        dispRows = sorter.getSorted()
        disp.formattedTable(title, heads, dispRows)

    def displayMemory(self):
        disp = Display(prefix="  ")
        heads = []
        heads.append(Header("type"))
        heads.append(Header("size", Header.COMMAS))
        heads.append(Header("batch"))
        heads.append(Header("thread-max", Header.COMMAS))
        heads.append(Header("total", Header.COMMAS))
        heads.append(Header("in-threads", Header.COMMAS))
        heads.append(Header("rebal-in", Header.COMMAS))
        heads.append(Header("rebal-out", Header.COMMAS))
        rows = []

        request = Message()
        response = Message()

        request.address = self.address
        request.reply_to = self.reply
        request.correlation_id = 1
        request.properties = {u'operation':u'GET', u'type':u'org.apache.qpid.dispatch.allocator'}

        self.M.put(request)
        self.M.send()

        self.M.recv()
        self.M.get(response)

        for t in response.body:
            row = []
            row.append(t['name'])
            row.append(t['type_size'])
            row.append(t['transfer_batch_size'])
            row.append(t['local_free_list_max'])
            row.append(t['total_alloc_from_heap'])
            row.append(t['held_by_threads'])
            row.append(t['batches_rebalanced_to_threads'])
            row.append(t['batches_rebalanced_to_global'])
            rows.append(row)
        title = "Types"
        sorter = Sorter(heads, rows, 'type', 0, True)
        dispRows = sorter.getSorted()
        disp.formattedTable(title, heads, dispRows)

    def displayMain(self, names, main):
        if   main == 'l': self.displayRouterLinks()
        elif main == 'n': self.displayRouterNodes()
        elif main == 'a': self.displayAddresses()
        elif main == 'm': self.displayMemory()

    def display(self, names):
        self.displayMain(names, config._types)


def main(argv=None):

    args = OptionsAndArguments(argv)
    bm   = BusManager()

    try:
        bm.SetHost(config._host)
        bm.display(args)
        bm.Disconnect()
        return 0
    except KeyboardInterrupt:
        print
    except Exception,e:
        print "Failed: %s - %s" % (e.__class__.__name__, e)
        raise

    bm.Disconnect()   # try to deallocate brokers
    return 1

if __name__ == "__main__":
        sys.exit(main())
