import cmd
import json
import sys
from typing import Tuple

from ovms import cmbdef, iodef, mbxqio, psldef, starlet


def pstat2str(s: int) -> str:
    dstate = {
        0: 'STOPPED',
        10: 'STARTING',
        20: 'RUNNING',
        30: 'BACKOFF',
        40: 'STOPPING',
        100: 'EXITED',
        200: 'FATAL',
        1000: 'UNKNOWN',
    }
    return dstate[s]


class Controller(cmd.Cmd):
    mcmd: mbxqio.MBXQIO
    mcmd_r: mbxqio.MBXQIO

    def __init__(
        self,
        mcmd: mbxqio.MBXQIO,
        mcmd_r: mbxqio.MBXQIO,
        completekey='tab',
        stdin=None,
        stdout=None,
    ):
        self.mcmd = mcmd
        self.mcmd_r = mcmd_r
        cmd.Cmd.__init__(self, completekey, stdin, stdout)

    def do_EOF(self, line):
        """To quit, type ^D or use the quit command"""
        return True

    def do_quit(self, line):
        """To quit, type ^D or use the quit command"""
        return True

    def do_shutdown(self, arg):
        """Shutdown supervisord, don't stop programs"""
        lst = arg.split()
        if len(lst) != 0:
            print('*** invalid number of arguments')
            return
        jscmd = json.dumps({'cmd': 'shutdown'})
        self.mcmd.write(jscmd.encode('ascii'), iodef.IO_M_READERCHECK)

    def do_status(self, arg):
        """Display status of programs"""
        lst = arg.split()
        jscmd = json.dumps({'cmd': 'status', 'programs': lst})
        self.mcmd.write(jscmd.encode('ascii'), iodef.IO_M_READERCHECK)
        res: bytes
        res = self.mcmd_r.read(4096, iodef.IO_M_WRITERCHECK)   # type: ignore
        jscmd = json.loads(res)
        for pinfo in jscmd['reply']:
            pinfo[3] = pstat2str(pinfo[3])
            print(pinfo)

    def help_status(self):
        print('status <name>\t\tGet status for a single process')
        print('status <gname>:*\tGet status for all ' 'processes in a group')
        print(
            'status <name> <name>\tGet status for multiple named ' 'processes'
        )
        print('status\t\t\tGet all process status info')

    def do_start(self, arg):
        lst = arg.split()
        if len(lst) < 1:
            print('*** invalid number of arguments')
            return
        jscmd = json.dumps({'cmd': 'start', 'programs': lst})
        self.mcmd.write(jscmd.encode('ascii'), iodef.IO_M_READERCHECK)

    def help_start(self):
        print('start <name>\t\tStart a process')
        print('start <gname>:*\t\tStart all processes in a group')
        print('start <name> <name>\tStart multiple processes or groups')
        print('start all\t\tStart all processes')

    def do_stop(self, arg):
        lst = arg.split()
        if len(lst) < 1:
            print('*** invalid number of arguments')
            return
        jscmd = json.dumps({'cmd': 'stop', 'programs': lst})
        self.mcmd.write(jscmd.encode('ascii'), iodef.IO_M_READERCHECK)

    def help_stop(self):
        print('stop <name>\t\tStop a process')
        print('stop <gname>:*\t\tStop all processes in a group')
        print('stop <name> <name>\tStop multiple processes or groups')
        print('stop all\t\tStop all processes')


def mbx_init() -> Tuple[int, int]:
    s, chancmd = starlet.crembx(
        prmflg=1,
        lognam='SUPERVISORD_CMD',
        maxmsg=2048,
        bufquo=8192,
        promsk=0x0000FF00,
        acmode=psldef.PSL_C_USER,
        flags=cmbdef.CMB_M_WRITEONLY,
    )
    s, chancmd_r = starlet.crembx(
        prmflg=1,
        lognam='SUPERVISORD_CMD_REPLY',
        maxmsg=32 * 1024,
        bufquo=64 * 1024,
        promsk=0x0000FF00,
        acmode=psldef.PSL_C_USER,
        flags=cmbdef.CMB_M_READONLY,
    )

    return chancmd, chancmd_r


def main():
    chan, chan_r = mbx_init()
    with (
        mbxqio.MBXQIO(channel=chan) as mcmd,
        mbxqio.MBXQIO(channel=chan_r) as mcmd_r,
    ):
        c = Controller(mcmd, mcmd_r)
        if len(sys.argv) > 1:
            c.onecmd(' '.join(sys.argv[1:]))
        else:
            c.cmdloop()


if __name__ == '__main__':
    main()