const std = @import("std");
const Io = std.Io;

fn handle_websocket(websocket: *std.http.Server.WebSocket) void {
    websocket.writeMessage("welcome", .text) catch return;

    while (true) {
        const sm = websocket.readSmallMessage() catch break;
        std.debug.print("websocket received: {s}\n", .{sm.data});
        websocket.writeMessage(sm.data, sm.opcode) catch break;
    }
}

fn handle_request(io: std.Io, stream: std.Io.net.Stream) void {
    var recv_buffer: [999]u8 = undefined;
    var send_buffer: [100]u8 = undefined;

    defer stream.close(io);

    var connection_br = stream.reader(io, &recv_buffer);
    var connection_bw = stream.writer(io, &send_buffer);
    var server = std.http.Server.init(&connection_br.interface, &connection_bw.interface);

    while (true) {
        var req = server.receiveHead() catch break;

        switch (req.upgradeRequested()) {
            .websocket => |ws| {
                std.debug.print("ws: {s}\n", .{ws.?});
                var websocket = req.respondWebSocket(.{ .key = ws.? }) catch break;
                std.debug.print("handling websocket business\n", .{});
                handle_websocket(&websocket);
                std.debug.print("done handling websocket business\n", .{});
            },
            else => {},
        }

        req.respond(
            \\ <script>
            \\ const socket = new WebSocket("ws://localhost:1234");
            \\ socket.addEventListener("open", (event) => {
            \\   socket.send("Hello Server!");
            \\ });
            \\ socket.addEventListener("message", (event) => {
            \\   console.log("Message from server ", event.data);
            \\ });
            \\ </script>
            \\ <p>hallo</p>
        , .{ .status = .ok }) catch break;
    }

    std.debug.print("closing http thread\n", .{});
}

pub fn main(init: std.process.Init) !void {
    // Prints to stderr, unbuffered, ignoring potential errors.
    std.debug.print("All your {s} are belong to us.\n", .{"codebase"});

    // This is appropriate for anything that lives as long as the process.
    const arena: std.mem.Allocator = init.arena.allocator();

    // Accessing command line arguments:
    const args = try init.minimal.args.toSlice(arena);
    for (args) |arg| {
        std.log.info("arg: {s}", .{arg});
    }

    // In order to do I/O operations need an `Io` instance.
    const io = init.io;

    const address = try std.Io.net.IpAddress.parseIp4("0.0.0.0", 1234);
    var net_server = try address.listen(io, .{ .reuse_address = true });

    while (true) {
        const stream = try net_server.accept(io);

        _ = io.async(handle_request, .{ io, stream });

        std.debug.print("created http thread\n", .{});
    }
}
