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

const FULL_AXIS = 4096; // u12
const QUARTER = FULL_AXIS / 4;

const pin_conf = pico.pins.GlobalConfiguration{
    .GPIO22 = .{ .name = "neopixel", .function = .PIO0 },
    .GPIO26 = .{ .name = "xaxis", .function = .ADC0 },
    .GPIO27 = .{ .name = "yaxis", .function = .ADC1 },
    .GPIO28 = .{ .name = "button", .direction = .in, .pull = .up },
};

const pins = pin_conf.pins();

const pio: pico.pio.Pio = .pio0;
const sm: pico.pio.StateMachine = .sm0;
const led_pin = pico.gpio.num(22);

pub fn main() !void {
    pin_conf.apply();
    pio.gpio_init(led_pin);
    pio.sm_set_pindir(sm, @intFromEnum(led_pin), 1, .out);

    const cycles_per_bit: comptime_int = ws2812_program.defines[0].value + //T1
        ws2812_program.defines[1].value + //T2
        ws2812_program.defines[2].value; //T3
    const div = @as(f32, @floatFromInt(pico.clock_config.sys.?.frequency())) /
        (800_000 * cycles_per_bit);

    pio.sm_load_and_start_program(sm, ws2812_program, .{
        .clkdiv = pico.pio.ClkDivOptions.from_float(div),
        .pin_mappings = .{
            .side_set = .{
                .base = @intFromEnum(led_pin),
                .count = 1,
            },
        },
        .shift = .{
            .out_shiftdir = .left,
            .autopull = true,
            .pull_threshold = 24,
            .join_tx = true,
        },
    }) catch unreachable;
    pio.sm_set_enabled(sm, true);

    // ACTUAL LOGIC

    const Colors = enum(u32) {
        red = 0x00ff0000,
        green = 0xff000000,
        blue = 0x0000ff00,
        white = 0xffffff00,
    };

    var color: Colors = .white;
    while (true) {
        // reading inputs
        if (pins.button.read() == 0) {
            pico.time.sleep_ms(20);
            if (pins.button.read() == 0) {
                color = switch (color) {
                    .white => .red,
                    .red => .green,
                    .green => .blue,
                    .blue => .white,
                };
                while (pins.button.read() == 0) {
                    asm volatile ("" ::: "memory");
                }
            }
        }
        const x = pico.adc.convert_one_shot_blocking(pins.xaxis) catch 0;
        // const y = pico.adc.convert_one_shot_blocking(pins.yaxos) catch 0;

        // writing outputs
        // TODO: this is unfinished
        switch (x) {
            0...QUARTER - 1 => {
                pio.sm_blocking_write(sm, @intFromEnum(color));
            },
            QUARTER...2 * QUARTER - 1 => {
                pio.sm_blocking_write(sm, 0x00000000);
                pio.sm_blocking_write(sm, @intFromEnum(color));
            },
            2 * QUARTER...3 * QUARTER - 1 => {
                pio.sm_blocking_write(sm, 0x00000000);
                pio.sm_blocking_write(sm, 0x00000000);
                pio.sm_blocking_write(sm, @intFromEnum(color));
            },
            else => {
                pio.sm_blocking_write(sm, 0x00000000);
                pio.sm_blocking_write(sm, 0x00000000);
                pio.sm_blocking_write(sm, 0x00000000);
                pio.sm_blocking_write(sm, @intFromEnum(color));
            },
        }

        pico.time.sleep_ms(100);
    }
}

const ws2812_program = blk: {
    @setEvalBranchQuota(10_000);
    break :blk pico.pio.assemble(
        \\;
        \\; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
        \\;
        \\; SPDX-License-Identifier: BSD-3-Clause
        \\;
        \\.program ws2812
        \\.side_set 1
        \\
        \\.define public T1 2
        \\.define public T2 5
        \\.define public T3 3
        \\
        \\.wrap_target
        \\bitloop:
        \\    out x, 1       side 0 [T3 - 1] ; Side-set still takes place when instruction stalls
        \\    jmp !x do_zero side 1 [T1 - 1] ; Branch on the bit we shifted out. Positive pulse
        \\do_one:
        \\    jmp  bitloop   side 1 [T2 - 1] ; Continue driving high, for a long pulse
        \\do_zero:
        \\    nop            side 0 [T2 - 1] ; Or drive low, for a short pulse
        \\.wrap
    , .{}).get_program_by_name("ws2812");
};