const std = @import("std");

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

    var wire1 = Wire{};
    var wire2 = Wire{};
    var wire3 = Wire{};
    var wire4 = Wire{};

    var buffer1 = Buffer{ .input = &wire1, .output = &wire2 };
    var buffer2 = Buffer{ .input = &wire2, .output = &wire3 };
    var buffer3 = Buffer{ .input = &wire3, .output = &wire4 };

    var alternating = false;
    while (true) {
        alternating = !alternating;

        wire1.digital = alternating;
        wire1.analog = if (alternating) 1.0 else 0.0;
        buffer1.process();
        buffer2.process();
        buffer3.process();

        std.debug.print("input:  {}\noutput: {}\n", .{ buffer1.input, buffer3.output });
    }
}

pub const Wire = struct {
    digital: bool = false,
    analog: f32 = 0.0,

    pub fn format(self: Wire, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
        _ = .{ fmt, options };
        try writer.print("Wire({s} | {d:0>1.4})", .{
            if (self.digital) "1" else "0",
            self.analog,
        });
    }

    fn analogSet(self: *Wire, value: f32) void {
        self.analog = std.math.clamp(value, 0.0, 1.0);
    }
};

pub const Buffer = struct {
    input: *Wire,
    output: *Wire,

    pub fn process(self: *Buffer) void {
        self.output.analogSet(self.input.analog);
    }
};

pub const Not = struct {
    input: *Wire,
    output: *Wire,

    // TODO check implementation
    pub fn process(self: *Buffer) void {
        self.output.analogSet(1.0 - self.input.analog);
    }
};