const std = @import("std");
const omt = @cImport(@cInclude("libomt.h"));

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = gpa.allocator();

    std.log.info("Sending...", .{});
    omt.omt_setloggingfilename("omtsendtest.log");

    const sender = omt.omt_send_create("Omtoy Sender", omt.OMTQuality_Default) orelse undefined;
    defer omt.omt_send_destroy(sender);

    std.log.info("Sender created: Omtoy Sender", .{});

    {
        var sender_info = std.mem.zeroes(omt.OMTSenderInfo);

        const product_name = "Omtoy";
        const manufacturer = "Kasper Toy Productions";
        const version = "0.1";

        @memcpy(sender_info.ProductName[0..product_name.len], product_name);
        @memcpy(sender_info.Manufacturer[0..manufacturer.len], manufacturer);
        @memcpy(sender_info.Version[0..version.len], version);

        omt.omt_send_setsenderinformation(sender, &sender_info);
    }

    const width = 1920;
    const height = 1080;
    const stride = width * 2;
    const data_length = stride * height;

    const frame_buffer = allocator.alloc(u8, data_length) catch undefined;
    defer allocator.free(frame_buffer);

    var video_frame = omt.OMTMediaFrame{
        .Type = omt.OMTFrameType_Video,
        .Width = width,
        .Height = height,
        .Codec = omt.OMTCodec_UYVY,
        .Timestamp = -1,
        .ColorSpace = omt.OMTColorSpace_BT709,
        .Flags = omt.OMTVideoFlags_None,
        .Stride = width * 2,
        .DataLength = width * 2 * height,
        .Data = @ptrCast(frame_buffer),
        .FrameRateN = 60000,
        .FrameRateD = 1000,
        .AspectRatio = @as(f32, width) / @as(f32, height),
        .CompressedData = null,
        .CompressedLength = 0,
        .FrameMetadata = null,
        .FrameMetadataLength = 0,
    };

    const content = try std.fs.cwd().readFileAlloc("california-1080-uyvy.yuv", allocator, .unlimited);
    defer allocator.free(content);

    const audio_samples = 800;
    const audio_buffer = try allocator.alloc(f32, audio_samples);
    defer allocator.free(audio_buffer);

    var prng = std.Random.DefaultPrng.init(80085);
    const rng = prng.random();
    fillAudioBuffer(rng, audio_buffer);

    var audio_frame = omt.OMTMediaFrame{
        .Type = omt.OMTFrameType_Audio,
        .Timestamp = -1,
        .Codec = omt.OMTCodec_FPA1,
        .SampleRate = 48000,
        .Channels = 2,
        .SamplesPerChannel = audio_samples,
        .Data = @ptrCast(audio_buffer),
        .DataLength = @intCast(audio_buffer.len * 2),
        .FrameMetadata = null,
        .FrameMetadataLength = 0,
    };

    var tally = omt.OMTTally{ .preview = 0, .program = 0 };
    var stats = std.mem.zeroes(omt.OMTStatistics);

    var frame_count: i32 = 0;
    var bytes: i32 = 0;

    const two_lines = try allocator.alloc(u8, stride * 2);
    defer allocator.free(two_lines);

    @memset(two_lines, 255);
    var line_position: usize = 0;

    // send 10000 frames
    while (frame_count < 10000) : (frame_count += 1) {
        @memcpy(frame_buffer, content);

        // TODO: copy the white line over here
        line_position += stride * 2;
        if (line_position >= video_frame.DataLength) {
            line_position = 0;
        }

        bytes += omt.omt_send(sender, &video_frame);

        if (@mod(frame_count, 60) == 0) {
            std.log.info("sent: {}", .{bytes});

            _ = omt.omt_send_gettally(sender, 0, &tally);
            std.log.info("tally preview: {}, program: {}", .{ tally.preview, tally.program });

            const frame = omt.omt_send_receive(sender, 0);
            if (frame != null) {
                const metadata = frame.*.Data;
                if (metadata != null) {
                    const metastr = @as([]u8, @ptrCast(metadata));
                    std.log.info("meta: {s}", .{metastr});
                }
            }

            const connections = omt.omt_send_connections(sender);
            std.log.info("connections: {}", .{connections});

            omt.omt_send_getvideostatistics(sender, &stats);
            std.log.info("statistics sent bytes: {}, frames: {}", .{ stats.BytesSent, stats.Frames });
        }

        _ = omt.omt_send(sender, &audio_frame);
        fillAudioBuffer(rng, audio_buffer);
    }
}

fn fillAudioBuffer(rng: std.Random, buffer: []f32) void {
    for (buffer) |*val| {
        val.* = rng.float(f32) * 2 - 1.0;
    }
}
