#!/usr/bin/env python3
# This file is placed in the Public Domain.


import os
import sys
import time


sys.path.insert(0, os.getcwd())


from tob.clients import Client
from tob.command import Commands, Config, command, parse, scanner
from tob.handler import Event
from tob.objects import Default, fmt
from tob.package import Mods, inits, modules
from tob.persist import Workdir, pidname
from tob.logging import level
 

class CLI(Client):

    def __init__(self):
        Client.__init__(self)
        self.register("command", command)

    def raw(self, txt):
        print(txt.encode('utf-8', 'replace').decode("utf-8"))


class Console(CLI):

    def callback(self, event):
        if not event.txt:
            return
        super().callback(event)
        event.wait()

    def poll(self):
        evt = Event()
        evt.txt = input("> ")
        evt.type = "command"
        return evt


def boot(doparse=True):
    Mods.dirs["mods"] = "mods"
    Mods.dirs["tob.modules"] = os.path.join(os.path.dirname(__file__), "modules")
    parse(Config, " ".join(sys.argv[1:]))
    print(fmt(Config))
    level(Config.level)
    if "v" in Config.opts:
        banner()
    if "a" in Config.opts:
        Config.sets.init = ",".join(modules())
    Workdir.wdr = os.path.expanduser(f"~/.{Config.name}")
    scanner()
    Commands.add(cmd)
    Commands.add(ver)


"scripts"


def background():
    daemon("-v" in sys.argv)
    privileges()
    boot(False)
    pidfile(pidname(Config.name))
    inits(spl(Config.sets.init or Config.default))
    forever()


def console():
    import readline # noqa: F401
    boot()
    for _mod, thr in inits(spl(Config.sets.init or Config.default)):
        if "w" in Config.opts:
            thr.join(30.0)
    csl = Console()
    csl.start()
    forever()


def control():
    if len(sys.argv) == 1:
        return
    boot()
    csl = CLI()
    evt = Event()
    evt.orig = repr(csl)
    evt.type = "command"
    evt.txt = " ".join(sys.argv[1:])
    evt.cmd  = evt.txt.split()[0]
    command(evt)
    evt.wait()


def service():
    privileges()
    boot(False)
    pidfile(pidname(Config.name))
    inits(spl(Config.sets.init or Config.default))
    forever()


"commands"


def cmd(event):
    event.reply(",".join(sorted(Commands.cmds)))


def ver(event):
    event.reply(f"{Config.name.upper()} {Config.version}")



"utility"


def banner():
    tme = time.ctime(time.time()).replace("  ", " ")
    print("%s %s since %s (%s)" % (Config.name.upper(), Config.version, tme, Config.level.upper()))


def check(txt):
    args = sys.argv[1:]
    for arg in args:
        if not arg.startswith("-"):
            continue
        for char in txt:
            if char in arg:
                return True
    return False


def daemon(verbose=False):
    pid = os.fork()
    if pid != 0:
        os._exit(0)
    os.setsid()
    pid2 = os.fork()
    if pid2 != 0:
        os._exit(0)
    if not verbose:
        with open('/dev/null', 'r', encoding="utf-8") as sis:
            os.dup2(sis.fileno(), sys.stdin.fileno())
        with open('/dev/null', 'a+', encoding="utf-8") as sos:
            os.dup2(sos.fileno(), sys.stdout.fileno())
        with open('/dev/null', 'a+', encoding="utf-8") as ses:
            os.dup2(ses.fileno(), sys.stderr.fileno())
    os.umask(0)
    os.chdir("/")
    os.nice(10)


def forever():
    while True:
        try:
            time.sleep(0.1)
        except (KeyboardInterrupt, EOFError):
            break


def pidfile(filename):
    if os.path.exists(filename):
        os.unlink(filename)
    path2 = pathlib.Path(filename)
    path2.parent.mkdir(parents=True, exist_ok=True)
    with open(filename, "w", encoding="utf-8") as fds:
        fds.write(str(os.getpid()))


def privileges():
    import getpass
    import pwd
    pwnam2 = pwd.getpwnam(getpass.getuser())
    os.setgid(pwnam2.pw_gid)
    os.setuid(pwnam2.pw_uid)


def spl(txt):
    try:
        result = txt.split(",")
    except (TypeError, ValueError):
        result = [
            txt,
        ]
    return [x for x in result if x]


"runtime"


def wrapped(func):
    try:
        func()
    except (KeyboardInterrupt, EOFError):
        print("")


def wrap(func):
    import termios
    old = None
    try:
        old = termios.tcgetattr(sys.stdin.fileno())
    except termios.error:
        pass
    try:
        wrapped(func)
    finally:
        if old:
            termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, old)


def main():
    if check("c"):
        wrap(console)
    elif check("d"):
        background()
    elif check("s"):
        wrapped(service)
    else:
        wrapped(control)


"trampoline"


if __name__ == "__main__":
    main()
