#!/usr/bin/env qore
# -*- mode: qore; indent-tabs-mode: nil -*-

%enable-all-warnings
%new-style
%allow-debugger

%requires qore >= 1.0
%requires HttpServer
%requires DebugProgramControl
%requires DebugHandler
%requires Logger

%exec-class DebugWrapper

class WebSocketDebugProgramControlHost inherits WebSocketDebugProgramControl {
    public {
        DebugWrapper dw;
    }
    constructor(DebugWrapper n_dw) : WebSocketDebugProgramControl("qdb-server v1.0") {
        dw = n_dw;
    }
}

class DebugWrapper {
    private {
        hash opts = (
            'help': 'h,help',
            'verbose': 'v,verbose:+',
            'run': 'r,run',
            'listen': 'l,listen=s@',
            'path': 'p,path=s',
        );
        DebugLogger logger;
        WebSocketDebugHandler wsHandler;
        HttpServer mServer;
        WebSocketDebugProgramControlHost dpc;
    }

    constructor() {
        WrapperGetOpt g(opts);
        # first we need split debug args and program args, 'qore-dbg debug-args program-name program-args'
        # it is not trivial, e.g.
        #    qore-dbg -v -l xxx xxx     #the second xxx is program name
        #   qore-dbg -v -h xxx xxx     #the first xxx is program name
        #    qore-dbg -v --listen=xxx xxx     #the second xxx is program name
        #    qore-dbg -v --listen xxx xxx     #the second xxx is program name
        # GetOpt does not support such a parse function
        #
        list dargs;
        hash opt;
        *string fileName;
        g.split(cast<list<string>>(ARGV), \dargs, \fileName, \ARGV);

        try {
            opt = g.parse2(\dargs);
        } catch (hash<ExceptionInfo> ex) {
            stderr.printf("%s: %s\n", ex.err, ex.desc);
            help(-1);
        }

        if (opt.help) {
            help();
        }

        logger = new DebugLogger();
        logger.verbose = opt.verbose + 1;
        try {
            logger.log(DUV_DEBUG, "create DebugProgram");
            dpc = new WebSocketDebugProgramControlHost(self);
            dpc.logger = logger;
            logger.log(DUV_DEBUG, "create WebSocketDebugHandler");
            AbstractWebSocketDebugHandler wsHandler = new WebSocketDebugHandler(dpc);
            wsHandler.logger = logger;

            logger.log(DUV_DEBUG, "create HttpServer");

            Logger http_logger("my-logger", LoggerLevel::getLevelInfo());
            if (logger.verbose >= DUV_DEBUG) {
                http_logger.addAppender(new MyAppender());
            }
            hash<HttpServerOptionInfo> http_opts = <HttpServerOptionInfo>{
                "logger": http_logger,
                "debug": True,
            };
            mServer = new HttpServer(http_opts);
            mServer.setHandler("ws-handler", opt.path ?? "", wsHandler.getContentType(), wsHandler);
            mServer.setDefaultHandler("ws-handler", wsHandler);
            if (opt.listen) {
                foreach string l in (opt.listen) {
                    mServer.addListener(http_get_listener_options_from_bind(l));
                    logger.log(DUV_INFO, "started debug server listener: %s", l);
                }
            } else {
                int port = mServer.addListener(0).port;
                logger.log(DUV_DEBUG, "debug server listening on port %d", port);
            }

            if (exists fileName) {
                Program pgm = dpc.createProgram(fileName, opt, ARGV);
                logger.log(DUV_INFO, "run program");
                background pgm.run();
            }
            # background or http server uses own thread, loop
        } catch (hash<ExceptionInfo> e) {
            stderr.printf("%s: %s\n", e.err, e.desc);
            exit(-1);
        }
    }

    public httplog(fmt) {
        logger.log(DUV_INFO, vsprintf(fmt, argv));
    }

    public httperr(fmt) {
        stderr.vprintf(fmt, argv);
    }

    private help(int exCode=1) {
        printf("usage: %s [options] <program> [<program params> ...]\n"
            "  where <file> is program being debugged, use \"/dev/stdin\" to get code from stdin\n"
            "  -v     verbose\n"
            "  -h     help\n"
            "  -l,--listen=<addr:port>  listener address, multiple listeners is supported, default: %s\n"
            "  -l,--listen=/<sock>      if begins with a \"/\" character then it is assumed to be a UNIX socket\n"
            "  -p,--path=<path>    URL path for HTTP handler, default is empty string\n"
            "\n"
            "Example:\n"
            "  %s -v -l 127.0.0.1:10000 -p debug myfile.q -my -program params\n"
            "\n"
            ,
            get_script_name(),
            0,
            get_script_name()

        );
        exit(exCode);
    }

    public dummy() {
    }
}

class MyAppender inherits LoggerAppenderWithLayout {
    constructor() : LoggerAppenderWithLayout("test", new LoggerLayoutPattern("%d T%t [%p]: %m%n")) {
        open();
    }

    processEventImpl(int type, auto params) {
        switch (type) {
            case EVENT_LOG:
                print(params);
                break;
        }
    }
}
