const std = @import("std");

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

    // var input = Wire{ .digital = false, .analog = 1.0 };
    // var input_buffer = Not{ .invert_output = false, .input = &input };

    // // analog in
    // var wire1 = Wire{};
    // var wire2 = Wire{};
    // var wire3 = Wire{};

    // // orange battery
    // var wire4 = Wire{};
    // var wire5 = Wire{};
    // var wire6 = Wire{};

    // // cblueorange battery
    // var wire7 = Wire{};
    // var wire8 = Wire{};
    // var wire9 = Wire{};

    // // red battery
    // var wire10 = Wire{};
    // var wire11 = Wire{};

    // // grey OR gates
    // var wire12 = Wire{};
    // var wire13 = Wire{};
    // var wire14 = Wire{};

    // var wire15 = Wire{};

    // // pink OR gates
    // var wire16 = Wire{};
    // var wire17 = Wire{};
    // var wire18 = Wire{};

    // // right side flippity floppities
    // var wire19 = Wire{};
    // var wire20 = Wire{};
    // var wire21 = Wire{};
    // var wire22 = Wire{};
    // var wire23 = Wire{};

    // var battery1 = Battery{ .value = -0.5 };
    // var battery2 = Battery{ .value = 0.5 };
    // var battery3 = Battery{ .value = -1.0 };

    // var or1_inputs = [_]*Wire{ &wire1, &wire2, &wire3 };
    // var or1 = Or{ .inputs = &or1_inputs };
    // var or2_inputs = [_]*Wire{ &};
    // var or2 = Or{ .inputs = &or2_inputs };

    // var or3_inputs = [_]*Wire{};
    // var or3 = Or{ .inputs = &or3_inputs };
    // var or4_inputs = [_]*Wire{};
    // var or4 = Or{ .inputs = &or4_inputs };
    // var or5_inputs = [_]*Wire{};
    // var or5 = Or{ .inputs = &or5_inputs };

    // var or6_inputs = [_]*Wire{};
    // var or6 = Or{ .inputs = &or6_inputs };
    // var and1 = And{};
    // var or7_inputs = [_]*Wire{};
    // var or7 = Or{ .inputs = &or7_inputs };
    // var and2 = And{};
    // var or8_inputs = [_]*Wire{};
    // var or8 = Or{ .inputs = &or8_inputs };

    // std.debug.print("{}\n", .{wire3});
}

pub const Component = struct {
    x: u16 = 0,
    y: u16 = 0,

    state: *anyopaque,
    processFn: *const fn (*Component) void,
};

pub const Circuit = struct {
    width: u16 = 8,
    height: u16 = 8,
    components: std.ArrayList(Component),
};

pub const Battery = struct {
    pub fn process(component: *Component) Signal {}
};

pub const Signal = struct {
    digital: i2,
    analog: f32,
    color: u24,
};

// 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 Battery = struct {
//     // component: Component = .{},

//     output: *Wire,

//     value: f32,
//     invert_output: bool = false,

//     pub fn process(self: *Battery) void {
//         if (self.invert_output) {
//             self.output.digital = self.value != 0.0;
//             self.output.analogSet(self.value);
//         } else {
//             self.output.digital = self.value == 0.0;
//             self.output.analogSet(1.0 - @abs(self.value));
//         }
//     }
// };

// pub const Not = struct {
//     // component: Component = .{},

//     input: *Wire,
//     output: *Wire,

//     invert_output: bool = true,

//     pub fn process(self: *Not) void {
//         if (self.invert_output) {
//             self.output.digital = !self.input.digital;
//             self.output.analogSet(1.0 - @abs(self.input.analog));
//         } else {
//             self.output.digital = self.input.digital;
//             self.output.analogSet(self.input.analog); // TODO does the output get clamped here?
//         }
//     }
// };

// pub const And = struct {
//     // component: Component,

//     inputs: []*Wire,
//     output: *Wire,

//     // if false, is in Minimum Input mode
//     // if true, is in Multiply Inputs mode
//     arithmetic_mode: bool = false,

//     // TODO check implementation
//     pub fn process(self: *And) void {
//         if (self.arithmetic_mode) {
//             self.output.digital = self.inputs[0].digital;
//             self.output.analog = self.inputs[0].analog;
//             for (self.inputs[1..]) |input| {
//                 self.output.digital = self.output.digital and input.digital;
//                 self.output.analog *= input.analog;
//             }
//         } else {
//             self.output.digital = self.inputs[0].digital;
//             self.output.analog = self.inputs[0].analog;
//             for (self.inputs[1..]) |input| {
//                 self.output.digital = self.output.digital and input.digital;
//                 self.output.analog = @min(self.output.analog, input.analog);
//             }
//         }
//         self.output.analog = std.math.clamp(self.output.analog, 0.0, 1.0);
//         // var digital = self.inputs[0].digital;
//         // if (digital) for (1..self.inputs.len - 1) |i| if (!self.inputs[i].digital) {
//         //     digital = false;
//         //     break;
//         // };
//         // self.output.digital = digital;

//         // var best = self.inputs[0].analog;
//         // for (self.inputs[1..]) |input| best = @min(best, input.analog);
//         // self.output.analog = best;
//     }
// };

// pub const Or = struct {
//     // component: Component,

//     inputs: []*Wire,
//     output: *Wire,

//     // if false, is in Maximum Input mode
//     // if true, is in Add Inputs mode
//     arithmetic_mode: bool = false,

//     // TODO check implementation
//     pub fn process(self: *Or) void {
//         if (self.arithmetic_mode) {
//             self.output.digital = self.inputs[0].digital;
//             self.output.analog = self.inputs[0].analog;
//             for (self.inputs[1..]) |input| {
//                 self.output.digital = self.output.digital and input.digital;
//                 self.output.analog += input.analog;
//             }
//         } else {
//             self.output.digital = self.inputs[0].digital;
//             self.output.analog = self.inputs[0].analog;
//             for (self.inputs[1..]) |input| {
//                 self.output.digital = self.output.digital and input.digital;
//                 self.output.analog = @max(self.output.analog, input.analog);
//             }
//         }
//         self.output.analog = std.math.clamp(self.output.analog, 0.0, 1.0);
//         // self.output.digital = 1;
//         // for (self.inputs) |input| if (input.digital) {
//         //     self.output.digital = true;
//         //     break;
//         // };
//     }
// };