const std = @import("std");
const microzig = @import("microzig");
const rp2xxx = microzig.hal;
const time = rp2xxx.time;
const pwm = rp2xxx.pwm;

const LOW = 0;
const HIGH = 100;
const WRAP = 100;
const SLEEP_MS = 50;

// Compile-time pin configuration
const pin_config = rp2xxx.pins.GlobalConfiguration{
    .GPIO16 = .{ .function = .PWM0_A },
    .GPIO17 = .{ .function = .PWM0_B },
    .GPIO18 = .{ .function = .PWM1_A },
    .GPIO19 = .{ .function = .PWM1_B },
    .GPIO20 = .{ .function = .PWM2_A },
    .GPIO21 = .{ .function = .PWM2_B },
    .GPIO22 = .{ .function = .PWM3_A },
    .GPIO26 = .{ .function = .PWM5_A },
    .GPIO27 = .{ .function = .PWM5_B },
    .GPIO28 = .{ .function = .PWM6_A },
};

const pins = pin_config.pins();

const duties = &[10 + 20]u10{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1023, 512 + 256, 512, 256 + 128, 256, 128, 64, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

pub fn main() !void {
    pin_config.apply();

    inline for (std.meta.fields(@TypeOf(pins))) |field| {
        const slice = @field(pins, field.name).slice();
        slice.set_wrap(WRAP);
        slice.enable();
    }

    // This is the manual way
    // rp2xxx.gpio.num(28).set_function(.pwm);
    // const pwm6a = pwm.Pwm{
    //     .slice_number = 6,
    //     .channel = .a,
    // };
    // pwm6a.slice().set_wrap(WRAP);
    // pwm6a.slice().enable();

    // while (true) {
    //     pwm6a.set_level(25);
    //     time.sleep_ms(SLEEP_MS * 10);
    //     pwm6a.set_level(100);
    //     time.sleep_ms(SLEEP_MS * 10);
    // }

    while (true) {
        for (0..20) |i| {
            inline for (std.meta.fields(@TypeOf(pins)), 0..) |field, counter| {
                const val = map(isize, duties[i + counter], 0, 1024, LOW, HIGH);
                @field(pins, field.name).set_level(@intCast(val));
            }
            time.sleep_ms(SLEEP_MS);
        }

        for (0..20) |i| {
            inline for (&.{ "28", "27", "26", "22", "21", "20", "19", "18", "17", "16" }, 0..) |io, counter| {
                const val = map(isize, duties[i + counter], 0, 1024, LOW, HIGH);
                _ = @field(pins, "GPIO" ++ io).set_level(@intCast(val));
            }
            time.sleep_ms(SLEEP_MS);
        }
    }
}

fn map(comptime T: type, value: T, fromLow: T, fromHigh: T, toLow: T, toHigh: T) T {
    return @divTrunc((value - fromLow) * (toHigh - toLow), (fromHigh - fromLow)) + toLow;
}