#!/usr/bin/env python3
"""
Simple interface to list, start, stop processes running on a remote ln manager.
"""
from __future__ import print_function

import sys
import time
import links_and_nodes as ln

class LnmCommander(ln.lnm_remote):
    def __init__(self, address, debug=False):
        ln.lnm_remote.__init__(self, address=address, debug=debug)

        # this needs to have manager python dir in path!
        if ln.ln_manager_dir not in sys.path:
            sys.path.insert(0, ln.ln_manager_dir)
        self.sysconf = self.request("get_system_configuration")

    def stop_process(self, stop=None):
        """
        stop a process, state or group
        :param stop: name of process, state or group to stop
        """
        if stop is None:
            print("No process to stop")
            return

        if stop in self.sysconf.groups:
            self.info("Stopping group %s\n" % stop)
            for obj in self.sysconf.groups[stop].group_members:
                self.stop_process(obj.name)
            return
        if stop in self.sysconf.processes:
            ptype = "Process"
            state = "stop"
        elif stop in self.sysconf.states:
            ptype = "State"
            state = "DOWN"
        else:
            self.error("%r does not name a Process/State or Group!\n" % stop)
            return
        self.info("requesting stop of %r\n" % stop)
        self.request("set_process_state_request", ptype=ptype, pname=stop, requested_state=state)

    def stop_all(self):
        """
        Stop all processes and states
        """
        for name in self.sysconf.processes:
            self.stop_process(name)
        for state in self.sysconf.states:
            self.stop_process(state)

    def start_process(self, start=None):
        """
        Start a process, state or group
        :param start: name of process, state or group to start
        """
        if start is None:
            print("No process to start")
            return
        if start in self.sysconf.groups:
            self.info("Starting group %s\n" % start)
            for obj in self.sysconf.groups[start].group_members:
                self.start_process(obj.name)
            return
        if start in self.sysconf.processes:
            ptype = "Process"
            state = "start"
        elif start in self.sysconf.states:
            ptype = "State"
            state = "UP"
        else:
            self.error("%r does not name a Process/State or Group!\n" % start)
            return
        self.info("requesting start of %r\n" % start)
        self.request("set_process_state_request", ptype=ptype, pname=start, requested_state=state)

    def list_all_processes(self):
        print("All processes:")
        for p in self.sysconf.processes:
            print(p)

    def list_all_groups(self):
        print("All groups:")
        for g in self.sysconf.groups:
            print(g)

    def list_by_name(self, name=None, ignorecase=False):
        if name is None:
            self.list_all_processes()
            return
        if ignorecase:
            name = name.lower()
        print("Processes/groups containing %s:" % name)
        for p in self.sysconf.processes:
            n = p.lower() if ignorecase else p
            if name in n:
                print("Process: %s" % p)
        for g in self.sysconf.groups:
            n = g.lower() if ignorecase else g
            if name in n:
                print("Group: %s" % g)

    def status(self, name=None, ignorecase=False):
        def color_state(p):
            if p.state == "stopped" and not p.no_error_on_stop:
                # red
                return "\x1b[31m" + p.state + "\x1b[0m"
            elif p.state == "ready" or (p.state == "started" and not p.has_ready_state()):
                # green
                return "\x1b[32m" + p.state + "\x1b[39m"
            elif p.state == "started" and p.has_ready_state():
                # bold yellow
                return "\x1b[33m\x1b[1m" + p.state + "\x1b[0m"
            elif "startup error" in str(p.state):
                # bold red
                return "\x1b[31m\x1b[1m" + p.state + "\x1b[0m"
            else:
                return str(p.state)
        if ignorecase and name is not None:
            name = name.lower()
        ps = []
        for pname, p in self.sysconf.processes.iteritems():
            n = pname.lower() if ignorecase else pname
            if name is None or name in n:
                state = color_state(p)
                if p.requested_state is not None:
                    state += " (requested state: " + p.requested_state + ")"
                #elif p.last_requested_state is not None:
                #    state += " (last requested state: " + p.last_requested_state + ")"
                ps.append(("Process %s:" % pname, state))
        pad_to = max([len(pn) for pn, _ in ps]) + 1
        for (pn, cs) in ps:
            print(pn.ljust(pad_to) + cs)

    def shutdown(self, stop_processes=False, stop_daemons=False):
        if stop_processes or stop_daemons:
            self.stop_all()
            time.sleep(2)
        if stop_daemons:
            print("Shutting down all ln daemons.")
            self.request("stop_all_daemons")
            time.sleep(1)
        print("Shutting down ln_manager.")
        self.request("exit")

    def on_output(self, name, output):
        # disable printing of process output
        return False
    def on_log_messages(self, msgs):
        # disable printing of log messages
        return False

if __name__ == "__main__":
    import argparse
    import socket
    import subprocess
    parser = argparse.ArgumentParser()
    parser.add_argument("--debug", action="store_true", dest="debug")
    parser.add_argument('-a', "-ln_manager", metavar="HOST:[PORT]",
                        help="host:port where remote LN manager is running")

    subparsers = parser.add_subparsers(dest='subparser', help='subcommands: %(prog)s -h for further help')
    # connect command
    connect_parser = subparsers.add_parser('connect', help="start GUI connecting to LN manager")
    connect_parser.add_argument("address", nargs='?', metavar="[HOST]:PORT",
                                help="host:port where remote LN manager is running")
    # list command
    list_parser = subparsers.add_parser('list', help='list processes/groups')
    list_parser.add_argument('name', action='store', nargs='?', help='processes/groups are filtered on name')
    list_parser.add_argument('-i', '--ignore_case', action="store_true", help="case insensitive name matching")
    # start command
    start_parser = subparsers.add_parser('start', help="start processes/groups")
    start_parser.add_argument('names', metavar="NAME", nargs='+', help="processes/groups to start")
    # stop command
    stop_parser = subparsers.add_parser('stop', help="stop processes/groups")
    stop_parser.add_argument('names', metavar="NAME", nargs='*', help="processes/groups to start")
    stop_parser.add_argument('--all', action="store_true", help="stop all processes")
    # status command
    status_parser = subparsers.add_parser('status', help="Show status of processes")
    status_parser.add_argument('name', action='store', nargs='?', help='processes/groups are filtered on name')
    status_parser.add_argument('-i', '--ignore_case', action="store_true", help="case insensitive name matching")
    # shutdown command
    shutdown_parser = subparsers.add_parser('shutdown', help="Shutdown LN manager")
    shutdown_parser.add_argument('-p', "--processes", action="store_true", help="Stop all processes")
    shutdown_parser.add_argument('-d', "--daemons", action="store_true", help="Also stop daemons (implies --processes)")

    args = parser.parse_args()

    if args.subparser in 'connect':
        ret = subprocess.call(["ln_manager", "--connect", args.address])
        sys.exit(ret)

    try:
        lnc = LnmCommander(address=args.address, debug=args.debug)
    except socket.error as e:
        print("Error: Could not connect to LN manager. Is it running?")
        sys.exit(1)

    if args.subparser in 'list':
        lnc.list_by_name(args.name, ignorecase=args.ignore_case)
    elif args.subparser in 'start':
        for n in args.names:
            lnc.start_process(n)
    elif args.subparser in 'stop':
        if args.all:
            lnc.stop_all()
        else:
            if len(args.names) == 0:
                stop_parser.error("Neither NAME nor --all given.")
            for n in args.names:
                lnc.stop_process(n)
    elif args.subparser in 'shutdown':
        lnc.shutdown(stop_processes=args.processes, stop_daemons=args.daemons)
    elif args.subparser in 'status':
        lnc.status(args.name, ignorecase=args.ignore_case)

