from __future__ import absolute_import
import os
import copy
import numpy as np
from collections import defaultdict

# get is_CASA6 and is_python3
from casatasks.private.casa_transition import *
if is_CASA6:
    from casatasks import casalog
    from casatools import ms, quanta, table, agentflagger
    from .mstools import write_history
    from . import flaghelper as fh

    qalocal = quanta( )
    tblocal = table( )
else:
    from taskinit import casalog, casac, qa, tb
    from mstools import write_history
    import flaghelper as fh

    # naked tool constructors ala CASA6
    agentflagger = casac.agentflagger
    ms = casac.ms

    # not really local
    qalocal = qa
    tblocal = tb

# common function to use to get a dictionary item iterator
if is_python3:
    def lociteritems(adict):
        return adict.items()
else:
    def lociteritems(adict):
        return adict.iteritems()

def flagcmd(
    vis=None,
    inpmode=None,
    inpfile=None,
    tablerows=None,
    reason=None,
    useapplied=None,
    tbuff=None,
    ants=None,
    action=None,
    flagbackup=None,
    clearall=None,
    rowlist=None,
    plotfile=None,
    savepars=None,
    outfile=None,
    overwrite=None
    ):

    #
    # Task flagcmd
    #    Reads flag commands from file or string and applies to MS

    try:
        from xml.dom import minidom
    except Exception as exc:
        raise ImportError('Failed to load xml.dom.minidom into python: {}'.format(exc))

    casalog.origin('flagcmd')

    aflocal = agentflagger()
    mslocal = ms()
    mslocal2 = ms()           

    try:
        # Use a default ntime to open the MS. The user-set ntime will be
        # used in the tool later
        ntime = 0.0

        # Open the MS and attach it to the tool
        if (type(vis) == str) & os.path.exists(vis):
            aflocal.open(vis, ntime)
        else:
            raise ValueError( 'Visibility data set not found - please verify the name' )

        # Check if vis is a cal table:
        # typevis = 1 --> cal table
        # typevis = 0 --> MS
        # typevis = 2 --> MMS
        iscal = False
        typevis = fh.isCalTable(vis)
        if typevis == 1:
            iscal = True
            
            if action != 'apply' and action != 'list':
                raise ValueError( 'Unsupported action for cal tables. Only apply and list are supported.' )
            
            if inpmode == 'table' and isinstance(inpfile, str) and inpfile == '':
                raise ValueError( 'inpmode=\'table\' needs an MS as inpfile' )
                                 
            flagcmds = {}       
            if inpmode == 'table' and fh.isCalTable(inpfile) == 0:
                # Read flag cmds from the MS
                flagcmds = readCalCmds(vis, inpfile, [], tablerows, reason, useapplied)
                listmode = 'cmd'
                
            elif inpmode == 'list':
                # Read flag cmds from a list
                flagcmds = readCalCmds(vis, '', inpfile, [], reason, True)  
                listmode = ''              
            else:
                raise ValueError( 'Unsupported inpmode for cal tables' )
                        
            # Apply flag cmds
            if len(flagcmds.keys( )) == 0:
                raise RuntimeError( 'There are no unapplied flags in the input. '\
                                 'Set useapplied=True to also use the previously-applied flags.' )
            
            # List flags on the screen/logger
            if action == 'list':
                casalog.post('Executing action = list')
                listFlagCmd(myflags=flagcmds, myoutfile='', listmode=listmode)
                
            elif action == 'apply':
                casalog.post('Executing action = apply')
                applyCalCmds(aflocal, vis, flagcmds, tablerows, flagbackup, outfile)
                
            # Save the flag cmds to an output file
            if savepars:
                if not overwrite and os.path.exists(outfile):
                    raise ValueError( 'You have set overwrite to False. Remove %s before saving the flag commands'%outfile )
                    
                fh.writeFlagCommands(vis, flagcmds, False, '', outfile, False)
                                                                
        else:
            # Input vis is an MS

            # Get overall MS time range for later use (if needed)
            mslocal2.open(vis)
            timd = mslocal2.range(['time'])
            mslocal2.close()
            if len(timd) != 0:
                ms_startmjds = timd['time'][0]
                ms_endmjds = timd['time'][1]
                t = qalocal.quantity(ms_startmjds, 's')
                t1sdata = t['value']
                ms_starttime = qalocal.time(t, form='ymd', prec=9)[0][0]
                t = qalocal.quantity(ms_endmjds, 's')
                t2sdata = t['value']
                ms_endtime = qalocal.time(t, form='ymd', prec=9)[0]
                # NOTE: could also use values from OBSERVATION table col TIME_RANGE
                casalog.post('MS spans timerange ' + ms_starttime + ' to '
                             + ms_endtime)
    
            myflagcmd = {}
    
            if action == 'clear':
                casalog.post('Action "clear" will disregard inpmode (no reading)')
                # Clear flag commands from FLAG_CMD in vis
                msfile = vis
    
                if clearall:
                    casalog.post('Deleting all rows from FLAG_CMD in MS '
                                 + msfile)
                    clearFlagCmd(msfile, myrowlist=rowlist)
                else:
                    casalog.post('Safety Mode: you chose not to set clearall=True, no action'
                                 )
                return
            
            elif inpmode == 'table':
    
                casalog.post('Reading from FLAG_CMD table')
                # Read from FLAG_CMD table into command list
                if inpfile == '':
                    msfile = vis
                else:
                    msfile = inpfile
    
                myflagcmd = readFromTable( msfile, myflagrows=tablerows,
                                           useapplied=useapplied, myreason=reason)
    
                listmode = 'table'
            elif inpmode == 'list':
                if action == 'unapply':
                    casalog.post("The unapply action can only be used with inpmode='table'",'WARN')
                    casalog.post("Save the commands to the FLAG_CMD table before using unapply",'WARN')
                    raise ValueError( "Unsupported action='unapply' for inpmode='list'" )
    
                # ##### TO DO: take time ranges calculation into account ??????
                # Parse the input file
                try:            
                    # Input commands are given in a list
                    if isinstance(inpfile, list):
                        
                        # It is a list of input files
                        if os.path.isfile(inpfile[0]):
                            flaglist = []
                            for ifile in inpfile:
                                casalog.post('Will read commands from the file '+ifile)                    
                                flaglist = flaglist + fh.readFile(ifile)
                            
                            myflagcmd = fh.parseDictionary(flaglist, reason)
                        
                        # It is a list of strings with flag commands
                        else:
                            casalog.post('Will read commands from a Python list')
                            myflagcmd = fh.parseDictionary(inpfile, reason)
                        listmode = ''
                        
                    # Input commands are given in a file
                    elif isinstance(inpfile, str):
                        
                        if inpfile == '':
                             casalog.post('Input file is empty', 'ERROR')
                             
                        casalog.post('Will read commands from the file '+inpfile)
                        flaglist = fh.readFile(inpfile)
                        casalog.post('%s'%flaglist,'DEBUG')
                        
                        myflagcmd = fh.parseDictionary(flaglist, reason)
                        listmode = 'file'
                    
                    else:
                        casalog.post('Input type is not supported', 'ERROR')
                        
                    listmode = 'list'
                    casalog.post('%s'%myflagcmd,'DEBUG1')
                                                                                            
                except Exception as instance:
                    raise Exception( 'Error reading the input list: {} '.format(instance))
                
    
            elif inpmode == 'xml':
    
                casalog.post('Reading from Flag.xml')
                if action == 'unapply':
                    casalog.post("The unapply action can only be used with inpmode='table' or 'list';'",'WARN')
                    casalog.post("save the commands to the FLAG_CMD table before using unapply.",'WARN')
                    raise ValueError( "Unsupported action='unapply' for inpmode='xml'" )

                # Read from Flag.xml (also needs Antenna.xml)
                if inpfile == '':
                    flagtable = vis
                else:
                    flagtable = inpfile
    
                # Actually parse table. Fail if Flag.xml or Antenna.xml is not found
                try:
                    myflags = fh.parseXML(flagtable, mytbuff=tbuff)            
                except Exception as exc:
                    raise RuntimeError('Error while parsing XML: {}'.format(exc))
    
                casalog.post('%s' % myflags, 'DEBUG')
    
                # Construct flags per antenna, selecting by reason if desired
                if ants != '' or reason != 'any':
                    myflagcmd = selectXMLFlags(myflags, myantenna=ants,myreason=reason)
                else:
                    myflagcmd = myflags
    
#                listmode = 'online'
                listmode = 'xml'
                
            else:
                raise ValueError( 'Input type is not supported' )

            # Before performing any action on the flag cmds, check them! 
            vrows = list(myflagcmd.keys())
            if len(vrows) == 0:
                raise RuntimeError( 'There are no unapplied flags in the input. Set useapplied=True to also use the previously-applied flags.' )
            else:
                casalog.post('Read ' + str(len(vrows))
                             + ' lines from input')
                
            casalog.post('Flagcmd dictionary is: %s'%myflagcmd, 'DEBUG1')
              
            #
            # ACTION to perform on input file
            #
            casalog.post('Executing action = ' + action)
    
            # List the flag commands from inpfile on the screen
            # and save them or not to outfile
            if action == 'list':
    
                # List the flag cmds on the screen
                listFlagCommands(myflagcmd, listmode=listmode)

                # Save the flag cmds to the outfile
                if savepars:
                    # These cmds came from the internal FLAG_CMD, only list on the screen
                    if outfile == '':
                        if inpmode == 'table' and inpfile == '':
                            pass
                        else:
                            casalog.post('Saving commands to FLAG_CMD')
                            fh.writeFlagCommands(vis, myflagcmd, False, 
                                                 '', '', True)
                    elif not overwrite and os.path.exists(outfile):
                        raise RuntimeError( 'You have set overwrite to False. Remove %s before saving the flag commands'%outfile )

                    else:
                        casalog.post('Saving commands to ' + outfile)
                        fh.writeFlagCommands(vis, myflagcmd, False, '', outfile, False)
                        
            elif action == 'apply' or action == 'unapply':
    
                # Apply/Unapply the flag commands to the data
                apply = True

                # Select the data
    
                # Select a loose union of the data selection from the list.
                # The loose union will be calculated for field and spw only.
                # Antenna, correlation and timerange should be handled by the agent
                unionpars = {}
#                 if len(vrows) > 1:
#                     unionpars = fh.parseUnion(vis, myflagcmd)
#                     if len(unionpars.keys()) > 0:
#                         casalog.post('Pre-selecting a subset of the MS: ')
#                         casalog.post('%s' % unionpars)
#                     else:
#                         casalog.post('Iterating through the entire MS')
    
#                elif len(vrows) == 1:
                if len(vrows) == 1:
    
                # Get all the selection parameters, but set correlation to ''
                    # if the table was selected by row, we need to
                    # know what is the key number in the dictionary
                    cmd0 = myflagcmd[vrows[0]]['command']
                    unionpars = fh.parseSelectionPars(cmd0)
                    casalog.post('The selected subset of the MS will be: ')
                    casalog.post('%s' % unionpars)
    
                aflocal.selectdata(unionpars)
    
                # Parse the agents parameters
                if action == 'unapply':
                    apply = False
    
                casalog.post('Parse the parameters for the agents')
                fh.parseAgents(aflocal, myflagcmd, [], apply, True, '')
    
                # Initialize the Agents
                aflocal.init()
    
                # Backup the flags before running
                if flagbackup:
                    fh.backupFlags(aflocal, msfile='', prename='flagcmd')
    
                # Run the tool
                aflocal.run(True)
    
                aflocal.done()
    
                # Update the APPLIED column
                if savepars:
                    # These flags came from internal FLAG_CMD. Always update APPLIED
                    if outfile == '':
                        if inpmode == 'table' and inpfile == '':
                            updateTable(vis, mycol='APPLIED',
                                    myval=apply, myrowlist=vrows)
                        else:
                            # save to FLAG_CMD
                            casalog.post('Saving commands to FLAG_CMD')
                            fh.writeFlagCommands(vis, myflagcmd, apply, '', 
                                                 '', True)
                    # Save to a file
                    else:                    
                        # Still need to update APPLIED column
                        if inpmode == 'table' and inpfile == '':
                            updateTable(vis, mycol='APPLIED',
                                    myval=apply, myrowlist=vrows)

                        if not overwrite and os.path.exists(outfile):
                            raise RuntimeError( 'You have set overwrite to False. Remove %s before saving the flag commands'%outfile )

                        else:
                            casalog.post('Saving commands to file '+ outfile)
                            fh.writeFlagCommands(vis, myflagcmd, apply, '', outfile, False)
                            
                # Do not save cmds but maybe update APPLIED
                else:
                    if inpmode == 'table' and inpfile == '':
                        updateTable(vis, mycol='APPLIED', myval=apply,
                                    myrowlist=vrows)
                    
            elif action == 'plot':
    
                keylist = myflagcmd.keys()
                if len(keylist) > 0:
                    # Plot flag commands from FLAG_CMD or xml
                    casalog.post('Warning: will only reliably plot individual per-antenna flags'
                                 )
                    fns = newplotflags(myflagcmd, plotfile, t1sdata, t2sdata)
                    return {'plotfiles': fns}

                else:
                    casalog.post('Warning: empty flag dictionary, nothing to plot'
                                 )
            elif action == 'extract':
                # Make the command dictionary a string again
                outdict = copy.deepcopy(myflagcmd)
                for key in myflagcmd.keys():
                    cmddict = myflagcmd[key]['command']
                    cmdline = ""
                    for k,v in lociteritems(cmddict):
                        cmdline = cmdline + k + '=' + str(v) + ' '
                    cmdline.rstrip()
                    outdict[key]['command'] = cmdline
                    
                casalog.post('Returning extracted dictionary')
                return outdict
            
    
    finally:
        aflocal.done()
        
    # Write history only to action='apply' or 'unapply'
    # write history
    if not iscal and (action == 'apply' or action == 'unapply'):
        try:
            param_names = flagcmd.__code__.co_varnames[:flagcmd.__code__.co_argcount]
            if is_python3:
                vars = locals( )
                param_vals = [vars[p] for p in param_names]
            else:
                param_vals = [eval(p) for p in param_names]

            write_history(mslocal, vis, 'flagcmd', param_names,
                          param_vals, casalog)

        except Exception as instance:
            casalog.post("*** Error \'%s\' updating HISTORY" % (instance),
                         'WARN')


# ************************************************************************
#                    Helper Functions
# ************************************************************************


def readFromTable(
    msfile,
    myflagrows=[],
    useapplied=True,
    myreason='any',
    ):
    '''Read flag commands from rows of the FLAG_CMD table of msfile
    If useapplied=False then include only rows with APPLIED=False
    If myreason is anything other than '', then select on that'''

    #
    # Return flagcmd structure:
    #
    # The flagcmd key is the integer row number from FLAG_CMD
    #
    #   Dictionary structure:
    #   key : 'id' (string)
    #         'mode' (string)         flag mode '','clip','shadow','quack'
    #         'antenna' (string)
    #         'timerange' (string)
    #         'reason' (string)
    #         'time' (float)          in mjd seconds
    #         'interval' (float)      in mjd seconds
    #         'cmd' (string)          string (for COMMAND col in FLAG_CMD)
    #         'type' (string)         'FLAG' / 'UNFLAG'
    #         'applied' (bool)        set to True here on read-in
    #         'level' (int)           set to 0 here on read-in
    #         'severity' (int)        set to 0 here on read-in

    # Open and read columns from FLAG_CMD
    mstable = os.path.join(msfile,'FLAG_CMD')

    # Note, tb.getcol doesn't allow random row access, read all

    try:
        tblocal.open(mstable)
        f_time = tblocal.getcol('TIME')
        f_interval = tblocal.getcol('INTERVAL')
        f_type = tblocal.getcol('TYPE')
        f_reas = tblocal.getcol('REASON')
        f_level = tblocal.getcol('LEVEL')
        f_severity = tblocal.getcol('SEVERITY')
        f_applied = tblocal.getcol('APPLIED')
        f_cmd = tblocal.getcol('COMMAND')
        tblocal.close()
    except:
        casalog.post('Error reading table ' + mstable, 'ERROR')
        raise Exception

    nrows = len(f_time)

    myreaslist = []

    # Parse myreason
    if type(myreason) == str:
        if myreason != 'any':
            myreaslist.append(myreason)
    elif type(myreason) == list:
        myreaslist = myreason
    else:
        casalog.post('Cannot read reason; it contains unknown variable types'
                     , 'ERROR')
        return
    
    myflagcmd = {}

    if nrows > 0:
        nflagd = 0
        if len(myflagrows) > 0:
            rowlist = myflagrows
        else:
            rowlist = list(range(nrows))
        # Prune rows if needed
        if not useapplied:
            rowl = []
            for i in rowlist:
                if not f_applied[i]:
                    rowl.append(i)
            rowlist = rowl
        if len(myreaslist) > 0:
            rowl = []
            for i in rowlist:
                if myreaslist.count(f_reas[i]) > 0:
                    rowl.append(i)
            rowlist = rowl
            
        # Define the way to parse the strings
        myParser = fh.Parser(' ', '=')
        
        for i in rowlist:
            flagd = {}
            cmd = f_cmd[i]
            if cmd == '':
                casalog.post('Ignoring empty COMMAND string', 'WARN')
                continue

            # Extract antenna and timerange strings from cmd
            flagd['id'] = str(i)
            flagd['antenna'] = ''
            flagd['mode'] = ''
            flagd['timerange'] = ''
            flagd['time'] = f_time[i]
            flagd['interval'] = f_interval[i]
            flagd['type'] = f_type[i]
            flagd['reason'] = f_reas[i]
            flagd['level'] = f_level[i]
            flagd['severity'] = f_severity[i]
            flagd['applied'] = f_applied[i]
            
            # If shadow, remove the addantenna dictionary
            if cmd.__contains__('shadow') \
                and cmd.__contains__('addantenna'):
                i0 = cmd.rfind('addantenna')
                if cmd[i0 + 11] == '{':
                    # It is a dictionary. Remove it from line
                    i1 = cmd.rfind('}')
                    antpar = cmd[i0 + 11:i1 + 1]
                    temp = cmd[i0:i1 + 1]
                    newcmd = cmd.replace(temp, '')
                    antpardict = fh.convertStringToDict(antpar)
                    flagd['addantenna'] = antpardict
                    cmd = newcmd

            # Get a dictionary without type evaluation
            preparsing = myParser.parseNoEval(cmd)
            
            # Evaluate the types into a new dictionary
            parsed = fh.evaluateParameters(preparsing)

            flagd['command'] = parsed
            
            if 'mode' in parsed:
                flagd['mode'] = parsed['mode']
            if 'timerange' in parsed:
                flagd['timerange'] = parsed['timerange']
            if 'antenna' in parsed:
                flagd['antenna'] = parsed['antenna']
            if 'id' in parsed:
                flagd['id'] = parsed['id']

            # Keep original key index, might need this later
            myflagcmd[i] = flagd
            nflagd += 1

    else:
        casalog.post('FLAG_CMD table in %s is empty, no flags extracted'
                      % msfile, 'WARN')

    return myflagcmd

def readFromCmd(cmdlist, ms_startmjds, ms_endmjds):
    '''Read the parameters from a list of commands'''

    # Read a list of strings and return a dictionary of parameters
    myflagd = {}
    nrows = len(cmdlist)
    if nrows == 0:
        casalog.post('WARNING: empty flag command list', 'WARN')
        return myflagd

    t = qalocal.quantity(ms_startmjds, 's')
    ms_startdate = qalocal.time(t, form=['ymd', 'no_time'])[0]
    t0 = qalocal.totime(ms_startdate + '/00:00:00.0')
    # t0d = qalocal.convert(t0,'d')
    t0s = qalocal.convert(t0, 's')

    ncmds = 0
    for i in range(nrows):
        cmdstr = cmdlist[i]
        # break string into key=val sets
        keyvlist = cmdstr.split()
        if len(keyvlist) > 0:
            ant = ''
            timstr = ''
            tim = 0.5 * (ms_startmjds + ms_endmjds)
            intvl = ms_endmjds - ms_startmjds
            reas = ''
            cmd = ''
            fid = str(i)
            typ = 'FLAG'
            appl = False
            levl = 0
            sevr = 0
            fmode = ''
            for keyv in keyvlist:
                # check for comment character #
                if keyv.count('#') > 0:
                    # break out of loop parsing keyvals
                    break
                try:
                    (xkey, val) = keyv.split('=')
                except:
                    casalog.post('Not a key=val pair: ' + keyv, 'WARN')
                    break
                xval = val
                # Use eval to deal with conversion from string
                # xval = eval(val)
                # strip quotes from value (if still a string)
                if type(xval) == str:
                    if xval.count("'") > 0:
                        xval = xval.strip("'")
                    if xval.count('"') > 0:
                        xval = xval.strip('"')

                # Strip these out of command string
                if xkey == 'reason':
                    reas = xval
                elif xkey == 'applied':
                    appl = False
                    if xval == 'True':
                        appl = True
                elif xkey == 'level':
                    levl = int(xval)
                elif xkey == 'severity':
                    sevr = int(xval)
                elif xkey == 'time':
                    tim = xval
                elif xkey == 'interval':
                    intvl = xval
                else:
                    # Extract (but keep in string)
                    if xkey == 'timerange':
                        timstr = xval
                        # Extract TIME,INTERVAL
                        try:
                            (startstr, endstr) = timstr.split('~')
                        except:
                            if timstr.count('~') == 0:
                            # casalog.post('Assuming a single start time ')
                                startstr = timstr
                                endstr = timstr
                            else:
                                raise Exception("too may ~'s. Not a start~end range. Error "
                                                "parsing " + timstr )
                        t = qalocal.totime(startstr)
                        starts = qalocal.convert(t, 's')
                        if starts['value'] < 1.E6:
                            # assume a time offset from ref
                            starts = qalocal.add(t0s, starts)
                        startmjds = starts['value']
                        if endstr == '':
                            endstr = startstr
                        t = qalocal.totime(endstr)
                        ends = qalocal.convert(t, 's')
                        if ends['value'] < 1.E6:
                            # assume a time offset from ref
                            ends = qalocal.add(t0s, ends)
                        endmjds = ends['value']
                        tim = 0.5 * (startmjds + endmjds)
                        intvl = endmjds - startmjds
                    elif xkey == 'antenna':

                        ant = xval
                    elif xkey == 'id':
                        fid = xval
                    elif xkey == 'unflag':
                        if xval == 'True':
                            typ = 'UNFLAG'
                    elif xkey == 'mode':
                        fmode = xval
                    cmd = cmd + ' ' + keyv
            # Done parsing keyvals
            # Make sure there is a non-blank command string after reason/id extraction
            if cmd != '':
                flagd = {}
                flagd['id'] = fid
                flagd['mode'] = fmode
                flagd['antenna'] = ant
                flagd['timerange'] = timstr
                flagd['reason'] = reas
                flagd['command'] = cmd
                flagd['time'] = tim
                flagd['interval'] = intvl
                flagd['type'] = typ
                flagd['level'] = levl
                flagd['severity'] = sevr
                flagd['applied'] = appl
                # Insert into main dictionary
                myflagd[ncmds] = flagd
                ncmds += 1

    casalog.post('Parsed ' + str(ncmds) + ' flag command strings')

    return myflagd


def readFromFile(
    cmdlist,
    ms_startmjds,
    ms_endmjds,
    myreason='',
    ):
    '''Parse list of flag command strings and return dictionary of flagcmds
    Inputs:
       cmdlist (list,string) list of command strings (default for TIME,INTERVAL)
       ms_startmjds (float)  starting mjd (sec) of MS (default for TIME,INTERVAL)
       ms_endmjds (float)    ending mjd (sec) of MS'''

#
#   Usage: myflagcmd = getflags(cmdlist)
#
#   Dictionary structure:
#   fid : 'id' (string)
#         'mode' (string)         flag mode '','clip','shadow','quack'
#         'antenna' (string)
#         'timerange' (string)
#         'reason' (string)
#         'time' (float)          in mjd seconds
#         'interval' (float)      in mjd seconds
#         'cmd' (string)          string (for COMMAND col in FLAG_CMD)
#         'type' (string)         'FLAG' / 'UNFLAG'
#         'applied' (bool)        set to True here on read-in
#         'level' (int)           set to 0 here on read-in
#         'severity' (int)        set to 0 here on read-in
#
# v3.2 Updated STM 2010-12-03 (3.2.0) handle comments # again
# v3.2 Updated STM 2010-12-08 (3.2.0) bug fixes in flagsort use, parsing
# v3.3 Updated STM 2010-12-20 (3.2.0) bug fixes parsing errors
#

    myflagd = {}
    nrows = len(cmdlist)
    if nrows == 0:
        casalog.post('WARNING: empty flag command list', 'WARN')
        return myflagd

    # Parse the reason
    reasonlist = []
    if type(myreason) == str:
        if myreason != '':
            reasonlist.append(myreason)
    elif type(myreason) == list:
        reasonlist = myreason
    else:
        casalog.post('Cannot read reason; it contains unknown variable types'
                     , 'ERROR')
        return

    t = qalocal.quantity(ms_startmjds, 's')
    ms_startdate = qalocal.time(t, form=['ymd', 'no_time'])[0]
    t0 = qalocal.totime(ms_startdate + '/00:00:00.0')
    # t0d = qalocal.convert(t0,'d')
    t0s = qalocal.convert(t0, 's')

    rowlist = []

    # IMPLEMENT THIS LATER
    # First select by reason. Simple selection...
#    if len(reasonlist) > 0:
#        for i in range(nrows):
#            cmdstr = cmdlist[i]
#            keyvlist = cmdstr.split()
#            if len(keyvlist) > 0:
#                for keyv in keyvlist:
#                    (xkey, xval) = keyv.split('=')
#
#                    if type(xval) == str:
#                        if xval.count("'") > 0:
#                            xval = xval.strip("'")
#                        if xval.count('"') > 0:
#                            xval = xval.strip('"')
#
#                    if xkey == 'reason':
#                        if reasonlist.count(xval) > 0
#    else:
    rowlist = list(range(nrows))

    # Now read the only the commands from the file that satisfies the reason selection

    ncmds = 0
#    for i in range(nrows):
    for i in rowlist:
        cmdstr = cmdlist[i]

        # break string into key=val sets
        keyvlist = cmdstr.split()
        if len(keyvlist) > 0:
            ant = ''
            timstr = ''
            tim = 0.5 * (ms_startmjds + ms_endmjds)
            intvl = ms_endmjds - ms_startmjds
            reas = ''
            cmd = ''
            fid = str(i)
            typ = 'FLAG'
            appl = False
            levl = 0
            sevr = 0
            fmode = ''
            for keyv in keyvlist:
                # check for comment character #
                if keyv.count('#') > 0:
                    # break out of loop parsing keyvals
                    break
                try:
                    (xkey, val) = keyv.split('=')
                except:
                    casalog.post('Not a key=val pair: ' + keyv, "WARN")
                    break
                xval = val
                # Use eval to deal with conversion from string
                # xval = eval(val)
                # strip quotes from value (if still a string)
                if type(xval) == str:
                    if xval.count("'") > 0:
                        xval = xval.strip("'")
                    if xval.count('"') > 0:
                        xval = xval.strip('"')

                # Strip these out of command string
                if xkey == 'reason':
                    reas = xval
                elif xkey == 'applied':

                    appl = False
                    if xval == 'True':
                        appl = True
                elif xkey == 'level':
                    levl = int(xval)
                elif xkey == 'severity':
                    sevr = int(xval)
                elif xkey == 'time':
                    tim = xval
                elif xkey == 'interval':
                    intvl = xval
                else:
                    # Extract (but keep in string)
                    if xkey == 'timerange':
                        timstr = xval
                        # Extract TIME,INTERVAL
                        try:
                            (startstr, endstr) = timstr.split('~')
                        except:
                            if timstr.count('~') == 0:
                            # casalog.post('Assuming a single start time ')
                                startstr = timstr
                                endstr = timstr
                            else:
                                raise Exception( "too may ~'s. Not a start~end range. Error "
                                                 "parsing " + timstr )
                        t = qalocal.totime(startstr)
                        starts = qalocal.convert(t, 's')
                        if starts['value'] < 1.E6:
                            # assume a time offset from ref
                            starts = qalocal.add(t0s, starts)
                        startmjds = starts['value']
                        if endstr == '':
                            endstr = startstr
                        t = qalocal.totime(endstr)
                        ends = qalocal.convert(t, 's')
                        if ends['value'] < 1.E6:
                            # assume a time offset from ref
                            ends = qalocal.add(t0s, ends)
                        endmjds = ends['value']
                        tim = 0.5 * (startmjds + endmjds)
                        intvl = endmjds - startmjds
                    elif xkey == 'antenna':

                        ant = xval
                    elif xkey == 'id':
                        fid = xval
                    elif xkey == 'unflag':
                        if xval == 'True':
                            typ = 'UNFLAG'
                    elif xkey == 'mode':
                        fmode = xval
                    cmd = cmd + ' ' + keyv
            # Done parsing keyvals
            # Make sure there is a non-blank command string after reason/id extraction
            if cmd != '':
                flagd = {}
                flagd['id'] = fid
                flagd['mode'] = fmode
                flagd['antenna'] = ant
                flagd['timerange'] = timstr
                flagd['reason'] = reas
                flagd['command'] = cmd
                flagd['time'] = tim
                flagd['interval'] = intvl
                flagd['type'] = typ
                flagd['level'] = levl
                flagd['severity'] = sevr
                flagd['applied'] = appl
                # Insert into main dictionary
                myflagd[ncmds] = flagd
                ncmds += 1

    casalog.post('Parsed ' + str(ncmds) + ' flag command strings')

    return myflagd


def updateTable(
    msfile,
    mycol='',
    myval=None,
    myrowlist=[],
    ):
    '''Update commands in myrowlist of the FLAG_CMD table of msfile    
       Usage: updateflagcmd(msfile,myrow,mycol,myval)'''

    # Example:
    #
    #    updateflagcmd(msfile,mycol='APPLIED',myval=True)
    #       Mark all rows as APPLIED=True
    #
    #    updateflagcmd(msfile,mycol='APPLIED',myval=True,myrowlist=[0,1,2])
    #       Mark rows 0,1,2 as APPLIED=True
    #

    if mycol == '':
        casalog.post('WARNING: No column to was specified to update; doing nothing'
                     , 'WARN')
        return

    # Open and read columns from FLAG_CMD
    mstable = os.path.join(msfile,'FLAG_CMD')
    try:
        tblocal.open(mstable, nomodify=False)
    except:
        raise Exception( 'Error opening table ' + mstable )

    nrows = int(tblocal.nrows())

    # Check against allowed colnames
    colnames = tblocal.colnames()
    if colnames.count(mycol) < 1:
        casalog.post('Error: column mycol=' + mycol + ' not one of: '
                     + str(colnames))
        return

    nlist = len(myrowlist)

    if nlist > 0:
        rowlist = myrowlist
    else:

        rowlist = list(range(nrows))
        nlist = nrows

    if nlist > 0:
        try:
            tblocal.putcell(mycol, rowlist, myval)
        except:
            raise Exception( 'Error updating FLAG_CMD column ' + mycol \
                + ' to value ' + str(myval) )

        casalog.post('Updated ' + str(nlist)
                     + ' rows of FLAG_CMD table in MS')
    tblocal.close()

def listFlagCommands(myflags=None, listmode=''):
    '''List flags from MS or a file. The flags are read from
       a dictionary created by fh.parseDictionary()
    Format according to listmode:
        =''          do nothing
        ='list'      Format for flag command strings
        ='table'     Format for FLAG_CMD flags
        ='xml'    Format for online flags'''

    #
    #   Dictionary structure:
    #   fid : 'id' (string)
    #         'mode' (string)         flag mode '','clip','shadow','quack'
    #         'antenna' (string)
    #         'timerange' (string)
    #         'reason' (string)
    #         'time' (float)          in mjd seconds
    #         'interval' (float)      in mjd seconds
    #         'cmd' (string)          string (for COMMAND col in FLAG_CMD)
    #         'type' (string)         'FLAG' / 'UNFLAG'
    #         'applied' (bool)
    #         'level' (int)
    #         'severity' (int)
    #


    # list to logger and screen
    if listmode == 'table':
        phdr = '%-8s %-32s %-7s %s' % (
            'Row',
            'Reason',
            'Applied',
            'Command'
            )
        casalog.post(phdr)
        mydash=80*'-'
        casalog.post('%-80s'%mydash)
        for k in myflags.keys():
#            time = myflags[k]['TIME']
            row = myflags[k]['id']
            reason = myflags[k]['reason']
            applied = myflags[k]['applied']
            
            cmddict = myflags[k]['command']
            cmdline = ""
            for key,val in lociteritems(cmddict):
                cmdstr = ""
                if isinstance(val, str):
                    # Add quotes to string values
                    cmdstr = "'"+val+"'"
                    val = cmdstr
                cmdline = cmdline + key + '=' + str(val) + ' '
            
            # Print to logger
            pstr = '%-8s %-32s %-7s %s' % (
                row, reason,applied,cmdline)
            casalog.post(pstr)

    elif listmode == 'list':
        phdr = '%-8s %-32s %s' % ('Key', 'Reason', 'Command')
        casalog.post(phdr)
        mydash=80*'-'
        casalog.post('%-80s'%mydash)
        for k in myflags.keys():
            cmddict = myflags[k]['command']
            reason = myflags[k]['reason']
            if 'reason' in cmddict:
                cmddict.pop('reason')
                
            cmdline = ""
            for key,val in lociteritems(cmddict):
                cmdstr = ""
                if isinstance(val, str):
                    # Add quotes to string values
                    cmdstr = "'"+val+"'"
                    val = cmdstr
                cmdline = cmdline + key + '=' + str(val) + ' '
            
            # Print to logger
            pstr = '%-8s %-32s %s' % (k, reason, cmdline)
            casalog.post(pstr)
            
    elif listmode == 'xml':
        phdr = '%-8s %-8s %-48s %-32s' % ('Key', 'Antenna',
                'Timerange', 'Reason')
        casalog.post(phdr)
        mydash=80*'-'
        casalog.post('%-80s'%mydash)
        for k in myflags.keys():
            reason = ''
            antenna = ''
            timerange = ''
            cmddict = myflags[k]
            if 'reason' in cmddict:
                reason = cmddict['reason']
            if 'antenna' in cmddict:
                antenna = cmddict['antenna']
            if 'timerange' in cmddict:
                timerange = cmddict['timerange']
                
            pstr = '%-8s %-8s %-48s %-32s' % (k, antenna,timerange,reason)
            casalog.post(pstr)

    return

def listFlagCmd(
    myflags=None,
    myantenna='',
    myreason='',
    myoutfile='',
    listmode='',
    ):
    '''List flags in myflags dictionary
    
    Format according to listmode:
        =''          do nothing
        ='file'      Format for flag command strings
        ='cmd'       Format for FLAG_CMD flags
        ='online'    Format for online flags'''

    #
    #   Dictionary structure:
    #   fid : 'id' (string)
    #         'mode' (string)         flag mode '','clip','shadow','quack'
    #         'antenna' (string)
    #         'timerange' (string)
    #         'reason' (string)
    #         'time' (float)          in mjd seconds
    #         'interval' (float)      in mjd seconds
    #         'cmd' (string)          string (for COMMAND col in FLAG_CMD)
    #         'type' (string)         'FLAG' / 'UNFLAG'
    #         'applied' (bool)
    #         'level' (int)
    #         'severity' (int)
    #

    useid = False

    if myoutfile != '':
        try:
            lfout = open(myoutfile, 'w')
        except:
            raise Exception( 'Error opening list output file ' \
                + myoutfile )

    keylist = myflags.keys()
    if len(keylist) == 0:
        casalog.post('There are no flags to list', 'WARN')
        return
    # Sort keys
#    keylist.sort

    # Set up any selection
    if myantenna != '':
        casalog.post('Selecting flags by antenna="' + str(myantenna)
                     + '"')
    myantlist = myantenna.split(',')

    if myreason != '':
        casalog.post('Selecting flags by reason="' + str(myreason) + '"'
                     )
    myreaslist = myreason.split(',')

    if listmode == 'online':
        phdr = '%8s %12s %8s %32s %48s' % ('Key', 'FlagID', 'Antenna',
                'Reason', 'Timerange')
    elif listmode == 'cmd':
        phdr = '%8s %45s %32s %6s %7s %3s %3s %s' % (
            'Row',
            'Timerange',
            'Reason',
            'Type',
            'Applied',
            'Level',
            'Severity',
            'Command',
            )
    elif listmode == 'file':
        phdr = '%8s %32s %s' % ('Key', 'Reason', 'Command')
    else:
        return

    if myoutfile != '':
        # list to output file
        lfout.write(phdr + '\n')
    else:
        # list to logger
        casalog.post(phdr)

    # Loop over flags
    for key in keylist:
        fld = myflags[key]
        # Get fields
        skey = str(key)
        if 'id' in fld and useid:
            fid = fld['id']
        else:
            fid = str(key)
        if 'antenna' in fld:
            ant = fld['antenna']
        else:
            ant = 'Unset'
        if 'timerange' in fld:
            timr = fld['timerange']
        else:
            timr = 'Unset'
        if 'reason' in fld:
            reas = fld['reason']
        else:
            reas = 'Unset'
        if 'command' in fld:
            cmd = fld['command']
            # To be verified
#            if 'addantenna' in fld:
#                addantenna = fld['addantenna']
#                cmd = cmd + ' addantenna=' + str(addantenna)
#        else:

#            cmd = 'Unset'
        if 'type' in fld:
            typ = fld['type']
        else:
            typ = 'FLAG'
        if 'level' in fld:
            levl = str(fld['level'])
        else:
            levl = '0'
        if 'severity' in fld:
            sevr = str(fld['severity'])
        else:
            sevr = '0'
        if 'applied' in fld:
            appl = str(fld['applied'])
        else:
            appl = 'Unset'

        # Print out listing
        if myantenna == '' or myantlist.count(ant) > 0:
            if myreason == '' or myreaslist.count(reas) > 0:
                if listmode == 'online':
                    pstr = '%8s %12s %8s %32s %48s' % (skey, fid, ant,
                            reas, timr)
                elif listmode == 'cmd':
                    # Loop over dictionary with commands
                    cmdline = ""
                    for k,v in lociteritems(cmd):
                        cmdline = cmdline + k + '=' + str(v) + ' '
                    
                    cmdline = cmdline.rstrip()                       
                    pstr = '%8s %45s %32s %6s %7s %3s %3s %s' % (
                        skey,
                        timr,
                        reas,
                        typ,
                        appl,
                        levl,
                        sevr,
                        cmdline,
                        )
                else:
                    cmdline = ""
                    for k,v in lociteritems(cmd):
                        cmdline = cmdline + k + '=' + str(v) + ' '
                    
                    cmdline = cmdline.rstrip()                       
                    pstr = '%8s %45s %32s %6s %7s %3s %3s %s' % (
                        skey,
                        timr,
                        reas,
                        typ,
                        appl,
                        levl,
                        sevr,
                        cmdline,
                        )
                    pstr = '%8s %32s %s' % (skey, reas, cmdline)
                if myoutfile != '':
                    # list to output file
                    lfout.write(pstr + '\n')
                else:
                    # list to logger
                    casalog.post(pstr)
    if myoutfile != '':
        lfout.close()


def selectXMLFlags(
    myflags=None,
    myantenna='',
    myreason='any',
    ):

    #
    # Return dictionary of input flags using selection by antenna/reason
    # and grouped/sorted by flagsort.
    #
    #   selectFlags: Return dictionary of flags using selection by antenna/reason
    #              and grouped/sorted by flagsort.
    #      myflags (dictionary)  input flag dictionary (e.g. from readflagxml
    #      myantenna (string)    selection by antenna(s)
    #      myreason (string)     selection by reason(s)
    #
    #   Usage: myflagd = selectFlags(myflags,antenna,reason)
    #
    #   Dictionary structure:
    #   fid : 'id' (string)
    #         'mode' (string)         flag mode '','clip','shadow','quack','online'
    #         'antenna' (string)
    #         'timerange' (string)
    #         'reason' (string)
    #         'time' (float)          in mjd seconds
    #         'interval' (float)      in mjd seconds
    #         'cmd' (string)          command string (for COMMAND col in FLAG_CMD)
    #         'type' (string)         'FLAG' / 'UNFLAG'
    #         'applied' (bool)        set to True here on read-in
    #         'level' (int)           set to 0 here on read-in
    #         'severity' (int)        set to 0 here on read-in
    #
    #
    #
    # Check if any operation is needed
    if myantenna == '' and myreason == '':
        casalog.post('No selection or sorting needed - sortflags returning input dictionary')
        flagd = myflags
        return flagd
    #
    flagd = {}
    nflagd = 0
    keylist = myflags.keys()
    casalog.post('Selecting from ' + str(len(keylist)) \
                 + ' flagging commands')
        
    if len(keylist) == 0:
        casalog.post('No flags found in input dictionary')
        return myflags

    #
    # Sort by key
    #
    keylist.sort()
    #
    # Construct flag command list for selected ant,reason
    #
    casalog.post('Selecting flags by antenna="' + str(myantenna)
                 + '"')
    myantlist = myantenna.split(',')

    casalog.post('Selecting flags by reason="' + str(myreason) + '"'
                 )
    myreaslist = []
# Parse myreason
    if type(myreason) == str:
        if myreason == '':
            casalog.post('WARNING: reason= is treated as selection on a blank REASON!'
                         , 'WARN')
        if myreason != 'any':
            myreaslist.append(myreason)
    elif type(myreason) == list:
        myreaslist = myreason
    else:
        casalog.post('ERROR: reason contains unknown variable type'
                     , 'SEVERE')
        return
    if len(myreaslist) > 0:
        casalog.post('Selecting for reasons: ' + str(myreaslist))

# Note antenna and reason selection checks for inclusion not exclusivity
    doselect = myantenna != '' or len(myreaslist) > 0

# Now loop over flags, break into sorted and unsorted groups
    nunsortd = 0
    unsortd = {}
    unsortdlist = []
    
# All flags are in unsorted list
    unsortd = myflags.copy()
    unsortdlist = unsortd.keys()
    nunsortd = len(unsortdlist)

# selection on unsorted flags
    if doselect and nunsortd > 0:
        keylist = unsortd.keys()
        for key in keylist:
            myd = unsortd[key]
            ant = myd['antenna']
            antlist = ant.split(',')
            reas = myd['reason']
            reaslist = reas.split(',')
    # break this flag by antenna
            antstr = ''
            reastr = ''
            addf = False

            for a in antlist:
                if myantenna == '' or myantlist.count(a) > 0:
                    addr = False
                    if len(myreaslist) > 0:
                        for r in myreaslist:
                            if reas == r or reaslist.count(r) > 0:
                                addr = True
                # check if this is a new reason
                                rlist = reastr.split(',')
                                if reastr != '':
                                    rlist = reastr.split(',')
                                    if rlist.count(r) == 0:
                                        reastr += ',' + r
                                else:
                                    reastr = r
                    else:
                        addr = True
                        reastr = reas
                    if addr:
                        addf = True
                        if antstr != '':
                # check if this is a new antenna
                            alist = antstr.split(',')
                            if alist.count(a) == 0:
                                antstr += ',' + a
                        else:
                            antstr = a
            if addf:
                flagd[nflagd] = myd
                flagd[nflagd]['antenna'] = antstr
                flagd[nflagd]['reason'] = reastr
                nflagd += 1
        flagdlist = flagd.keys()
    elif nunsortd > 0:
    # just copy to flagd w/o selection
        flagd = unsortd.copy()
        flagdlist = flagd.keys()
        nflagd = len(flagdlist)

    if nflagd > 0:
        casalog.post('Found total of ' + str(nflagd)
                     + ' flags meeting selection criteria')
    else:
        casalog.post('No flagging commands found meeting criteria')

    return flagd


# Done


def clearFlagCmd(msfile, myrowlist=[]):
    #
    # Delete flag commands (rows) from the FLAG_CMD table of msfile
    #
    # Open and read columns from FLAG_CMD

    mstable = os.path.join(msfile,'FLAG_CMD')
    try:
        tblocal.open(mstable, nomodify=False)
    except:
        raise Exception( 'Error opening table ' + mstable )

    nrows = int(tblocal.nrows())
    casalog.post('There were ' + str(nrows) + ' rows in FLAG_CMD')
    if nrows > 0:
        if len(myrowlist) > 0:
            rowlist = myrowlist
        else:
            rowlist = list(range(nrows))
        try:
            tblocal.removerows(rowlist)
            casalog.post('Deleted ' + str(len(rowlist))
                         + ' from FLAG_CMD table in MS')
        except:
            tblocal.close()
            raise Exception( 'Error removing rows ' + str(rowlist) \
                + ' from table ' + mstable )

    else:
        casalog.post('No rows to clear')

    tblocal.close()

def newplotflags(
    myflags,
    plotname,
    t1sdata,
    t2sdata,
    ):
    #
    # Function to plot flagging dictionary
    # Adapted from J.Marvil
    # Updated STM v4.1 2011-11-02 to handle ALMA flags
    # Updated STM v4.2 2012-02-16 trim flag times to data times
    # Updated STM v4.2 2012-04-10 bug fix in trim flag times to data times
    # Updated STM v4.2 2012-04-10 messages to logger

    # After the swig converstion, it seems that the following
    # line is not needed anymore
    #    qa = casac.qa = qatool = casac.quanta()

    try:
        import pylab as pl
    except ImportError as exc:
        raise ImportError('Failed to load pylab, required by flagcmd: {}'.format(exc))

    # list of supported colors (in order)
    colorlist = [
        'red',
        'blue',
        'green',
        'black',
        'cyan',
        'magenta',
        'yellow',
        'orange',
        ]
    ncolors = len(colorlist)

    # get list of flag keys
    keylist = myflags.keys()

    # get lists of antennas and reasons
    # make plotting dictionary
    myants = []
    myreas = []
    plotflag = {}
    ipf = 0
    for key in keylist:
        antstr = myflags[key]['antenna']
        reastr = myflags[key]['reason']
        timstr = myflags[key]['timerange']
        if antstr != '':
            # flags that have antenna specified
            antlist = antstr.split(',')
            nantlist = len(antlist)
        else:
            # Special
            antlist = ['All']
            nantlist = 1
        #
        realist = reastr.split(',')
        nrealist = len(realist)
        #
        timlist = timstr.split(',')
        ntimlist = len(timlist)
        #
        # Break these into nants x ntimes flags
        # Trick is assigning multiple reasons
        # Normal cases:
        # A. One reason, single/multiple antennas x times
        # B. Multiple reasons=times, single/multiple antenna(s)
        # C. Single time, multiple antennas/reasons
        # D. Multiple reasons, no way to correspond with times
        #
        timmin = 1.0E11
        timmax = 0.0
        if nrealist == 1:
            # simplest case, single reason
            reas = realist[0]
            if reas == '':
                reas = 'Unknown'
            if myreas.count(reas) == 0:
                myreas.append(reas)
            for ia in range(nantlist):
                ant = antlist[ia]
                if myants.count(ant) == 0:
                    myants.append(ant)
                for it in range(ntimlist):
                    times = timlist[it]
                    plotflag[ipf] = {}
                    plotflag[ipf]['antenna'] = ant
                    plotflag[ipf]['reason'] = reas
                    plotflag[ipf]['timerange'] = times
                    plotflag[ipf]['show'] = True
                    ipf += 1
        elif nrealist == ntimlist:
            # corresponding reasons and times
            for ia in range(nantlist):
                ant = antlist[ia]
                if myants.count(ant) == 0:
                    myants.append(ant)
                for it in range(ntimlist):
                    times = timlist[it]
                    reas = realist[it]
                    if reas == '':
                        reas = 'Unknown'
                    if myreas.count(reas) == 0:
                        myreas.append(reas)
                    plotflag[ipf] = {}
                    plotflag[ipf]['antenna'] = ant
                    plotflag[ipf]['reason'] = reas
                    plotflag[ipf]['timerange'] = times
                    plotflag[ipf]['show'] = True
                    ipf += 1
        else:
            # no correspondence between multiple reasons and ants/times
            # assign reason 'Miscellaneous'
            reas = 'Miscellaneous'
            if myreas.count(reas) == 0:
                myreas.append(reas)
            for ia in range(nantlist):
                ant = antlist[ia]
                if myants.count(ant) == 0:
                    myants.append(ant)
                for it in range(ntimlist):
                    times = timlist[it]
                    plotflag[ipf] = {}
                    plotflag[ipf]['antenna'] = ant
                    plotflag[ipf]['reason'] = reas
                    plotflag[ipf]['timerange'] = times
                    plotflag[ipf]['show'] = True
                    ipf += 1

    myants.sort()
    nants = len(myants)
    nreas = len(myreas)
    casalog.post('Found ' + str(nreas) + ' reasons to plot for '
                 + str(nants) + ' antennas')
    npf = ipf
    casalog.post('Found ' + str(npf) + ' total flag ranges to plot')

    # sort out times
    for ipf in range(npf):
        times = plotflag[ipf]['timerange']
        if times != '':
            if times.count('~') > 0:
                t1 = times[:times.find('~')]
                t2 = times[times.find('~') + 1:]
            else:
                t1 = times
                t2 = t1
            (t1s, t2s) = (qalocal.convert(t1, 's')['value'], qalocal.convert(t2,
                          's')['value'])
            plotflag[ipf]['t1s'] = t1s
            plotflag[ipf]['t2s'] = t2s
            if t1s < timmin:
                timmin = t1s
            if t2s > timmax:
                timmax = t2s
    # min,max times
    q1 = qalocal.quantity(timmin, 's')
    time1 = qalocal.time(q1, form='ymd', prec=9)[0]
    q2 = qalocal.quantity(timmax, 's')
    time2 = qalocal.time(q2, form='ymd', prec=9)[0]
    casalog.post('Found flag times from ' + time1 + ' to ' + time2)

    # sort out blank times
    for ipf in range(npf):
        times = plotflag[ipf]['timerange']
        if times == '':
            if t2sdata >= t1sdata > 0:
                plotflag[ipf]['t1s'] = t1sdata
                plotflag[ipf]['t2s'] = t2sdata
            else:
                plotflag[ipf]['t1s'] = timmin
                plotflag[ipf]['t2s'] = timmax

    # if flag times are beyond range of data, trim them
    # Added STM 2012-02-16, fixed STM 2012-04-10
    ndropped = 0
    if t2sdata >= t1sdata > 0 and (timmin < t1sdata or timmax
                                   > t2sdata):
        # min,max data times
        q1 = qalocal.quantity(t1sdata, 's')
        tdata1 = qalocal.time(q1, form='ymd', prec=9)[0]
        q2 = qalocal.quantity(t2sdata, 's')
        tdata2 = qalocal.time(q2, form='ymd', prec=9)[0]
        casalog.post('WARNING: Trimming flag times to data limits '
                     + tdata1 + ' to ' + tdata2)

        for ipf in range(npf):
            t1s = plotflag[ipf]['t1s']
            t2s = plotflag[ipf]['t2s']
            if t1s < t1sdata:
                if t2s >= t1sdata:
                    # truncate to t1sdata
                    plotflag[ipf]['t1s'] = t1sdata
                else:
                    # entirely outside data range, do not plot
                    plotflag[ipf]['show'] = False
                    ndropped += 1

            if t2s > t2sdata:
                if t1s <= t2sdata:
                    # truncate to t2sdata
                    plotflag[ipf]['t2s'] = t2sdata
                else:
                    # entirely outside data range, do not plot
                    plotflag[ipf]['show'] = False
                    ndropped += 1

        if ndropped > 0:
            casalog.post('WARNING: Trimming dropped ' + str(ndropped)
                         + ' flags entirely')

    # make reason dictionary with mapping of colors and offsets (-0.3 to 0.3)
    readict = {}
    reakeys = []
    if nreas > ncolors:
        for i in range(nreas):
            reas = myreas[i]
            readict[reas] = {}
            if i < ncolors - 1:
                colr = colorlist[i]
                readict[reas]['color'] = colr
                readict[reas]['index'] = i
                offs = 0.3 - float(i) * 0.6 / float(ncolors - 1)
                readict[reas]['offset'] = offs
                reakeys.append(reas)
            else:
                colr = colorlist[ncolors - 1]
                readict[reas]['color'] = colr
                readict[reas]['index'] = ncolors - 1
                readict[reas]['offset'] = -0.3
        reakeys.append('Other')
        readict['Other'] = {}
        readict['Other']['color'] = colorlist[ncolors - 1]
        readict['Other']['index'] = ncolors - 1
        readict['Other']['offset'] = -0.3
    else:
        for i in range(nreas):
            reas = myreas[i]
            reakeys.append(reas)
            colr = colorlist[i]
            offs = 0.3 - float(i) * 0.6 / float(ncolors - 1)
            readict[reas] = {}
            readict[reas]['color'] = colr
            readict[reas]['index'] = i
            readict[reas]['offset'] = offs
    nlegend = len(reakeys)
    casalog.post('Will plot ' + str(nlegend) + ' reasons in legend')

    if plotname == '':
        pl.ion()
    else:
        pl.ioff()

    plotflagperant = defaultdict(list)
    for ipf, flag in lociteritems(plotflag):
        if not flag['show']:
            continue
        nflag = flag.copy()
        nflag['color'] = readict[flag['reason']]['color']
        nflag['offset'] = readict[flag['reason']]['offset']
        plotflagperant[flag['antenna']].append(nflag)

    nplotted = sum(len(x) for x in plotflagperant.values())
    casalog.post('Plotted %d flags' % nplotted)

    figs = []
    figsize = (8, 6)
    # maximum number of antennas per plot page (CAS-5187)
    antlimit = 28
    if len(myants) <= antlimit:
        figs.append(pl.figure(figsize=figsize))
        _plotants(figs[0], plotflagperant, myants, readict)
    else:
        # prefer DA on first page
        da = [x for x in myants if ('DA' in x) or ('CM' in x)]
        no_da = [x for x in myants if x not in da]
        # but limit to 28 antennas per figure
        if len(da) > antlimit:
            no_da.extend(da[antlimit:])
            da = da[:antlimit]

        if da:
            figs.append(pl.figure(figsize=figsize))
            _plotants(figs[-1], plotflagperant, da, readict)

        # stuff the rest on other figures
        while no_da:
            figs.append(pl.figure(figsize=figsize))
            _plotants(figs[-1], plotflagperant, no_da[:antlimit], readict)
            no_da = no_da[antlimit:]

    filenames = []
    if plotname == '':
        pl.draw()
    else:
        if len(figs) == 1:
            figs[0].savefig(plotname, dpi=150)
            filenames.append(plotname)
        else:
            fdirname = os.path.dirname(plotname)
            filename, ext = os.path.splitext(os.path.basename(plotname))
            for i, f in enumerate(figs):
                fn = '%s-%03d%s' % (os.path.join(fdirname, filename), i + 1, ext)
                filenames.append(fn)
                f.savefig(fn, dpi=150)
    return filenames


def _plotants(figure, plotflagperant, antlist, readict_inp):
    """
    plot flags of antennas

    Parameters
    ----------
    figure : matplotlib figure
    plotflagperant: dict
        dictionary of antennas containing list of flag plot dictionaries
    antlist: dict
        list of antenna names to plot
    readict:
        list of reasons for the legend
    """

    ax1 = figure.add_axes([.15, .1, .75, .85])
    nants = len(antlist)
    readict = dict()
    used_reasons = set()
    # These style params can be critical to produce meaningful (or not too
    # misleading) plots (CAS-13100)
    style_params = {'alpha': .7, 'marker': '.', 'markersize': 1, 'linewidth': 1}
    for antind, thisant in enumerate(antlist):
        for flag in plotflagperant[thisant]:
            thisoffset = flag['offset'] + antind + 1
            ax1.plot([flag['t1s'], flag['t2s']], [thisoffset] * 2,
                     color=flag['color'], **style_params)
            used_reasons.add(flag['reason'])

    # remove reasons that are not needed
    for k, v in readict_inp.items():
        if k in used_reasons:
            readict[k] = v

    myXlim = ax1.get_xlim()
    myXrange = myXlim[1] - myXlim[0]
    # Pad the time axis?
    PadTime = 0.050000000000000003
    if PadTime > 0:
        xPad = PadTime * myXrange
        x0 = myXlim[0] - xPad
        x1 = myXlim[1] + xPad
        ax1.set_xlim(x0, x1)
        myXrange = x1 - x0
    else:
        # casalog.post('  Rescaled x axis')
        x0 = myXlim[0]
        x1 = myXlim[1]

    legendFontSize = 12
    myYupper = nants + len(readict) + 1.5
    # place legend text
    x = x0 + 0.050000000000000003 * myXrange
    for i, reas in enumerate(readict.keys()):
        colr = readict[reas]['color']
        ax1.text(x, i + nants + 1, reas, color=colr, size=legendFontSize)
    ax1.set_ylim([0, myYupper])

    ax1.set_yticks(range(1, len(antlist) + 1))
    ax1.set_yticklabels(antlist)
    # casalog.post('  Relabled y axis')

    nxticks = 3
    ax1.set_xticks(np.linspace(myXlim[0], myXlim[1], nxticks))

    mytime = []
    myTimestr = []
    for itim in range(nxticks):
        time = myXlim[0] + (myXlim[1] - myXlim[0]) * float(itim) \
            / float(nxticks - 1)
        mytime.append(time)
        q1 = qalocal.quantity(time, 's')
        time1 = qalocal.time(q1, form='ymd', prec=9)[0]
        if itim > 0:
            time1s = time1[11:]
        else:
            time1s = time1
        myTimestr.append(time1s)

    ax1.set_xticklabels(myTimestr)

# def isModeValid(line):
#     '''Check if mode is valid based on a line
#        molinede --> line with strings
#         Returns True if mode is either one of the following:
#            '',manual,clip,quack,shadow,elevation      '''
# 
#     if line.__contains__('mode'):
#         if line.__contains__('manual') or line.__contains__('clip') \
#             or line.__contains__('quack') or line.__contains__('shadow'
#                 ) or line.__contains__('elevation'):
#             return True
#         else:
#             return False
# 
#     # No mode means manual
#     return True


#
#******************** CAL TABLE FUNCTIONS   **************************
#
def readCalCmds(caltable, msfile, flaglist, rows, reason, useapplied):
    '''Flag a cal table
    
    caltable    cal table name
    msfile      optional MS with flag cmds
    flagcmds    list with flag cmds or [] when msfile is given
    reason      select only flag cmds with this reason(s)
    useapplied  select APPLIED true or false
    '''
            
    myflagcmd = {}
    if msfile != '':   
        casalog.post('Reading flag cmds from FLAG_CMD table of MS')    
        # Read only the selected rows for action = apply and
        myflagcmd = readFromTable(msfile, myflagrows=rows, useapplied=useapplied, myreason=reason)
            
    elif flaglist != []:    
        # Parse the input file                    
        if isinstance(flaglist, list):
            casalog.post('Reading from input list')
            cmdlist = flaglist

            casalog.post('Input ' + str(len(cmdlist))
                         + ' lines from input list')
            # Make a FLAG_CMD compatible dictionary and select by reason
            myflagcmd = fh.parseDictionary(cmdlist, reason, False)

        elif isinstance(flaglist, str):

            casalog.post('Reading from input file '+flaglist)
            cmdlist = fh.readFile(flaglist)

            # Make a FLAG_CMD compatible dictionary and select by reason
            myflagcmd = fh.parseDictionary(cmdlist, reason, False)
            
        else:
            casalog.post('Unsupported inpfile type', 'ERROR')
                                            
    return myflagcmd

def applyCalCmds(aflocal, caltable, myflagcmd, tablerows, flagbackup, outfile):
    
    # Get the list of parameters
    cmdkeys = list(myflagcmd.keys())
    
    # Select the data
    selpars = {}    
    if len(cmdkeys) == 1:
        # Get all the selection parameters, but set correlation to ''
        cmd0 = myflagcmd[cmdkeys[0]]['command']
        selpars = fh.parseSelectionPars(cmd0)
        casalog.post('The selected subset of the MS will be: ')
        casalog.post('%s' % selpars)
    
    aflocal.selectdata(selpars)
        
    fh.parseAgents(aflocal, myflagcmd, [], True, True, '')
    
    # Initialize the Agents
    aflocal.init()
    
    # Backup the flags before running
    if flagbackup:
        fh.backupFlags(aflocal, msfile='', prename='flagcmd')
    
    # Run the tool
    aflocal.run(True, True)
    
    aflocal.done()