#!/usr/bin/env python

import os
import sys
import argparse
import tempfile
import traceback
import subprocess

import guardian.cli as cli

############################################################

def phelp(args=None):
    """help"""
    parser.print_help()

def sprint(args):
    """print general system information"""
    system = cli.init_system(args, load=False)
    cli.print_system(system)

def states(args):
    """print state enumeration"""
    system = cli.init_system(args, load=True)
    index = 0
    for state, index in system.ordered_state_index():

        flag = ''
        if system.is_request(state):
            flag = ' *'
        print '%s %s%s' % (index, state, flag)
        index += 1

def edges(args):
    """print transition edges"""
    system = cli.init_system(args, load=True)
    for edge in system.edges:
        print '%s -> %s' % edge

def path(args):
    """print path between two states as a list of states"""
    system = cli.init_system(args, load=True)
    for state in system.shortest_path(args.state0, args.state1):
        print state

def code(args):
    """print all usercode module paths, starting with system module"""
    system = cli.init_system(args, load=True)
    print system.path
    for code in system.usercode:
        print code

def graph(args):
    """draw system graph"""
    try:
        import guardian.graph as graph
    except ImportError as e:
        sys.exit("Graph drawing not supported: %s" % e)
    system = cli.init_system(args, load=True)
    if args.all:
        args.gotos = True
        args.jumps = True
    path = ()
    if args.state:
        if args.request:
            path = (args.state, args.request)
        else:
            path = (args.state,)
    if args.query:
        try:
            from guardian.manager import Node
        except ImportError as e:
            sys.exit("Node query not supported: %s" % e)
        node = Node(system.name)
        node.init()
        path = (node.state, node.request)
    for state in path:
        if state not in system:
            sys.exit("State '%s' not defined in system %s." % (state, system.name))

    dot = graph.sys2dot(system,
                        path=path,
                        show_index=args.index,
                        show_gotos=args.gotos,
                        show_jumps=args.jumps,
                        edge_constraints=args.constraints,
                        )

    # display graph or write to file
    default_format = 'pdf'
    if args.outfile is not False:
        outfile = '%s_%s' % (system.ifo, system.name)
        outpath = args.outfile
        if outpath is None:
            outpath = outfile
        elif os.path.isdir(os.path.expanduser(outpath)):
            outpath = os.path.join(os.path.expanduser(outpath), outfile)
        ext = os.path.splitext(outpath)[1]
        if args.format:
            fmt = args.format
        elif ext != '':
            fmt = ext.strip('.')
        else:
            fmt = default_format
        if ext == '':
            outpath += '.'+fmt
        dot.write(outpath, format=fmt)
    else:
        if not args.format:
            args.format = default_format
        suffix = '.%s' % args.format
        if args.format == 'pdf':
            viewer = ['evince', '-w']
        elif args.format == 'svg':
            viewer = ['inkview']
            #viewer = ['iceweasel']
        else:
            viewer = ['xdg-open']
        try:
            # FIXME: better temp file name
            with tempfile.NamedTemporaryFile(suffix=suffix, delete=False) as f:
                dot.write(f.name, format=args.format)
                try:
                    subprocess.call(viewer + [f.name])
                except OSError as e:
                    sys.exit("Graph viewer '%s' could not be found." % (viewer[0]))
        except KeyboardInterrupt:
            sys.exit()

def edit(args):
    """edit system module with specified editor"""
    try:
        system = cli.init_system(args, load=True)
    except:
        print >>sys.stderr, "WARNING: Exception encountered when loading module:"
        print >>sys.stderr
        print >>sys.stderr, traceback.format_exc()
        system = cli.init_system(args, load=False)
    if args.editor:
        editor = args.editor.split()
    else:
        xdgdefault = subprocess.check_output(['xdg-mime', 'query', 'default', 'text/x-python'])
        editor = os.path.splitext(xdgdefault)[0]
        if editor == '':
            editor = 'emacs'
        editor = [editor]
    cmd = editor + [system.path]
    if system.usercode:
        cmd += system.usercode
    try:
        subprocess.call(cmd)
    except KeyboardInterrupt:
        sys.exit()

############################################################

PROG = 'guardutil'
description = '''Advanced LIGO Guardian system utility.'''
epilog = """

Add '-h' after individual commands for command help.

Environment variables:
  IFO                       IFO designator (required)
  USERAPPS_DIR              LIGO "userapps" root path ('/opt/rtcds/userapps/release')
  GUARD_MODULE_PATH         Override default userapps system module search path
"""

parser = argparse.ArgumentParser(
    formatter_class=argparse.RawDescriptionHelpFormatter,
    prog=PROG,
    description=description,
    epilog=epilog,
    )

subparser = parser.add_subparsers(
    #title='Commands',
    metavar='<command>',
    #description='''subcommand (see "<cmd> -h" for specific usage).''',
    dest='cmd',
    #help=argparse.SUPPRESS,
    )

sp = {}

cmd = 'print'
func = sprint
sp[cmd] = subparser.add_parser(cmd, help=func.__doc__, description=func.__doc__)
sp[cmd].set_defaults(func=func)
cli.add_grd_arg(sp[cmd], 'system')

cmd = 'states'
func = states
sp[cmd] = subparser.add_parser(cmd, help=func.__doc__, description=func.__doc__)
sp[cmd].set_defaults(func=func)
cli.add_grd_arg(sp[cmd], 'system')

cmd = 'edges'
func = edges
sp[cmd] = subparser.add_parser(cmd, help=func.__doc__, description=func.__doc__)
sp[cmd].set_defaults(func=func)
cli.add_grd_arg(sp[cmd], 'system')

cmd = 'path'
func = path
sp[cmd] = subparser.add_parser(cmd, help=func.__doc__, description=func.__doc__)
sp[cmd].set_defaults(func=func)
cli.add_grd_arg(sp[cmd], 'system')
sp[cmd].add_argument('state0', help="initial state")
sp[cmd].add_argument('state1', help="final state")

cmd = 'code'
func = code
sp[cmd] = subparser.add_parser(cmd, help=func.__doc__, description=func.__doc__)
sp[cmd].set_defaults(func=func)
cli.add_grd_arg(sp[cmd], 'system')

cmd = 'graph'
func = graph
sp[cmd] = subparser.add_parser(cmd, help=func.__doc__, description=func.__doc__)
sp[cmd].set_defaults(func=func)
cli.add_grd_arg(sp[cmd], 'system')
sp[cmd].add_argument('-i', '--index', action='store_true',
                     help="add state indices to labels")
sp[cmd].add_argument('-g', '--gotos', action='store_true',
                     help="draw 'goto' edges")
sp[cmd].add_argument('-j', '--jumps', action='store_true',
                     help="draw 'jump' edges")
sp[cmd].add_argument('-a', '--all', action='store_true',
                     help="show all edges (-gj)")
sp[cmd].add_argument('-c', '--constraints', action='store_true',
                     help="use edge constraints for goto and jump edges")
sp[cmd].add_argument('-q', '--query', action='store_true',
                     help="query via EPICS for <state> and <request> from running guard node for highlighting path")
sp[cmd].add_argument('-f', '--format', metavar='<type>', type=str,
                     help="drawing format: 'pdf', 'svg', etc. (default: 'pdf')")
sp[cmd].add_argument('-o', '--outfile', metavar='<path>', type=str, nargs='?', default=False,
                     help="save graph to file.  If path is a directory, a file named \"<system name>.<format>\" will be saved in that directory.  If path includes extension the extension will be used to determine format.")
sp[cmd].add_argument('state', metavar='<state>', type=str, nargs='?',
                     help="state to highlight")
sp[cmd].add_argument('request', metavar='<request>', type=str, nargs='?',
                     help="final (request) state for highlighting path")

cmd = 'edit'
func = edit
sp[cmd] = subparser.add_parser(cmd, help=func.__doc__, description=func.__doc__)
sp[cmd].set_defaults(func=func)
cli.add_grd_arg(sp[cmd], 'system')
sp[cmd].add_argument('-e', '--editor', type=str,
                     help="editor (emacs, gedit, etc. (default: xdg-open))")

############################################################

if __name__ == '__main__':

    # show help on insufficient arguments
    # FIXME: should throw non-zero exit code
    if len(sys.argv) < 3:
        sys.argv.append('-h')

    args = parser.parse_args()

    if not args.system:
        sys.exit("Must specify system.")

    args.func(args)
