const std = @import("std");
const zm = @import("zmath");
const znet = @import("znet");

const shared = @import("shared");

const chunk = @import("chunk.zig");
const server = @import("server.zig");

const dt          : f32 = 1.0 / 200.0;
var   t           : f32 = 0;
var   gt          : f32 = 0;
var   accumulator : f32 = 0;
var   k           : f32 = 1.0;
var   frame       : i32 = 0;

var stdout: *std.io.Writer = undefined;

pub fn main() !void {
    std.log.info("Hello Server!", .{});
    var dbg_allocator = std.heap.DebugAllocator(.{}).init;
    defer _ = dbg_allocator.deinit();
    const allocator = dbg_allocator.allocator();

    var stdout_buffer: [1024]u8 = undefined;
    var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
    stdout = &stdout_writer.interface;

    try stdout.flush();

    try znet.init();
    defer znet.deinit();

    server.host = try znet.Host.init(.{
        .addr = try .init(.{
            .ip = .any,
            .port = .{ .uint = 5000 },
        }),
        .peer_limit = 32,
        .channel_limit = .max,
        .incoming_bandwidth = .unlimited,
        .outgoing_bandwidth = .unlimited,
    });

    // const address = try std.net.Address.parseIp4("127.0.0.1", shared.protocol.SERVER_PORT);
    // var tcp_server = try address.listen(.{});

    // defer tcp_server.deinit();

    var the_chunk = try shared.chunk.initChunk(allocator);
    defer shared.chunk.deinitChunk(&the_chunk, allocator);

    var old_time = std.time.nanoTimestamp();

    while (true) {
        const new_time = std.time.nanoTimestamp();
        var frame_time : f32 = @as(f32, @floatFromInt(new_time - old_time)) / 1_000_000_000.0;
        old_time = new_time;

        if (frame_time > 0.25)
            frame_time = 0.25;

        t += frame_time;
        accumulator += frame_time * k;

        // std.log.info("t: {}", .{t});

        while (try server.host.service(0)) |event| switch (event) {
            .connect => |data| {
                _ = data;
                // std.log.info("{}", .{data.peer});
            },
            .disconnect => |data| {
                _ = data;
                // std.log.info("{}", .{data.peer});
            },
            .receive => |data| {
                std.log.info("{s}", .{data.packet.dataSlice()});
                defer data.packet.deinit();
            },
        };

        // const connection = try tcp_server.accept();
        // defer connection.stream.close();

        // var recv_buffer: [4096]u8 = undefined;
        // var reader = connection.stream.reader(&recv_buffer);

        // const line = try reader.interface().takeDelimiterExclusive('\n');
        // std.log.info("Received: {s}", .{line});

        // var send_buffer: [4096]u8 = undefined;
        // var writer = connection.stream.writer(&send_buffer);
        // const w = &writer.interface;
        // try shared.protocol.sendHello(w, .{ .msg = "Hello from server!" });

        try chunk.spawn(&the_chunk, shared.entity.Elf, allocator, .{
            .pos = zm.f32x4(1, 1, 0, 0),
            .hp = 10,
        });

        // try chunk.spawn(&the_chunk, shared.entity.Monster, allocator, .{
        //     .pos = zm.f32x4(1, 1, 0, 0),
        //     .hp = 20,
        // }, w);

        // try chunk.spawn(&the_chunk, shared.entity.Projectile, allocator, .{
        //     .pos = zm.f32x4(0, 0, 0, 0),
        //     .vel = zm.f32x4(0.2, 0, 0, 0),
        // }, w);

        shared.chunk.updateChunk(&the_chunk);
    }
}

//fn handle_connection(connection: std.net.Server.Connection) !void {}

// test "simple test" {
//     const gpa = std.testing.allocator;
//     var list: std.ArrayList(i32) = .empty;
//     defer list.deinit(gpa); // Try commenting this out and see if zig detects the memory leak!
//     try list.append(gpa, 42);
//     try std.testing.expectEqual(@as(i32, 42), list.pop());
// }

// test "fuzz example" {
//     const Context = struct {
//         fn testOne(context: @This(), input: []const u8) anyerror!void {
//             _ = context;
//             // Try passing `--fuzz` to `zig build test` and see if it manages to fail this test case!
//             try std.testing.expect(!std.mem.eql(u8, "canyoufindme", input));
//         }
//     };
//     try std.testing.fuzz(Context{}, Context.testOne, .{});
// }
