#!/usr/bin/env python

import os
import sys
import numpy
import pprint
import pymongo
import radical.utils         as ru
import radical.pilot         as rp
import radical.pilot.utils   as rpu
import radical.synapse       as rs
import radical.synapse.utils as rsu


_DEFAULT_DBURL = os.environ.get ('RADICAL_SYNAPSE_DBURL')


# ------------------------------------------------------------------------------
#
def bson2json (bson_data) :

    # thanks to
    # http://stackoverflow.com/questions/16586180/typeerror-objectid-is-not-json-serializable

    import json
    from   bson.objectid import ObjectId

    class MyJSONEncoder (json.JSONEncoder) :
        def default (self, o):
            if  isinstance (o, ObjectId) :
                return str (o)
            if  isinstance (o, datetime.datetime) :
                seconds  = time.mktime (o.timetuple ())
                seconds += (o.microsecond / 1000000.0) 
                return seconds
            return json.JSONEncoder.default (self, o)

    return ru.parse_json (MyJSONEncoder ().encode (bson_data))



# ------------------------------------------------------------------------------
#
def get_json (db, dbname, cachedir) :

    records = db.collection_names()
    ret     = None

    if records:
        for record in records :
            ret = bson2json (list(db[record].find ()))
    else:
        print 'WARNING: given DB is empty -- try cache'
        fname = '%s/%s.json' % (cachedir, dbname)
        try:
            ret = ru.read_json_str('%s/%s.json' % (cachedir, dbname))
        except Exception as e:
            print 'ERROR  : no cache found at %s (%s)' % (fname, e)
            sys.exit (-1)

    return ret


# ------------------------------------------------------------------------------
#
def my_round (x, base=5) :

    # thanks to 
    # http://stackoverflow.com/questions/2272149/round-to-5-or-other-number-in-python

    return int(base * round(float(x) / base))


# ------------------------------------------------------------------------------
#
def usage (msg=None, noexit=False) :

    if  msg :
        print "\n      Error: %s" % msg

    print """
      usage     : %s -m <mode> [-u dburl] [-d dbname]
      example   : %s -m stat    -u mongodb://user:password@localhost/ -d synapse_test


      modes     :

        help    : show this message
        list    : show  a  list   of databases
        tree    : show  a  tree   of record entries
        dump    : show  a  tree   of record entries, with details
        stat    : show statistics of record entries
        plot    : plot record entries

      arguments :
        
        -u      : database URL
        -d      : database name
        -c      : cachedir where <sid>.json caches are kept (also needs -s)
        -t      : terminal type for plotting (pdf and/or png, default is both)
        -x      : command which synapse executed/profiled/emulated
        -s      : synapse mode ( exe[cuted] | pro[filed] | emu[lated] | any) 
                  (can be or-ed via '|')


      The default command is 'list'.  
      The default MongoDB is '%s'.
      
""" % (sys.argv[0], sys.argv[0], _DEFAULT_DBURL)

    if  msg :
        sys.exit (1)

    if  not noexit :
        sys.exit (0)


# ------------------------------------------------------------------------------
#
def dump_database (mongo, db, dbname, cachedir) :

    print "dbname : %s" % dbname
    handle_database (mongo, db, 'dump', dbname, cachedir, None)


# ------------------------------------------------------------------------------
#
def tree_database (mongo, db, dbname, cachedir) :

    handle_database (mongo, db, 'tree', dbname, cachedir, None)


# ------------------------------------------------------------------------------
#
def list_databases (mongo, db, dbname, cachedir) :

  # if  dbname :
  #     print "invalid dbname parameter on 'list'"
  #     sys.exit (-1)

    dbnames = mongo.database_names ()

    if  not dbnames :
        print 'no databases at %s' % mongo

    else :
        print "Databases:"
        for dbname in dbnames :
            if dbname not in ['local'] :
                print "  %s" % dbname


# ------------------------------------------------------------------------------
def sort_database (mongo, db, dbname, cachedir) :

    docs = rpu.get_database_docs (mongo, db, dbname, cachedir=cachedir)

    print "pilot managers :" 
    for doc in docs['pmgr'] :
        print "  %s" %  doc['_id']

    print "pilots :" 
    for doc in docs['pilot'] :
        print "  %s" %  doc['_id']

    print "unit manager"
    for doc in docs['umgr'] :
        print "  %s" %  doc['_id']

    print "units"
    for doc in docs['unit'] :
        print "  %s" %  doc['_id']


# ------------------------------------------------------------------------------
def hist_database (mongo, db, dbname, cachedir) :

    events    = rpu.get_database_events   (mongo, db, dbname, cachedir=cachedir)

    if  not events :
        print "no records found in database %s" % dbname
        sys.exit (-1)

    start = events[0][4]

    # ascii output of time sorted events and slot history

    print "database: %s" % dbname
    print "start   : %s" % str(ru.time_stamp (start))

    for e in events :
        seconds = ru.time_diff (start, e[4])
        print "          %08.2fs : %10s : %15s : %20s (%s)" % (seconds, e[1], e[2], e[5], e[0])


    if  slothists :
        for pilot_info in slothists :
            print "pilot   : %s" % pilot_info['pilot_id']
            for slothist in pilot_info['slothist'] :
                seconds = ru.time_diff (start, slothist['timestamp'])
                print "          %08.2fs : %s" % (seconds, str(slothist['slotstate']))


# ------------------------------------------------------------------------------
def get_stats (docs, events, slothist) :

    n_units               = 0
    n_pilots              = 0
    unit_state_durations  = dict()
    pilot_stats           = dict()
    units                 = dict()

    pilot_stats['pilots'] = dict()

    for cu in docs['unit'] :
        units[str(cu['_id'])] = cu

    for doc in docs['pilot'] :

        n_pilots   += 1
        pid         = str(doc['_id'])
        pilot_info  = dict()

        pilot_info['resource']     = doc['description']['resource']
        pilot_info['cores']        = doc['description']['cores']
        pilot_info['n_units']      = 0
        pilot_info['unit_states']  = dict()
        pilot_info['pilot_states'] = list()

        # we assume that states are time-ordered
        state = doc['statehistory'][0]['state']
        start = doc['statehistory'][0]['timestamp']
        for t in doc['statehistory'][1:] :
            ts = t['timestamp']
            s  = t['state']
            pilot_info['pilot_states'].append ({'state'    : state, 
                                                'duration' : ru.time_diff(start, ts)})
            state = s
            start = ts

        for cu in doc['unit_ids'] :
            uid                    = str(cu)
            n_units               += 1
            pilot_info['n_units'] += 1

            if  not uid in units :
                print 'unknonwn unit %s' % uid
                sys.exit ()

            unit_doc = units[uid]
            state    = unit_doc['statehistory'][0]['state']
            start    = unit_doc['statehistory'][0]['timestamp']

            for t in unit_doc['statehistory'][1:] :
                ts = t['timestamp']
                s  = t['state']

                if not state in pilot_info['unit_states'] :
                    pilot_info['unit_states'][state]        = dict()
                    pilot_info['unit_states'][state]['dur'] = list()

                pilot_info['unit_states'][state]['dur'].append (ru.time_diff(start, ts))
                state = s
                start = ts

        pilot_runtime = (doc['finished'] - doc['started']) * len (slothist[pid]['slots'])
        pilot_busy = 0.0
        for slot in slothist[pid]['slot_infos'] :
            for slot_used in slothist[pid]['slot_infos'][slot] :
                pilot_busy += slot_used[1] - slot_used[0]

        pilot_info['started']     = doc['started']
        pilot_info['finished']    = doc['finished']
        pilot_info['cpu_burned']  = pilot_runtime
        pilot_info['cpu_used']    = pilot_busy
        pilot_info['utilization'] = pilot_busy * 100 / pilot_runtime

        for s in pilot_info['unit_states'] :
            import numpy
            array = numpy.array (pilot_info['unit_states'][s]['dur'])
            pilot_info['unit_states'][s]['num' ] = len        (array)
            pilot_info['unit_states'][s]['mean'] = numpy.mean (array)
            pilot_info['unit_states'][s]['std' ] = numpy.std  (array)
            pilot_info['unit_states'][s]['dur' ] = list()


        pilot_stats['pilots'][pid] = pilot_info

    pilot_stats['n_pilots'] = n_pilots

    return pilot_stats

    
# ------------------------------------------------------------------------------
def stat_database (db_json) :

    print

    for doc in db_json :

        print "index: %s" % doc['command_idx']

        for profile in doc['profiles'] :
            print profile.keys()
            print "  command: %s" % profile['cmd']
            print "  time   : %s" % profile['time']['real']
            print "  cpu    : %s" % len(profile['cpu']['sequence'])
            print "  io     : %s" % len(profile['sto']['sequence'])

            mem = profile['mem']
            print "  mem: %s" % mem['peak']
            print "# %15s  %15s  %15s" % ('time', 'size', 'rss')
            for s in mem['sequence']:
                if not s[1]: 
                    continue
                print '  %15s  %15s  %15s' % (s[0], s[1]['size'], s[1]['rss'])


# ------------------------------------------------------------------------------
def plot_database (db_json, dbname, filters, term, modes) :
    """
    plot results :P
    """

    dat_tot_mem = open("/tmp/rs_%s.mem.tot.dat" % dbname, 'w')
    dat_tot_io  = open("/tmp/rs_%s.io.tot.dat"  % dbname, 'w')
    dat_tot_cpu = open("/tmp/rs_%s.cpu.tot.dat" % dbname, 'w')
                                       
    dat_inc_mem = open("/tmp/rs_%s.mem.inc.dat" % dbname, 'w')
    dat_inc_io  = open("/tmp/rs_%s.io.inc.dat"  % dbname, 'w')
    dat_inc_cpu = open("/tmp/rs_%s.cpu.inc.dat" % dbname, 'w')
                                       
    dat_acc_mem = open("/tmp/rs_%s.mem.acc.dat" % dbname, 'w')
    dat_acc_io  = open("/tmp/rs_%s.io.acc.dat"  % dbname, 'w')
    dat_acc_cpu = open("/tmp/rs_%s.cpu.acc.dat" % dbname, 'w')

    dat_tot_cpu.write(("# " + ("%15s  "*11)+"\n") % ('host', 'mode', 'tid', 'ttc', 'ops', 'flops', 'efficiency', 'utilization', 'load', 'fpc', 'threads'))
    dat_inc_cpu.write(("# " + ("%15s  "* 8)+"\n") % ('host', 'mode', 'tid', 'ttc', 'ops', 'flops', 'efficiency', 'utilization'))
    dat_acc_cpu.write(("# " + ("%15s  "* 5)+"\n") % ('host', 'mode', 'tid', 'ttc', 'ops'))
    dat_tot_io.write (("# " + ("%15s  "* 6)+"\n") % ('host', 'mode', 'tid', 'ttc', 'read', 'write'))
    dat_inc_io.write (("# " + ("%15s  "* 6)+"\n") % ('host', 'mode', 'tid', 'ttc', 'read', 'write'))
    dat_acc_io.write (("# " + ("%15s  "* 6)+"\n") % ('host', 'mode', 'tid', 'ttc', 'read', 'write'))
    dat_tot_mem.write(("# " + ("%15s  "* 7)+"\n") % ('host', 'mode', 'tid', 'ttc', 'rss',  'size', 'peak'))
    dat_inc_mem.write(("# " + ("%15s  "* 6)+"\n") % ('host', 'mode', 'tid', 'ttc', 'rss',  'size'))
    dat_acc_mem.write(("# " + ("%15s  "* 6)+"\n") % ('host', 'mode', 'tid', 'ttc', 'rss',  'size'))

    # some boundaries we want to let gnuplot know about
    max_time      = 0.0
    max_ttc       = 0.0
    max_tot_mem   = 0.0
    max_inc_mem   = 0.0
    max_load      = 0.0
    max_tot_read  = 0.0
    max_tot_write = 0.0
    max_tot_io    = 0.0
    max_inc_read  = 0.0
    max_inc_write = 0.0
    max_inc_io    = 0.0
    max_tot_flops = 0.0
    max_tot_ops   = 0.0
    max_inc_flops = 0.0
    max_inc_ops   = 0.0

    profile_id = 0
    for doc in sorted(db_json, key=lambda x:x['profile']['time']['real']):

        profile     = doc['profile']
        profile_id += 1

        perf = profile.get ('time')
        mem  = profile.get ('mem')
        cpu  = profile.get ('cpu')
        io   = profile.get ('sto')
        cmd  = profile.get ('cmd')

        mode = doc['mode'][0:3]
        host = profile.get ('sys',{}).get('hostname',os.environ.get('RADICAL_SYNAPSE_HOST'))

        if not host:
            pprint.pprint(doc)
            print "need hostname, set RADICAL_SYNAPSE_HOST"
            sys.exit()

        accept = False

        if not filters:
            accept = True
        else:
            for f in filters :
                if f in cmd:
                    accept = True

        if not accept:
          # print "ignore %s" % cmd
            continue

      # print "  command: %s" % cmd
      # print "  time   : %s" % perf['real']
      # print "%s %s" % (perf['real'], cmd)

        real = perf['real']

        max_ttc = max(max_ttc, real)

        if mem:

            if not 'rss' in mem:
                continue

          # dat_tot_mem.write("# %15s  %15s  %15s  %15s  %15s\n" % ('id', 'ttc', 'rss',  'size', 'peak'))
            dat_tot_mem.write("  %15s  %15s  %15s  %15.2f  %15d  %15d  %15d\n"
                    % (host, mode, profile_id, real, mem['rss'], mem['size'], mem['peak']))

            max_tot_mem = max(max_tot_mem, mem['rss'])
          # max_tot_mem = max(max_tot_mem, mem['size'])
          # max_tot_mem = max(max_tot_mem, mem['peak'])

            acc_mem_size = 0.0
            acc_mem_rss  = 0.0

            dat_acc_mem.write("  %15s  %15s  %15s  %15.2f  %15d  %15d\n"
                    % (host, mode, profile_id, 0.0, acc_mem_rss, acc_mem_size))

            for s in mem['sequence']:

                if not s[1]:
                    continue

              # print 'm',
                dat_inc_mem.write("  %15s  %15s  %15s  %15.2f  %15d  %15d\n"
                        % (host, mode, profile_id, s[0], s[1]['rss'], s[1]['size']))

                max_inc_mem = max(max_inc_mem, s[1]['rss'])
              # max_inc_mem = max(max_inc_mem, s[1]['size'])

                acc_mem_rss  += s[1]['rss']
                acc_mem_size += s[1]['size']

                dat_acc_mem.write("  %15s  %15s  %15s  %15.2f  %15d  %15d\n"
                        % (host, mode, profile_id, s[0], acc_mem_rss, acc_mem_size))

            dat_inc_mem.write("\n")
            dat_acc_mem.write("\n")


        if io:

            if not 'read' in io:
                continue

          # dat_tot_io.write("# %15s  %15s  %15s  %15s\n" % ('id', 'ttc', 'read', 'write'))
            dat_tot_io.write("  %15s  %15s  %15s  %15.2f  %15.3f  %15.3f\n"
                    % (host, mode, profile_id, real, io['read'], io['write']))

            max_tot_read  = max(max_tot_read,  io['read'])
            max_tot_write = max(max_tot_write, io['write'])
            max_tot_io    = max(max_tot_read,  max_tot_write)

            acc_io_read  = 0.0
            acc_io_write = 0.0

            dat_acc_io.write("  %15s  %15s  %15s  %15.2f  %15d  %15d\n"
                    % (host, mode, profile_id, 0.0, acc_io_read, acc_io_write))

            for s in io['sequence']:

              # print 'i',
                dat_inc_io.write("  %15s  %15s  %15s  %15.2f  %15.3f  %15.3f\n"
                        % (host, mode, profile_id, s[0], s[1]['read'], s[1]['write']))

                max_inc_read  = max(max_inc_read,  s[1]['read'])
                max_inc_write = max(max_inc_write, s[1]['write'])
                max_inc_io    = max(max_inc_read,  max_inc_write)

                acc_io_read  += s[1]['read']
                acc_io_write += s[1]['write']

                dat_acc_io.write("  %15s  %15s  %15s  %15.2f  %15d  %15d\n"
                        % (host, mode, profile_id, s[0], acc_io_read, acc_io_write))

            dat_inc_io.write("\n")
            dat_acc_io.write("\n")


        if cpu:

            # check if we have more than a basic profile
            if 'ops' in cpu:

                mega    = 1024*1024

                flops   = int(cpu['ops'] / mega / real)
                ops     = int(cpu['ops'] / mega)
                effic   = float(cpu['efficiency'])
                utili   = float(cpu['utilization'])
                load    = cpu['load']
                fpc     = cpu['flops_per_core']
                threads = cpu.get('threads', 1)
          #     fpc     = 13600000000/4/4

                max_tot_flops = max(max_tot_flops, flops)
                max_tot_ops   = max(max_tot_ops  , ops  )
                max_load      = max(max_load     , load )

          #     print "%s - %s" % (ops, max_tot_ops)

                # ('id', 'ttc', 'ops', 'efficiency', 'utilization', 'load', 'fpc', 'threads'))
                dat_tot_cpu.write('  %15s  %15s  %15d  %15.2f  %15d  %15.2f  %15.2f  %15.3f  %15.2f  %15d  %15d\n' \
                        % (host, mode, profile_id, real, ops, flops, effic, utili, load, fpc, threads))

                acc_cpu_ops   = 0.0
                dat_acc_cpu.write('  %15s  %15s  %15d  %15.2f  %15d\n' \
                        % (host, mode, profile_id, 0.0, acc_cpu_ops))

                for s in cpu['sequence']:

                  # print 'c',
                    ts      = s[0]
                    sample  = s[1]
                    threads = sample.get('threads', 1)
                    ops     = sample.get('ops', 0)
                    utili   = sample.get('utilization', 0)
                    effic   = sample.get('efficiency', 0)
                    flops   = sample.get('flops', 0)

                    if not ops:
                      # print "warning: no ops in samples? %s" % ts
                      # pprint.pprint (sample)
                        continue

                    max_inc_ops   = max(max_inc_ops  , ops  )
                    max_inc_flops = max(max_inc_flops, flops)

                    # ('id', 'ttc', 'ops', 'efficiency', 'utilization'))
                    dat_inc_cpu.write('  %15s  %15s  %15d  %15.2f  %15d  %15.2f  %15.2f  %15.2f\n' \
                            % (host, mode, profile_id, ts, ops, flops, effic, utili))

                    acc_cpu_ops += ops
                    dat_acc_cpu.write('  %15s  %15s  %15d  %15.2f  %15d\n' \
                            % (host, mode, profile_id, ts, acc_cpu_ops))

                dat_inc_cpu.write("\n")
                dat_acc_cpu.write("\n")
         
    dat_tot_mem.close()
    dat_tot_io .close()
    dat_tot_cpu.close()

    dat_inc_mem.close()
    dat_inc_io .close()
    dat_inc_cpu.close()

    dat_acc_mem.close()
    dat_acc_io .close()
    dat_acc_cpu.close()

    max_ttc = max(max_ttc, 1)

    f = ru.round_upper_bound
    bounds  = ""
    bounds += " -e max_time='\"%d\"'"      %   max_ttc
    bounds += " -e max_ttc='\"%d\"'"       % f(max_ttc       * 1.1)
    bounds += " -e max_tot_mem='\"%d\"'"   % f(max_tot_mem   * 1.1)
    bounds += " -e max_inc_mem='\"%d\"'"   % f(max_inc_mem   * 1.1)
    bounds += " -e max_load='\"%d\"'"      % f(max_load      * 1.1)
    bounds += " -e max_tot_read='\"%d\"'"  % f(max_tot_read  * 1.1)
    bounds += " -e max_tot_write='\"%d\"'" % f(max_tot_write * 1.1)
    bounds += " -e max_tot_io='\"%d\"'"    % f(max_tot_io    * 1.1)
    bounds += " -e max_inc_read='\"%d\"'"  % f(max_inc_read  * 1.1)
    bounds += " -e max_inc_write='\"%d\"'" % f(max_inc_write * 1.1)
    bounds += " -e max_inc_io='\"%d\"'"    % f(max_inc_io    * 1.1)
    bounds += " -e max_tot_flops='\"%d\"'" % f(max_tot_flops * 1.1)
    bounds += " -e max_tot_ops='\"%d\"'"   % f(max_tot_ops   * 1.1)
    bounds += " -e max_inc_flops='\"%d\"'" % f(max_inc_flops * 1.1)
    bounds += " -e max_inc_ops='\"%d\"'"   % f(max_inc_ops   * 1.1)
    bounds += " -e max_tasks='\"%d\"'"     % profile_id


    if syn_modes:
        mode_tag = "-e modes='\"%s\"'" % ('_'.join(syn_modes))
    else:
        mode_tag = ""

    sys_cmd = "gnuplot -e experiment='\"%s\"' %s %s %s/radical-synapse-stats.plot" \
            % (dbname, mode_tag, bounds, os.path.dirname(__file__))

    print sys_cmd
    os.system(sys_cmd)



# ------------------------------------------------------------------------------
def handle_database (mongo, db, mode, dbname, cachedir, pname) :
    """
    For the given db, traverse collections
    """

    # FIXME: cache(dir) is not used

    print " +-- db   %s" % dbname

    cnames = db.collection_names()

    for cname in cnames :

        with open('./coll_%s.json' % cname, 'w') as dat:
            json = bson2json (list(db[cname].find ()))
            dat.write (pprint.pformat(json))

        if  mode == 'list' and not cname :
            print " | +-- coll %s" % cname

        elif  mode == 'remove' and not pname :
            try :
                db.drop_collection (cname)
                print "  removed collection %s" % cname
            except :
                pass # ignore errors

        else :
            handle_coll (mongo, db, mode, cname, pname)



# ------------------------------------------------------------------------------
def handle_coll (mongo, db, mode, cname, pname) :
    """
    For a given collection, traverse all documents
    """

    if 'indexes' in cname :
        return

    collection = db[cname]
    print " | +-- coll %s" % cname

    docs = collection.find ()

    for doc in docs :

        name = doc['_id']

        if  mode == 'list' and not pname :
            print " | | +-- doc  %s  [%3s] [%s] " \
                    % (name, len(doc['profiles'][0]['mem']['sequence']), 
                       doc['command_idx'])

        elif  mode == 'remove' :
            if (not pname) or (str(name)==str(pname)) :
                try :
                    collection.remove (name)
                    print "  removed document %s" % name
                except Exception as e:
                    pass # ignore errors

        else :
            if (not pname) or (str(name)==str(pname)) :
                handle_doc (collection, mode, doc)


# ------------------------------------------------------------------------------
def handle_doc (collection, mode, doc) :
    """
    And, surprise, for a given document, show it according to 'mode'
    """

    name = doc['_id']

    if  mode == 'list' :

        for key in doc :
            print " | | | +-- %s" % (key)

    elif  mode == 'tree' :
        print " | | +-- doc  %s" % (name)
        pprint.pprint (doc)
        print " | | +-- doc  %s  [%3s] [%s] " \
                % (name, len(doc['profiles'][0]['mem']['sequences']), 
                   doc['command_idx'])
        for key in doc :
            print " | | | +-- %s" % (key)

    elif  mode == 'dump' :
        print " | | +-- doc  %s" % (name)
        for key in doc :
            txt_in  = pprint.pformat (doc[key])
            txt_out = ""
            lnum    = 1
            for line in txt_in.split ('\n') :
                if  lnum != 1 :
                    txt_out += ' | | | |                '
                txt_out += line
                txt_out += '\n'
                lnum    += 1

            print " | | | +-- %-10s : %s" % (key, txt_out[:-1]) # remove last \n


# ------------------------------------------------------------------------------
#
if __name__ == '__main__' :

    import optparse
    parser = optparse.OptionParser (add_help_option=False)

    parser.add_option('-d', '--dbname',    dest='dbname')
    parser.add_option('-u', '--dburl',     dest='url')
    parser.add_option('-m', '--mode',      dest='mode')
    parser.add_option('-c', '--cachedir',  dest='cachedir')
    parser.add_option('-t', '--terminal',  dest='term')
    parser.add_option('-h', '--help',      dest='help',     action="store_true")
    parser.add_option('-f', '--filter',    dest='filters')
    parser.add_option('-x', '--command',   dest='cmd')
    parser.add_option('-s', '--syn-mode',  dest='syn_mode')

    options, args = parser.parse_args ()

    if  args :
        usage ("Too many arguments (%s)" % args)

    if  options.help :
        usage ()

    if  options.mode in ['help'] : 
        usage ()

    if  not options.mode :
        usage ("No mode specified")

    if  not options.url : 
        options.url = _DEFAULT_DBURL


    mode     = options.mode 
    cmd      = options.cmd 
    url      = options.url
    dbname   = options.dbname
    term     = options.term
    cachedir = options.cachedir
    filters  = options.filters
    syn_mode = options.syn_mode

    if not syn_mode or syn_mode == 'any':
        syn_mode = 'emu|pro|exe'

    syn_modes = syn_mode.split('|')

    if  not filters:
        filters = ''
    filters = filters.split(',')

    if  not term :
        term = "pdf,png"

    if  not cachedir :
        cachedir = os.getcwd ()

    if  not os.path.isdir (cachedir) :
        usage ("%s is no valid cachedir" % cachedir)

    url = ru.Url (options.url)

    if url.schema == 'mongodb':
        mongo, db, dbname, cname, pname = ru.mongodb_connect (str(url), url)
    else:
        mongo, db, dbname, cname, pname = (None, None, None, None, None)

    print "modes   : %s" % mode
    print "db url  : %s" % url
    print "db name : %s" % dbname
    print "cachedir: %s" % cachedir
    print "filter  : %s" % filters
    print "command : %s" % cmd
    print "syn.mode: %s" % syn_modes


    for m in mode.split (',') :

        if  m not in ['list', 'dump', 'tree', 'hist', 'sort', 'stat', 'plot', 'help'] : 
            usage ("Unsupported mode '%s'" % m)

        if  m in ['list']:
            # that is the only mode which does not need any db content
            list_databases (mongo, db, dbname, cachedir)

        else:

            if cmd:
              # docs = rs.utils.get_all_frames (cmd)
              # print len(docs)
              # sys.exit()

                db_json = rsu.get_profiles (cmd, mode=syn_modes)
                dbname  = os.path.basename (url.path)
            else:
                import json
                db_json = get_json (db, dbname, cachedir)

            if   m == 'tree' : tree_database  (db_json, dbname, filters, cachedir) 
            elif m == 'dump' : dump_database  (db_json, dbname, filters, cachedir)
            elif m == 'sort' : sort_database  (db_json, dbname, filters, cachedir)
            elif m == 'hist' : hist_database  (db_json, dbname, filters, cachedir)
            elif m == 'stat' : stat_database  (db_json, dbname, filters, cachedir)
            elif m == 'plot' : plot_database  (db_json, dbname, filters, term, syn_modes)
            elif m == 'help' : usage (noexit=True)
            else             : usage ("unknown mode '%s'" % mode)


# ------------------------------------------------------------------------------

