const std = @import("std");

pub fn calcExprSource(allocator: std.mem.Allocator) ![]const u8 {
    return try allocator.dupe(u8,
        \\pub const CalcExpr = struct {
        \\    const Self = @This();
        \\    pub const Unit = enum {
        \\        px,
        \\        em,
        \\        rem,
        \\        percent,
        \\        pub fn toString(self: Unit) []const u8 {
        \\            return switch (self) {
        \\                .percent => "%",
        \\                else => @tagName(self),
        \\            };
        \\        }
        \\    };
        \\
        \\    pub const Op = enum {
        \\        add,
        \\        sub,
        \\        mul,
        \\        div,
        \\        pub fn toString(self: Op) []const u8 {
        \\            return switch (self) {
        \\                .add => "+",
        \\                .sub => "-",
        \\                .mul => "*",
        \\                .div => "/",
        \\            };
        \\        }
        \\    };
        \\
        \\    buf: [64]u8 = undefined,
        \\    len: u8 = 0,
        \\
        \\    fn init(t: []const u8) Self {
        \\        var self: Self = undefined;
        \\        if (t.len > self.buf.len) @panic("calc expression too complex");
        \\        std.mem.copyForwards(u8, self.buf[0..t.len], t);
        \\        self.len = @intCast(t.len);
        \\        return self;
        \\    }
        \\
        \\    fn text(self: *const Self) []const u8 {
        \\        return self.buf[0..self.len];
        \\    }
        \\
        \\    fn leaf(value: []const u8) Self {
        \\        return Self.init(value);
        \\    }
        \\
        \\    fn unitLeaf(unit: Unit, value: f32) Self {
        \\        var temp: [64]u8 = undefined;
        \\        const written = std.fmt.bufPrint(&temp, "{d}{s}", .{ value, unit.toString() }) catch @panic("calc expression too complex");
        \\        return Self.leaf(written);
        \\    }
        \\
        \\    fn numberLeaf(value: f32) Self {
        \\        var temp: [64]u8 = undefined;
        \\        const written = std.fmt.bufPrint(&temp, "{d}", .{value}) catch @panic("calc expression too complex");
        \\        return Self.leaf(written);
        \\    }
        \\
        \\    fn rawLeaf(value: []const u8) Self {
        \\        return Self.leaf(value);
        \\    }
        \\
        \\    pub fn px(value: f32) Self {
        \\        return Self.unitLeaf(.px, value);
        \\    }
        \\
        \\    pub fn em(value: f32) Self {
        \\        return Self.unitLeaf(.em, value);
        \\    }
        \\
        \\    pub fn rem(value: f32) Self {
        \\        return Self.unitLeaf(.rem, value);
        \\    }
        \\
        \\    pub fn percent(value: f32) Self {
        \\        return Self.unitLeaf(.percent, value);
        \\    }
        \\
        \\    pub fn raw(value: []const u8) Self {
        \\        return Self.rawLeaf(value);
        \\    }
        \\
        \\    pub fn number(value: f32) Self {
        \\        return Self.numberLeaf(value);
        \\    }
        \\
        \\    fn combine(self: Self, other: Self, op: Op) Self {
        \\        var temp: [128]u8 = undefined;
        \\        const lhs = self.text();
        \\        const rhs = other.text();
        \\        const op_text = op.toString();
        \\        const needed = lhs.len + rhs.len + op_text.len + 4;
        \\        if (needed > temp.len) @panic("calc expression too complex");
        \\
        \\        var i: usize = 0;
        \\        temp[i] = '(';
        \\        i += 1;
        \\        std.mem.copyForwards(u8, temp[i..][0..lhs.len], lhs);
        \\        i += lhs.len;
        \\        temp[i] = ' ';
        \\        i += 1;
        \\        std.mem.copyForwards(u8, temp[i..][0..op_text.len], op_text);
        \\        i += op_text.len;
        \\        temp[i] = ' ';
        \\        i += 1;
        \\        std.mem.copyForwards(u8, temp[i..][0..rhs.len], rhs);
        \\        i += rhs.len;
        \\        temp[i] = ')';
        \\        i += 1;
        \\
        \\        return Self.init(temp[0..i]);
        \\    }
        \\
        \\    pub fn add(self: Self, other: Self) Self {
        \\        return self.combine(other, .add);
        \\    }
        \\
        \\    pub fn sub(self: Self, other: Self) Self {
        \\        return self.combine(other, .sub);
        \\    }
        \\
        \\    pub fn mul(self: Self, factor: f32) Self {
        \\        return self.combine(Self.numberLeaf(factor), .mul);
        \\    }
        \\
        \\    pub fn div(self: Self, factor: f32) Self {
        \\        return self.combine(Self.numberLeaf(factor), .div);
        \\    }
        \\
        \\    pub fn format(self: *const Self, w: *std.Io.Writer) std.Io.Writer.Error!void {
        \\        try w.writeAll(self.text());
        \\    }
        \\};
    );
}
