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


"basis to prosecute"


import os
import sys
import termios
import time
import traceback


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


from zelf.client   import Client
from zelf.commands import Commands, command
from zelf.default  import Default
from zelf.errors   import Errors, debug
from zelf.object   import Object
from zelf.parser   import parse
from zelf.storage  import Storage, path, store
from zelf.thread   import launch
from zelf.utils    import mods, pidfile, spl, touch


from zelf import errors


PIDFILE = store("zelf.pid")
TIME = time.ctime(time.time()).replace("  ", " ")


Cfg = Default()
Cfg.mod = "bsc,err,flt,mod,sts,thr"
Cfg.name = "zelf"
Cfg.slogan = "the self"
Cfg.version = "100"
Cfg.description = f"{Cfg.name.upper()} {Cfg.version} {Cfg.mod.upper()} {Cfg.slogan}"


from zelf import modules


class CLI(Client):

    def raw(self, txt):
        print(txt)
        sys.stdout.flush()


class Console(CLI):

    def announce(self, txt):
        pass

    def handle(self, evt):
        command(evt)
        evt.wait()

    def prompt(self):
        return input("> ")

    def poll(self):
        try:
            return self.event(self.prompt())
        except EOFError:
            _thread.interrupt_main()


def daemon():
    pid = os.fork()
    if pid != 0:
        os._exit(0)
    os.setsid()
    os.umask(0)
    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())


def scan(pkg, modnames="", initer=False, dowait=False) -> []:
    if not pkg:
        return []
    inited = []
    scanned = []
    threads = []
    for modname in spl(modnames):
        module = getattr(pkg, modname, None)
        if not module:
            continue
        scanned.append(modname)
        Commands.scan(module)
        Storage.scan(module)
        if initer:
            try:
                module.init
            except AttributeError:
                continue
            inited.append(modname)
            threads.append(launch(module.init, name=f"init {modname}"))
    if dowait:
        for thread in threads:
            thread.join()
    return inited


def wrap(func) -> None:
    if "d" in Cfg.opts:
        debug("terminal disabled!")
        return
    old = None
    try:
        old = termios.tcgetattr(sys.stdin.fileno())
    except termios.error:
        pass
    try:
        func()
    except (EOFError, KeyboardInterrupt):
        print("")
        sys.stdout.flush()
    finally:
        if old:
            termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, old)
    for exc in Errors.errors:
        traceback.print_exception(
                                  type(exc),
                                  exc,
                                  exc.__traceback__
                                 )


def main():
    parse(Cfg, " ".join(sys.argv[1:]))
    if "d" in Cfg.opts:
        daemon()
        pidfile(PIDFILE)
        scan(modules, Cfg.mod, True)
        while 1:
            time.sleep(1.0)
        return
    if "a" in Cfg.opts:
        Cfg.mod = ",".join(mods(modules.__path__[0]))
    if "v" in Cfg.opts:
        Errors.output = print
        Cfg.description = f"{Cfg.name.upper()} {Cfg.version} {Cfg.mod.upper()} {Cfg.opts.upper()}\n{Cfg.slogan}"
        debug(Cfg.description)
    if "c" in Cfg.opts:
        scan(modules, Cfg.mod, True, True)
        csl = Console()
        csl.start()
        csl.forever()
    else:
        scan(modules, Cfg.mod)
        cli = CLI()
        evt = cli.event(Cfg.otxt)
        command(evt)
        evt.wait()


if __name__ == "__main__":
    wrap(main)
