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

fn discoverStream() ?[*c]u8 {
    var discovery_count: i32 = 0;

    while (discovery_count == 0) {
        const discovered = comt.omt_discovery_getaddresses(&discovery_count);
        std.log.info("Found {} streams:", .{discovery_count});

        const count: usize = @intCast(discovery_count);
        for (0..count) |i| {
            const name = discovered[i];
            std.log.info("  {s}", .{name});
        }

        if (discovery_count > 0) {
            return discovered[0];
        }
        std.Thread.sleep(1000 * 1000 * 1000);
    }
    return null;
}

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

    std.log.info("Receiving...", .{});
    comt.omt_setloggingfilename("omtrecvtest.log");

    const stream = discoverStream() orelse undefined;

    const frametype_flags = comt.OMTFrameType_Video | comt.OMTFrameType_Audio | comt.OMTFrameType_Metadata;
    const receiver = comt.omt_receive_create(
        stream,
        frametype_flags,
        comt.OMTPreferredVideoFormat_UYVYorBGRA,
        0,
    );
    defer comt.omt_receive_destroy(receiver);

    while (true) {
        const frame = comt.omt_receive(receiver, frametype_flags, 40);
        if (frame != null) {
            dumpOMTMediaFrameInfo(allocator, frame);
        }
    }
}

fn dumpOMTMediaFrameInfo(allocator: std.mem.Allocator, frame: *comt.OMTMediaFrame) void {
    std.log.info("--------- Received --------", .{});
    switch (frame.*.Type) {
        comt.OMTFrameType_Video => dumpVideoFrameInfo(frame),
        comt.OMTFrameType_Audio => dumpAudioFrameInfo(frame),
        comt.OMTFrameType_Metadata => dumpMetadataFrameInfo(allocator, frame),
        else => std.log.err("Undefined frame type", .{}),
    }
}

fn dumpVideoFrameInfo(frame: *comt.OMTMediaFrame) void {
    std.log.info("Frame: Video", .{});
    std.log.info(
        \\Timestamp: {d}
        \\Codec: {s}
        \\Width: {d}
        \\Height: {d}
        \\Stride: {d}
        \\FrameRateN: {d}
        \\FrameRateD: {d}
        \\AspectRatio: {d}
        \\ColorSpace: {s}
    , .{
        frame.Timestamp,
        omt.codecToString(frame.Codec) orelse "Invalid",
        frame.Width,
        frame.Height,
        frame.Stride,
        frame.FrameRateN,
        frame.FrameRateD,
        frame.AspectRatio,
        omt.colorSpaceToString(frame.ColorSpace),
    });
}

fn dumpAudioFrameInfo(frame: *comt.OMTMediaFrame) void {
    std.log.info("Frame: Audio", .{});
    std.log.info(
        \\Timestamp: {d}
        \\SampleRate: {d}
        \\Channels: {d}
        \\SamplesPerChannel: {d}
    , .{ frame.Timestamp, frame.SampleRate, frame.Channels, frame.SamplesPerChannel });
}

fn dumpMetadataFrameInfo(allocator: std.mem.Allocator, frame: *comt.OMTMediaFrame) void {
    std.log.info("Frame: Metadata", .{});
    if (frame.FrameMetadataLength == 0 or frame.FrameMetadata == null) {
        std.log.info("None", .{});
        return;
    }

    const metadata_length: usize = @intCast(frame.FrameMetadataLength);
    const buffer = allocator.alloc(u8, metadata_length) catch @panic("OOM");
    defer allocator.free(buffer);

    std.debug.print("metadata: {?p}\n", .{frame.FrameMetadata});
    std.mem.copyForwards(u8, buffer, @ptrCast(@alignCast(frame.FrameMetadata)));
    std.log.info("{s}", .{buffer});
}
