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

const shared = @import("shared");

const client = @import("client.zig");
const entity = @import("entity.zig");

const screen_width = 640;
const screen_height = 480;

var running: bool = true;

var dbg_allocator: std.heap.DebugAllocator(.{}) = undefined;
const allocator = dbg_allocator.allocator();

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

// var connection: ?std.net.Stream = undefined;

pub fn main() !void {
    std.log.info("Hello Client!", .{});

    try init();
    try znet.init();
    defer znet.deinit();
    rl.initWindow(1280, 720, "zzz");
    defer rl.closeWindow();

    const host = try znet.Host.init(.{
        .addr = null,
        .peer_limit = 1,
        .channel_limit = .max,
        .incoming_bandwidth = .unlimited,
        .outgoing_bandwidth = .unlimited,
    });
    defer host.deinit();

    const peer = try host.connect(.{
        .addr = try .init(.{
            .ip = .{ .ipv4 = "127.0.0.1" },
            .port = .{ .uint = 5000 },
        }),
        .channel_limit = .max,
        .data = 0,
    });

    defer _ = dbg_allocator.deinit();

    // connect() catch |err| switch (err) {
    //     error.ConnectionRefused => {
    //         std.log.err("server refused connection", .{});
    //     },
    //     else => {
    //         std.log.err("unexpected connect error: {}", .{err});
    //         return err;
    //     },
    // };

    // try stdout.flush();

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

    shared.chunk.updateChunk(&the_chunk);

    var elf = entity.Elf.init();

    // var send_buf: [1024]u8 = undefined;
    // var writer = if (connection) |*conn| conn.writer(&send_buf) else return;

    // try shared.protocol.sendHello(&writer.interface, .{ .msg = "Hello from client" });

    // var recv_buf: [1024]u8 = undefined;
    // var reader = if (connection) |*conn| conn.reader(&recv_buf) else return;
    // const line = try reader.interface().takeDelimiterExclusive('\n');
    // std.log.info("{s}", .{line});

    // const camera = rl.Camera{ .fovy = 45, .position = .{ .x = 0, .y = 0, .z = 10 }, .projection = .perspective, .target = .{ .x = 0, .y = 0, .z = 0 }, .up = .{ .x = 0, .y = 1, .z = 0 } };

    rl.initAudioDevice();

    if (rl.isAudioDeviceReady()) {
        std.log.info("audio device is ready!", .{});
    } else {
        std.log.info("audio device is NOT ready!", .{});
    }

    const music = try rl.loadMusicStream("assets/romantic-piano-431010.mp3");
    std.log.info("{}", .{rl.isMusicValid(music)});
    // rl.playMusicStream(music);
    std.log.info("is music playing? {}", .{rl.isMusicStreamPlaying(music)});

    while (!rl.windowShouldClose()) { // Detect window close button or ESC key
        while (try host.service(0)) |event| switch (event) {
            .connect => |data| {
                _ = data;
                // std.log.info("{}", .{data.peer});
            },
            .disconnect => |data| {
                _ = data;
                // std.log.info("{}", .{data.peer});
            },
            .receive => |data| {
                defer data.packet.deinit();

                const slice = data.packet.dataSlice();

                std.log.info("{s}", .{slice});

                var reader = bufzilla.Reader(.{}).init(slice);

                const id = try reader.readPath("id");

                std.log.info("{any}", .{id});
            },
        };

        if (peer.state() == .connected) {
            const packet = try znet.Packet.init("Hello, Server!", 0, .reliable);
            try peer.send(packet);
        }
        // _ = peer;

        // Update
        //----------------------------------------------------------------------------------
        // TODO: Update your variables here
        //----------------------------------------------------------------------------------

        rl.updateMusicStream(music);

        // Draw
        //----------------------------------------------------------------------------------
        rl.clearBackground(.sky_blue);

        rl.beginDrawing();

        rl.drawText("Congrats! You created your first window!", rl.getMouseX(), rl.getMouseY(), 20, .white);
        rl.drawRectangleLines(0, 0, 100, 100, .red);
        rl.drawFPS(0, 0);
        elf.draw();

        // rl.beginMode3D(camera);
        // rl.drawSphere(.{ .x = 0, .y = 0, .z = 0 }, 1, .red);
        // rl.endMode3D();

        rl.endDrawing();

        //----------------------------------------------------------------------------------
    }
}

fn init() !void {
    dbg_allocator = std.heap.DebugAllocator(.{}).init;
    var stdout_buffer: [1024]u8 = undefined;
    var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
    stdout = &stdout_writer.interface;
}

fn connect() !void {
    // const address = try std.net.Address.parseIp4("127.0.0.1", shared.protocol.SERVER_PORT);

    // connection = try std.net.tcpConnectToAddress(address);

    // std.log.info("Connected to server", .{});
}

//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, .{});
// }
