#!/usr/bin/env python3
# This file is placed in the Public Domain.
# pylint: disable=C0413,W0212,E0401


"main"


import getpass
import os
import pathlib
import pwd
import readline
import sys
import termios
import time


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


from otpcr.cli      import CLI
from otpcr.errors   import Errors, errors
from otpcr.event    import Event
from otpcr.help     import TXT
from otpcr.log      import Logging
from otpcr.main     import cmnd, init, scan
from otpcr.parse    import parse
from otpcr.persist  import skel
from otpcr.run      import Cfg


from otpcr import modules
from otpcr import user


Cfg.version = "23"


if os.path.exists("mods"):
    import mods as MODS
else:
    MODS = None


class Console(CLI):

    "Console"

    def announce(self, txt):
        "disable announce."

    def callback(self, evt):
        "wait for callback."
        CLI.callback(self, evt)
        evt.wait()

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



def daemon(pidfile, verbose=False):
    "switch to background."
    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("/")
    if os.path.exists(pidfile):
        os.unlink(pidfile)
    path = pathlib.Path(pidfile)
    path.parent.mkdir(parents=True, exist_ok=True)
    with open(pidfile, "w", encoding="utf-8") as fds:
        fds.write(str(os.getpid()))


def modnames():
    "list all modules."
    return sorted({x for x in dir(modules) + dir(user) + dir(MODS) if not x.startswith("__")})


def privileges(username):
    "drop privileges."
    pwnam = pwd.getpwnam(username)
    os.setgid(pwnam.pw_gid)
    os.setuid(pwnam.pw_uid)


def wrap(func):
    "reset terminal."
    old3 = None
    try:
        old3 = termios.tcgetattr(sys.stdin.fileno())
    except termios.error:
        pass
    try:
        func()
    except (KeyboardInterrupt, EOFError):
        print("")
    finally:
        if old3:
            termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, old3)


def wrapped():
    "wrap main."
    wrap(main)
    errors()


def main():
    "main"
    readline.redisplay()
    skel()
    parse(Cfg, " ".join(sys.argv[1:]))
    Cfg.dis = Cfg.sets.dis
    Cfg.mod += "," + ",".join(modnames())
    if "h" in Cfg.opts:
        print(TXT)
        return
    if "v" in Cfg.opts:
        dte = " ".join(time.ctime(time.time()).replace("  ", " ").split()[1:])
        modiess = ",".join([x.upper() for x in modnames()])
        print(f'{dte} {Cfg.name.upper()} {Cfg.opts.upper()} {modiess}'.replace("  ", " "))
    wait = False
    if "d" in Cfg.opts:
        Cfg.user = getpass.getuser()
        CLI.out = Errors.out = Logging.out = None
        daemon(Cfg.pidfile, "-v" in sys.argv)
        privileges(Cfg.user)
        modstr = "," + ",".join(modnames())
        init(modules, modstr)
        init(user, modstr)
        wait = True
    elif "c" in Cfg.opts:
        csl = Console(print)
        if "i" in Cfg.opts:
            init(modules, Cfg.mod, Cfg.dis)
            init(user, Cfg.mod, Cfg.dis)
            init(MODS, Cfg.mod, Cfg.dis)
        csl.start()
        wait = True
    scan(modules, Cfg.mod, Cfg.dis)
    scan(user, Cfg.mod, Cfg.dis)
    scan(MODS, Cfg.mod, Cfg.dis)
    if Cfg.otxt:
        cmnd(Cfg.otxt, print)
    if wait or "w" in Cfg.opts:
        while 1:
            time.sleep(1.0)


if __name__ == "__main__":
    wrapped()
