import * as http from "http";
import * as url from "url";
import * as querystring from "querystring";
import { ActiveLogger } from "@activeledger/activelogger";
import { ActiveGZip } from "@activeledger/activeutilities";
export class ActiveHttpd {
    constructor() {
        this.routes = [];
    }
    use(url, method, handler) {
        let path = url == "/" ? [url] : url.split("/").filter(url => url);
        this.routes.push({
            path,
            pac: this.pathAstriskCount(path),
            method,
            handler
        });
    }
    listen(port, log = false) {
        let httpd = this;
        this.server = http.createServer();
        this.server.on("request", async function (req, res) {
            if (log)
                ActiveLogger.info(`${req.method} - ${req.url}`);
            const parsedUrl = url.parse(req.url);
            const pathSegments = parsedUrl.pathname
                .split("/")
                .filter(url => url);
            if (!pathSegments.length) {
                pathSegments.push("/");
            }
            if (req.method == "POST" || req.method == "PUT") {
                let body = [];
                req.on("data", chunk => {
                    body.push(chunk);
                });
                req.on("end", async () => {
                    let data;
                    if (req.headers["content-encoding"] == "gzip") {
                        data = (await ActiveGZip.ungzip(Buffer.concat(body))).toString();
                    }
                    else {
                        data = Buffer.concat(body).toString();
                    }
                    if (req.headers["content-type"] == "application/json") {
                        data = JSON.parse(data);
                    }
                    httpd.processListen({
                        url: pathSegments,
                        query: querystring.parse(parsedUrl.query),
                        body: data
                    }, req, res);
                });
            }
            else {
                httpd.processListen({
                    url: pathSegments,
                    query: querystring.parse(parsedUrl.query)
                }, req, res);
            }
        });
        this.server.listen(port);
    }
    async processListen(incoming, req, res) {
        let handler = this.findHandler(incoming.url.slice(0), req.method, this.routes);
        if (handler) {
            try {
                const data = await handler(incoming, req, res);
                if (data) {
                    if (data == "handled") {
                        return;
                    }
                    if (!res.statusCode) {
                        res.statusCode = 200;
                    }
                    if (Buffer.isBuffer(data)) {
                        res.write(data);
                    }
                    else {
                        this.writeAsHttpData(data, res);
                    }
                    res.end();
                }
                else {
                    res.statusCode = 404;
                    res.write("404");
                    res.end();
                }
            }
            catch (error) {
                res.statusCode = error.status || error.statusCode || 500;
                this.writeAsHttpData(error, res);
                res.end();
            }
        }
        else {
            res.statusCode = 404;
            res.write("404");
            res.end();
        }
    }
    writeAsHttpData(data, res) {
        if (typeof data == "object") {
            res.setHeader("Content-type", ActiveHttpd.mimeType[".json"]);
            res.write(JSON.stringify(data));
        }
        else {
            res.write(data);
        }
    }
    findHandler(path, method, routes, position = 0) {
        let search = path.shift();
        let handlers = [];
        let i = routes.length;
        while (i--) {
            let route = routes[i];
            if (route.method == method || route.method == "ALL") {
                if (position >= route.path.length) {
                    if (route.path[route.path.length - 1] == "**") {
                        handlers.push(route);
                    }
                }
                else {
                    if (route.path[position] == "*" ||
                        route.path[position] == "**" ||
                        route.path[position] == search) {
                        handlers.push(route);
                    }
                }
            }
        }
        if (handlers.length) {
            if (path.length) {
                return this.findHandler(path, method, handlers, ++position);
            }
            else {
                return this.selectSingleHandler(handlers, position);
            }
        }
        else {
            return null;
        }
    }
    selectSingleHandler(handlers, position) {
        if (handlers.length) {
            if (handlers.length > 1) {
                handlers = handlers.sort((a, b) => {
                    if (a.pac > b.pac) {
                        return -1;
                    }
                    return 1;
                });
            }
            let i = handlers.length;
            while (i--) {
                if (handlers[i].path.length - 1 == position) {
                    return handlers[i].handler;
                }
                if (handlers[i].path[handlers[i].path.length - 1] == "**") {
                    return handlers[i].handler;
                }
            }
        }
        return null;
    }
    pathAstriskCount(path) {
        let c = 0;
        for (let i = 0; i < path.length; i++) {
            const element = path[i];
            if (element == "*") {
                c++;
            }
            else if (element == "**") {
                c += 2;
            }
            else {
                break;
            }
        }
        return c;
    }
}
ActiveHttpd.mimeType = {
    ".html": "text/html",
    ".js": "text/javascript",
    ".json": "application/json",
    ".css": "text/css",
    ".png": "image/png",
    ".jpg": "image/jpeg",
    ".ttf": "aplication/font-sfnt",
    ".woff": "font/woff"
};
//# sourceMappingURL=httpd.js.map