// SPDX-License-Identifier: LCL-1.0
// Copyright (c) 2026 Markus Maiwald
// Stewardship: Self Sovereign Society Foundation
//
// This file is part of the Nexus Commonwealth.
// See legal/LICENSE_COMMONWEALTH.md for license terms.

//! Rumpk Layer 0: UART Driver
//!
//! Minimal serial I/O for QEMU 'virt' machine.
//! Supports PL011 (ARM64) and 16550A (RISC-V).
//!
//! SAFETY: All MMIO accesses use volatile pointers.
//! Ring buffer is initialized to undefined for performance;
//! head/tail indices ensure only written bytes are read.

const std = @import("std");
const builtin = @import("builtin");

// ARM64 PL011 Constants
const PL011_BASE: usize = 0x09000000;
const PL011_DR: usize = 0x00;
const PL011_FR: usize = 0x18;
const PL011_TXFF: u32 = 1 << 5;

// RISC-V 16550A Constants
const NS16550A_BASE: usize = 0x10000000;
const NS16550A_THR: usize = 0x00; // Transmitter Holding Register
const NS16550A_LSR: usize = 0x05; // Line Status Register
const NS16550A_THRE: u8 = 1 << 5; // Transmitter Holding Register Empty
const NS16550A_IER: usize = 0x01; // Interrupt Enable Register
const NS16550A_FCR: usize = 0x02; // FIFO Control Register
const NS16550A_LCR: usize = 0x03; // Line Control Register

// Input Ring Buffer (256 bytes, power of 2 for fast masking)
const INPUT_BUFFER_SIZE = 256;
// SAFETY(RingBuffer): Only accessed via head/tail indices.
// SAFETY(RingBuffer): Only accessed via head/tail indices.
// Bytes are written before read. No uninitialized reads possible.
var input_buffer: [INPUT_BUFFER_SIZE]u8 = undefined;
var input_head = std.atomic.Value(u32).init(0); // Write position
var input_tail = std.atomic.Value(u32).init(0); // Read position

pub fn init() void {
    // Initialize buffer pointers
    input_head.store(0, .monotonic);
    input_tail.store(0, .monotonic);

    switch (builtin.cpu.arch) {
        .riscv64 => init_riscv(),
        else => {},
    }
}

pub fn init_riscv() void {
    const base = NS16550A_BASE;

    // 1. Enable Interrupts (Received Data Available)
    const ier: *volatile u8 = @ptrFromInt(base + NS16550A_IER);
    ier.* = 0x01; // 0x01 = Data Ready Interrupt.

    // 2. Disable FIFO (16450 Mode) to ensure immediate non-buffered input visibility
    const fcr: *volatile u8 = @ptrFromInt(base + NS16550A_FCR);
    fcr.* = 0x00;

    // 2b. Enable Modem Control (DTR | RTS | OUT2)
    // Essential for allowing interrupts and signaling readiness
    const mcr: *volatile u8 = @ptrFromInt(base + 0x04); // NS16550A_MCR
    mcr.* = 0x0B;

    // 3. Set LCR to 8N1
    const lcr: *volatile u8 = @ptrFromInt(base + NS16550A_LCR);
    lcr.* = 0x03;

    // --- LOOPBACK TEST ---
    // Enable Loopback Mode (Bit 4 of MCR)
    mcr.* = 0x1B; // 0x0B | 0x10

    // Write a test byte: 0xA5
    const thr: *volatile u8 = @ptrFromInt(base + NS16550A_THR);
    const lsr: *volatile u8 = @ptrFromInt(base + NS16550A_LSR);
    // Wait for THRE
    while ((lsr.* & NS16550A_THRE) == 0) {}
    thr.* = 0xA5;

    // Wait for Data Ready
    var timeout: usize = 1000000;
    while ((lsr.* & 0x01) == 0 and timeout > 0) {
        timeout -= 1;
    }

    var passed = false;
    var reason: []const u8 = "Timeout";

    if ((lsr.* & 0x01) != 0) {
        // Read RBR
        const rbr: *volatile u8 = @ptrFromInt(base + 0x00);
        const val = rbr.*;
        if (val == 0xA5) {
            passed = true;
        } else {
            reason = "Data Mismatch";
        }
    }

    // Disable Loopback (Restore MCR)
    mcr.* = 0x0B;

    if (passed) {
        write_bytes("[UART] Loopback Test: PASS\n");
    } else {
        write_bytes("[UART] Loopback Test: FAIL (");
        write_bytes(reason);
        write_bytes(")\n");
    }

    // Capture any data already in hardware FIFO
    poll_input();
}

/// Poll UART hardware and move available bytes into ring buffer
/// Should be called periodically (e.g. from scheduler or ISR)
pub fn poll_input() void {
    switch (builtin.cpu.arch) {
        .riscv64 => {
            const thr: *volatile u8 = @ptrFromInt(NS16550A_BASE + NS16550A_THR);
            const lsr: *volatile u8 = @ptrFromInt(NS16550A_BASE + NS16550A_LSR);

            // Read all available bytes from UART FIFO
            while ((lsr.* & 0x01) != 0) { // Data Ready
                const byte = thr.*;

                // Add to ring buffer if not full
                const head_val = input_head.load(.monotonic);
                const tail_val = input_tail.load(.monotonic);
                const next_head = (head_val + 1) % INPUT_BUFFER_SIZE;

                if (next_head != tail_val) {
                    input_buffer[head_val] = byte;
                    input_head.store(next_head, .monotonic);
                }
                // If full, drop the byte (could log this in debug mode)
            }
        },
        .aarch64 => {
            const dr: *volatile u32 = @ptrFromInt(PL011_BASE + PL011_DR);
            const fr: *volatile u32 = @ptrFromInt(PL011_BASE + PL011_FR);

            while ((fr.* & (1 << 4)) == 0) { // RXFE (Receive FIFO Empty) is bit 4
                const byte: u8 = @truncate(dr.*);

                const head_val = input_head.load(.monotonic);
                const tail_val = input_tail.load(.monotonic);
                const next_head = (head_val + 1) % INPUT_BUFFER_SIZE;

                if (next_head != tail_val) {
                    input_buffer[head_val] = byte;
                    input_head.store(next_head, .monotonic);
                }
            }
        },
        else => {},
    }
}

fn write_char_arm64(c: u8) void {
    const dr: *volatile u32 = @ptrFromInt(PL011_BASE + PL011_DR);
    const fr: *volatile u32 = @ptrFromInt(PL011_BASE + PL011_FR);

    while ((fr.* & PL011_TXFF) != 0) {}
    dr.* = c;
}

fn write_char_riscv64(c: u8) void {
    const thr: *volatile u8 = @ptrFromInt(NS16550A_BASE + NS16550A_THR);
    const lsr: *volatile u8 = @ptrFromInt(NS16550A_BASE + NS16550A_LSR);

    // Wait for THRE (Transmitter Holding Register Empty)
    while ((lsr.* & NS16550A_THRE) == 0) {}
    thr.* = c;
}

fn write_char(c: u8) void {
    switch (builtin.cpu.arch) {
        .aarch64 => write_char_arm64(c),
        .riscv64 => write_char_riscv64(c),
        else => {}, // Do nothing on others
    }
}

pub fn write_bytes(bytes: []const u8) void {
    for (bytes) |b| {
        if (b == '\n') {
            write_char('\r');
        }
        write_char(b);
    }
}

pub fn read_byte() ?u8 {
    // First, poll UART to refill buffer
    poll_input();

    // Then read from buffer
    const head_val = input_head.load(.monotonic);
    const tail_val = input_tail.load(.monotonic);

    if (tail_val != head_val) {
        const byte = input_buffer[tail_val];
        input_tail.store((tail_val + 1) % INPUT_BUFFER_SIZE, .monotonic);
        return byte;
    }

    return null;
}

pub fn read_direct() ?u8 {
    switch (builtin.cpu.arch) {
        .riscv64 => {
            const thr: *volatile u8 = @ptrFromInt(NS16550A_BASE + NS16550A_THR);
            const lsr: *volatile u8 = @ptrFromInt(NS16550A_BASE + NS16550A_LSR);
            if ((lsr.* & 0x01) != 0) {
                return thr.*;
            }
        },
        else => {},
    }
    return null;
}

pub fn get_lsr() u8 {
    switch (builtin.cpu.arch) {
        .riscv64 => {
            const lsr: *volatile u8 = @ptrFromInt(NS16550A_BASE + NS16550A_LSR);
            return lsr.*;
        },
        else => return 0,
    }
}

pub fn puts(s: []const u8) void {
    write_bytes(s);
}

pub fn putc(c: u8) void {
    if (c == '\n') {
        write_char('\r');
    }
    write_char(c);
}

pub fn print(s: []const u8) void {
    puts(s);
}

pub const Writer = struct {
    pub const Error = error{};
    pub fn write(self: Writer, bytes: []const u8) Error!usize {
        _ = self;
        write_bytes(bytes);
        return bytes.len;
    }
    pub fn print(self: Writer, comptime fmt: []const u8, args: anytype) Error!void {
        return std.fmt.format(self, fmt, args);
    }
};

pub fn print_hex(value: usize) void {
    const hex_chars = "0123456789ABCDEF";
    write_bytes("0x");
    var i: usize = 0;
    while (i < 16) : (i += 1) {
        const shift: u6 = @intCast((15 - i) * 4);
        const nibble = (value >> shift) & 0xF;
        write_char(hex_chars[nibble]);
    }
}

export fn uart_print_hex(value: u64) void {
    print_hex(value);
}

pub fn print_hex8(value: u8) void {
    const hex_chars = "0123456789ABCDEF";
    const nibble1 = (value >> 4) & 0xF;
    const nibble2 = value & 0xF;
    write_char(hex_chars[nibble1]);
    write_char(hex_chars[nibble2]);
}

export fn uart_print_hex8(value: u8) void {
    print_hex8(value);
}
