#!/usr/bin/env python

from __future__ import print_function
from __future__ import absolute_import
from __future__ import division

import sys
import re
import argparse
import ROOT
ROOT.PyConfig.IgnoreCommandLineOptions = True
from rat import ROOT


class SmartFormatter(argparse.HelpFormatter):
    '''
    Sneaky little argparse formatter to make help output for an argument
    with a fixed set of options to look nice.
    '''
    def _split_lines(self, text, width):
        if text.startswith('R|'):
            return text[2:].splitlines()
        # this is the RawTextHelpFormatter._split_lines
        return argparse.HelpFormatter._split_lines(self, text, width)

def dump_metadata(meta):
    '''
    Function to get a variety of metadata from the specified file.
    '''
    npass = meta.GetPassCount()
    if npass > 0:
        for ipass in range(0, npass):
            print('Pass {}:'.format(ipass))
            print('  Host                {} ({})'.format(meta.GetHostNames()[ipass],
                                                         meta.GetHostSystems()[ipass]))
            print('  RAT version         {}'.format(meta.GetVersions()[ipass]))
            print('  RAT revision        {}'.format(meta.GetRevisions()[ipass]))
            print('  ROOT version        {}'.format(meta.GetRootVersions()[ipass]))
            print('  GEANT4 version      {}'.format(meta.GetGeant4Versions()[ipass].strip()))
            print('  Contains MC         {}'.format(meta.GetContainsMCFlags()[ipass]))
            print('  Contains Data       {}'.format(meta.GetContainsDataFlags()[ipass]))
            print('  Events Generated    {}'.format(meta.GetEventsGeneratedCounts()[ipass]))
            print('  Events Stored       {}'.format(meta.GetEventsStoredCounts()[ipass]))

            db = meta.GetMetaDB(ipass)
            for ioc in range(0, db.GetOverrideCommandCount()):
                oc = db.GetOverrideCommand(ioc)
                print('  {:<19} {}'.format(oc.first.strip(), oc.second.strip()))
            for iof in range(0, db.GetFileCount()):
                print('  File {}'.format(db.GetFile(iof)))
    else:
        sys.exit("No metadata for any passes in ROOT file.")

def compare_metadata(meta, fname):
    '''
    Function to compare metadata.
    Compares metadata to the specified file.
    '''
    g = ROOT.TFile.Open(fname)
    if not g:
        return '0'
    if g.IsZombie():
        return '0'
    gmeta = g.Get('meta')
    if gmeta:
        npass = meta.GetPassCount()
        if npass != gmeta.GetPassCount():
            return '-'
        for ipass in range(0, npass):
            if meta.GetVersions()[ipass] != gmeta.GetVersions()[ipass]:
                return '-'
            if meta.GetRevisions()[ipass] != gmeta.GetRevisions()[ipass]:
                return '-'
            if meta.GetRootVersions()[ipass] != gmeta.GetRootVersions()[ipass]:
                return '-'
            if meta.GetGeant4Versions()[ipass] != gmeta.GetGeant4Versions()[ipass]:
                return '-'
            if meta.GetContainsMCFlags()[ipass] != gmeta.GetContainsMCFlags()[ipass]:
                return '-'
            if meta.GetContainsDataFlags()[ipass] != gmeta.GetContainsDataFlags()[ipass]:
                return '-'
            if meta.GetEventsStoredCounts()[ipass] != gmeta.GetEventsStoredCounts()[ipass]:
                return '-'
            if meta.GetEventsGeneratedCounts()[ipass] != gmeta.GetEventsGeneratedCounts()[ipass]:
                return '-'

            db = meta.GetMetaDB(ipass)
            gdb = gmeta.GetMetaDB(ipass)
            if db.GetOverrideCommandCount() != gdb.GetOverrideCommandCount():
                return '-'
            if db.GetFileCount() != gdb.GetFileCount():
                return '-'
            for ioc in range(0, db.GetOverrideCommandCount()):
                oc = db.GetOverrideCommand(ioc)
                match = False
                for i in range(0, gdb.GetOverrideCommandCount()):
                    goc = gdb.GetOverrideCommand(i)
                    if oc.first == goc.first:
                        if oc.second == goc.second:
                            match = True
                            break
                        return '-'
                if not match:
                    return '-'
            for iof in range(0, db.GetFileCount()):
                of = db.GetFile(iof)
                match = False
                for i in range(0, gdb.GetFileCount()):
                    if of == gdb.GetFile(i):
                        match = True
                        break
                if not match:
                    return '-'
    else:
        return '-'
    return '+'


def main():
    '''
    Main ratinfo function to call; handles argument parsing.
    '''
    parser = argparse.ArgumentParser(description='''Executable used to obtain information related to a given RAT file.'''
                                     , formatter_class=SmartFormatter)

    parser.add_argument('command', metavar='command', type=str, choices=['log', 'macro', 'meta', 'dbkeys', 'db'],
                        help=('R|Type of information to obtain about file.\n'
                              'Allowed commands are:\n'
                              '\n'
                              'log    Print log from RAT run which generated first file.\n'
                              'macro  Print RAT macro which generated first file.\n'
                              'meta   Print metadata (versions, overrides) for first file.\n'
                              '    If more files listed, compare metadata with the first file,\n'
                              '    listing matches with a +, and mismatches with a -.\n'
                              'dbkeys List keys in db TMap in first file.\n'
                              'db     Print tables in db TMap, with regular expressions to\n'
                              '    match keys as further arguments.\n'))
    parser.add_argument('file_path', type=str, help="Path to RAT file.")
    parser.add_argument('extra_info', type=str, nargs="*", help="For 'meta' and 'db' options, extra info can be provided here.")

    args = parser.parse_args()

    # we will detect and report errors ourselves
    ROOT.gErrorIgnoreLevel = ROOT.kFatal

    # For almost all commands, only care about one file.
    if len(args.extra_info) > 0 and args.command != 'meta' and args.command != 'db':
        print('ERROR: only input additional information if using the "meta" or "db" commands.')
        sys.exit(2)

    # Open file; do sanity checks
    f = ROOT.TFile.Open(args.file_path)
    if not f:
        sys.exit("No such file {}.".format(args.file_path))

    if f.IsZombie():
        sys.exit("Failed to open file {}.".format(args.file_path))

    # Run relevant command as chosen by user
    if args.command == 'log':
        log = f.Get("log")
        if log:
            print(log.GetString())
        else:
            sys.exit("No log present in ROOT file.")
    elif args.command == 'macro':
        macro = f.Get('macro')
        if macro:
            print(macro.GetString())
        else:
            sys.exit("No macro present in ROOT file.")
    elif args.command == 'dbkeys':
        db = f.Get('db')
        if db:
            idb = db.MakeIterator()
            while idb.Next():
                print(idb.Key())
        else:
            sys.exit("No db present in ROOT file.")
    elif args.command == 'db':
        db = f.Get('db')
        if db:
            idb = db.MakeIterator()
            for re_arg in args.extra_info:
                p = re.compile(re_arg)
                idb.Reset()
                while idb.Next():
                    if p.match(idb.Key().GetName()):
                        print('{} = {}'.format(idb.Key(), idb.Value()))
        else:
            sys.exit("No db present in ROOT file.")
    elif args.command == 'meta':
        meta = f.Get('meta')
        if meta:
            dump_metadata(meta)
            if len(args.extra_info) > 0:
                print('+ {}'.format(args.file_path))
                for arg in args.extra_info:
                    c = compare_metadata(meta, arg)
                    print('{} {}'.format(c, arg))
        else:
            sys.exit("No metadata present in ROOT file.")
    else:
        sys.exit("Invalid command: {}".format(args.command))


if __name__ == '__main__':
    main()