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


"main"


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


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


from op.message import Event
from op.handler import Client, parse_cmd
from op.storage import Workdir, skel
from op.logging import debug, enable
from op.objects import Default, cdir
from op.runtime import broker, cmnd, dte, init, scan
from op.threads import errors, setout


from op import modules


Cfg             = Default()
Cfg.dis         = ""
Cfg.mod         = "cmd,mod"
Cfg.opts        = ""
Cfg.name        = __file__.rsplit(os.sep, maxsplit=1)[-1]
Cfg.version     = "32"
Cfg.wdr         = os.path.expanduser(f"~/.{Cfg.name}")
Cfg.pidfile     = os.path.join(Cfg.wdr, f"{Cfg.name}.pid")


Workdir.workdir = Cfg.wdr


class Console(Client):

    "Console"

    def __init__(self):
        Client.__init__(self)
        broker.add(self)

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

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

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

    def say(self, _channel, txt):
        "print to console"
        txt = txt.encode('utf-8', 'replace').decode()
        print(txt)


def daemon(pidfile, verbose=False):
    "switch to background."
    # pylint: disable=W0212
    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)
    cdir(os.path.dirname(pidfile))
    with open(pidfile, "w", encoding="utf-8") as fds:
        fds.write(str(os.getpid()))


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


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


def main():
    "main"
    parse_cmd(Cfg, " ".join(sys.argv[1:]))
    skel()
    if "a" in Cfg.opts:
        Cfg.mod = ",".join(modules.__dir__())
    setout(print)
    if "v" in Cfg.opts:
        enable(print)
        debug(f"{Cfg.name.upper()} {Cfg.opts.upper()} {Cfg.mod.upper()} started {dte}")
    if "h" in Cfg.opts:
        print(__doc__)
    elif "d" in Cfg.opts:
        Cfg.mod  = ",".join(modules.__dir__())
        Cfg.user = getpass.getuser()
        daemon(Cfg.pidfile, "v" in Cfg.opts)
        scan(modules, Cfg.mod)
        privileges(Cfg.user)
        init(modules, Cfg.mod)
        while 1:
            time.sleep(1.0)
    elif "c" in Cfg.opts:
        scan(modules, Cfg.mod, Cfg.sets.dis)
        init(modules, Cfg.mod, Cfg.sets.dis)
        csl = Console()
        csl.start()
        while 1:
            time.sleep(1.0)
    elif Cfg.otxt:
        scan(modules, Cfg.mod, Cfg.sets.dis)
        cmnd(Cfg.otxt, print)


if __name__ == "__main__":
    readline.redisplay()
    wrap(main)
    errors()
