// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include "rtl8139.h"
#include "io.h"
#include "kutils.h"
#include "platform.h"

#define RTL8139_MAC_0         0x00
#define RTL8139_TSD0          0x10
#define RTL8139_TSAD0         0x20
#define RTL8139_RBSTART       0x30
#define RTL8139_CR            0x37
#define RTL8139_CAPR          0x38
#define RTL8139_CBR           0x3A
#define RTL8139_IMR           0x3C
#define RTL8139_ISR           0x3E
#define RTL8139_TCR           0x40
#define RTL8139_RCR           0x44
#define RTL8139_CONFIG_1      0x52

#define RTL8139_RCR_AAP       (1 << 0) // Accept All Packets
#define RTL8139_RCR_APM       (1 << 1) // Accept Physical Match Packets
#define RTL8139_RCR_AM        (1 << 2) // Accept Multicast Packets
#define RTL8139_RCR_AB        (1 << 3) // Accept Broadcast Packets
#define RTL8139_RCR_WRAP      (1 << 7) // WRAP

#define RTL8139_CR_TE         (1 << 2) // Transmitter Enable
#define RTL8139_CR_RE         (1 << 3) // Receiver Enable
#define RTL8139_CR_RST        (1 << 4) // Reset

static int rtl8139_initialized = 0;
static uint64_t mmio_base_addr = 0;
static uint8_t mac_addr[6];

// Receive buffer must be 8K + 16 bytes + 1.5K
// Let's use 32K buffer for safety, which is standard.
static uint8_t rx_buffer[32768 + 16 + 1500] __attribute__((aligned(4096)));
static uint16_t rx_ptr = 0; // Current read position

// Transmit buffers (4 descriptors in RTL8139)
static uint8_t tx_buffers[4][4096] __attribute__((aligned(4096)));
static uint8_t tx_curr = 0;

static inline uint8_t rtl8139_inb(uint16_t offset) { return *(volatile uint8_t*)(uintptr_t)(mmio_base_addr + offset); }
static inline uint16_t rtl8139_inw(uint16_t offset) { return *(volatile uint16_t*)(uintptr_t)(mmio_base_addr + offset); }
static inline uint32_t rtl8139_inl(uint16_t offset) { return *(volatile uint32_t*)(uintptr_t)(mmio_base_addr + offset); }

static inline void rtl8139_outb(uint16_t offset, uint8_t value) { *(volatile uint8_t*)(uintptr_t)(mmio_base_addr + offset) = value; }
static inline void rtl8139_outw(uint16_t offset, uint16_t value) { *(volatile uint16_t*)(uintptr_t)(mmio_base_addr + offset) = value; }
static inline void rtl8139_outl(uint16_t offset, uint32_t value) { *(volatile uint32_t*)(uintptr_t)(mmio_base_addr + offset) = value; }

int rtl8139_init(pci_device_t* pci_dev) {
    if (rtl8139_initialized) return 0;

    // Enable bus mastering
    uint32_t command = pci_read_config(pci_dev->bus, pci_dev->device, pci_dev->function, 0x04);
    pci_write_config(pci_dev->bus, pci_dev->device, pci_dev->function, 0x04, command | (1 << 2) | (1 << 1));

    uint32_t bar1 = pci_read_config(pci_dev->bus, pci_dev->device, pci_dev->function, 0x14); // BAR1: MMIO
    if (bar1 == 0 || bar1 == 0xFFFFFFFF) return -1;
    if (bar1 & 1) return -1; // Should not be I/O space

    mmio_base_addr = p2v(bar1 & ~0xF);

    extern void serial_write(const char *str);
    serial_write("[RTL8139] MMIO Base: 0x");
    char hex_buf[32]; k_itoa_hex(mmio_base_addr, hex_buf); serial_write(hex_buf); serial_write("\n");

    // Power on (CONFIG1)
    rtl8139_outb(RTL8139_CONFIG_1, 0x00);

    // Software Reset
    rtl8139_outb(RTL8139_CR, RTL8139_CR_RST);
    while (rtl8139_inb(RTL8139_CR) & RTL8139_CR_RST) {
        // Wait
    }

    // Read MAC
    uint32_t mac_low = rtl8139_inl(RTL8139_MAC_0);
    uint32_t mac_high = rtl8139_inw(RTL8139_MAC_0 + 4);
    mac_addr[0] = (mac_low >> 0) & 0xFF;
    mac_addr[1] = (mac_low >> 8) & 0xFF;
    mac_addr[2] = (mac_low >> 16) & 0xFF;
    mac_addr[3] = (mac_low >> 24) & 0xFF;
    mac_addr[4] = (mac_high >> 0) & 0xFF;
    mac_addr[5] = (mac_high >> 8) & 0xFF;

    serial_write("[RTL8139] MAC: ");
    for(int i=0; i<6; i++) {
        char buf[4];
        k_itoa_hex(mac_addr[i], buf);
        serial_write(buf);
        if(i<5) serial_write(":");
    }
    serial_write("\n");

    // Init RX buffer
    uint32_t rx_phys = v2p((uint64_t)(uintptr_t)rx_buffer);
    rtl8139_outl(RTL8139_RBSTART, rx_phys);

    // Set IMR / ISR
    rtl8139_outw(RTL8139_IMR, 0x0005); // TOK and ROK

    // Set RCR (Receive Configuration Register)
    // Accept Broadcast/Multicast/Physical Match + WRAP
    rtl8139_outl(RTL8139_RCR, RTL8139_RCR_AB | RTL8139_RCR_AM | RTL8139_RCR_APM | RTL8139_RCR_WRAP | (3 << 11)); // 32k rx buffer

    // Enable Transmitter and Receiver
    rtl8139_outb(RTL8139_CR, RTL8139_CR_RE | RTL8139_CR_TE);
    
    // Config TCR
    rtl8139_outl(RTL8139_TCR, (0x03 << 24)); // IFG

    rtl8139_initialized = 1;
    return 0;
}

int rtl8139_send_packet(const void* data, size_t length) {
    if (!rtl8139_initialized) return -1;
    if (length > 1792) return -1;

    // Use current tx buffer
    uint8_t* tx_buf = tx_buffers[tx_curr];
    uint8_t* src = (uint8_t*)data;
    for (size_t i = 0; i < length; i++) tx_buf[i] = src[i];

    uint32_t phys = v2p((uint64_t)(uintptr_t)tx_buf);
    rtl8139_outl(RTL8139_TSAD0 + (tx_curr * 4), phys);
    
    // Status is length | clear bit 13 to send
    rtl8139_outl(RTL8139_TSD0 + (tx_curr * 4), length);

    tx_curr = (tx_curr + 1) % 4;
    return 0;
}

int rtl8139_receive_packet(void* buffer, size_t buffer_size) {
    if (!rtl8139_initialized) return 0;

    uint8_t cmd = rtl8139_inb(RTL8139_CR);
    if ((cmd & 1) == 1) { // Buffer empty
        return 0; 
    }

    uint16_t* rx_head = (uint16_t*)(rx_buffer + rx_ptr);
    uint16_t rx_status = rx_head[0];
    uint16_t rx_length = rx_head[1]; // includes 4 bytes CRC at end

    if (rx_status & (1 << 5)) { // Bad packet
        // Bad packet received. We need to skip it or reset.
    }

    if (rx_length > 0 && rx_length <= buffer_size + 4) {
        uint8_t* pkt = (uint8_t*)(rx_head) + 4;
        uint16_t net_len = rx_length - 4; // Strip CRC
        
        uint8_t* dest = (uint8_t*)buffer;
        for (int i = 0; i < net_len; i++) {
            dest[i] = pkt[i];
        }

        // Update rx_ptr
        rx_ptr = (rx_ptr + rx_length + 4 + 3) & ~3; // Align up to 4 bytes
        if (rx_ptr > 32768) {
            rx_ptr -= 32768; // Wrap around
        }

        rtl8139_outw(RTL8139_CAPR, rx_ptr - 16);
        return net_len;
    }

    // Default error handling, skip it
    rx_ptr = (rx_ptr + rx_length + 4 + 3) & ~3;
    if (rx_ptr > 32768) rx_ptr -= 32768;
    rtl8139_outw(RTL8139_CAPR, rx_ptr - 16);
    return 0;
}

int rtl8139_get_mac(uint8_t* mac_out) {
    if (!rtl8139_initialized) return -1;
    for (int i = 0; i < 6; i++) {
        mac_out[i] = mac_addr[i];
    }
    return 0;
}
