const std = @import("std");
const pico = @import("microzig").hal;

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

// Compile-time pin configuration
const pin_config = pico.pins.GlobalConfiguration{
    .GPIO15 = .{ .function = .PWM7_B },
    .GPIO14 = .{ .function = .PWM7_A },
    .GPIO13 = .{ .function = .PWM6_B },
    .GPIO12 = .{ .function = .PWM6_A },
    .GPIO11 = .{ .function = .PWM5_B },
    .GPIO10 = .{ .function = .PWM5_A },
    .GPIO9 = .{ .function = .PWM4_B },
    .GPIO8 = .{ .function = .PWM4_A },
    .GPIO7 = .{ .function = .PWM3_B },
    .GPIO6 = .{ .function = .PWM3_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();

    // ADC setup
    pico.adc.configure_gpio_pin_num(.ain0);
    pico.adc.apply(.{ .temp_sensor_enabled = false });

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

    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));
            }
            const sample = pico.adc.convert_one_shot_blocking(.ain0) catch unreachable;
            // TODO make it work with map(u12, sample, 0, 4095, 10, 1000)
            pico.time.sleep_ms(@min(500, sample));
        }

        for (0..20) |i| {
            inline for (&.{ "15", "14", "13", "12", "11", "10", "9", "8", "7", "6" }, 0..) |io, counter| {
                const val = map(isize, duties[i + counter], 0, 1024, LOW, HIGH);
                _ = @field(pins, "GPIO" ++ io).set_level(@intCast(val));
            }
            const sample = pico.adc.convert_one_shot_blocking(.ain0) catch unreachable;
            pico.time.sleep_ms(@min(500, sample));
        }
    }
}

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;
}