const std = @import("std");
const enet = @import("enet");

const DH = std.crypto.dh.X25519;
const AES = std.crypto.aead.aes_gcm.Aes256Gcm;
const SHA = std.crypto.hash.sha2.Sha256;

pub fn main() !void {
    if (enet.enet_initialize() != 0) return error.ENetInitFailed;
    defer enet.enet_deinitialize();

    const addr = enet.ENetAddress{
        .host = enet.ENET_HOST_ANY,
        .port = enet.ENET_PORT_ANY,
    };
    const host = enet.enet_host_create(&addr, 32, 1, 0, 0) orelse return error.ENetHostCreateFailed;
    defer enet.enet_host_destroy(host);

    var server_addr = enet.ENetAddress{
        .host = enet.ENET_HOST_ANY,
        .port = 9405,
    };
    _ = enet.enet_address_set_host(&server_addr, "localhost");
    const peer = enet.enet_host_connect(host, &server_addr, 1, 0);
    _ = peer;

    var event: enet.ENetEvent = undefined;

    while (true) {
        while (enet.enet_host_service(host, &event, 100) > 0) {
            switch (event.type) {
                enet.ENET_EVENT_TYPE_CONNECT => {
                    std.debug.print("client: connected to peer {any}\n", .{event.peer.*.address});
                },
                else => {
                    std.debug.print("client: other event...\n", .{});
                },
            }
        }
    }
}

// it even works in ReleaseFast so I think it's ok.
test "in-place-encrypt" {
    const al = std.testing.allocator;

    var key: [AES.key_length]u8 = undefined;
    std.crypto.random.bytes(&key);
    const iv: [AES.nonce_length]u8 = undefined;
    std.crypto.random.bytes(&key);

    var tag: [AES.tag_length]u8 = undefined;

    // 1mb message. very big. should find an error if one exists.
    const original_message: []u8 = try al.alloc(u8, 1 << 20);
    defer al.free(original_message);
    std.crypto.random.bytes(original_message);

    const message = try al.dupe(u8, original_message);
    defer al.free(message);

    const ad: []u8 = try al.dupe(u8, "u");
    defer al.free(ad);

    AES.encrypt(message, &tag, message, ad, iv, key);
    try std.testing.expect(!std.mem.eql(u8, message, original_message));
    try AES.decrypt(message, message, tag, ad, iv, key);
    try std.testing.expect(std.mem.eql(u8, message, original_message));
}
