# SPDX-License-Identifier: AGPL-3.0-or-later | Commercial license available
# © Concepts 1996–2026 Miroslav Šotek. All rights reserved.
# © Code 2020–2026 Miroslav Šotek. All rights reserved.
# ORCID: 0009-0009-3560-0851
# Contact: www.anulum.li | protoscience@anulum.li
# SC-NeuroCore — Driver for the SC NeuroCore IP on PYNQ


# Mock classes for PYNQ
class MockMMIO:
    def __init__(self, base_address, range_):
        self.memory = {}

    def write(self, offset, value):
        self.memory[offset] = value

    def read(self, offset):
        return self.memory.get(offset, 0)


class MockOverlay:
    def __init__(self, bitfile):
        self.ip_dict = {
            "sc_neuron_0": {"phys_addr": 0x40000000, "addr_range": 0x1000},
            "sc_source_0": {"phys_addr": 0x40010000, "addr_range": 0x1000},
        }
        self.sc_neuron_0 = MockMMIO(0x40000000, 0x1000)
        self.sc_source_0 = MockMMIO(0x40010000, 0x1000)


# Mock Driver (This would be in sc_neurocore.drivers.pynq_driver)
class SCNeuroCoreDriver:
    """
    Driver for the SC NeuroCore IP on PYNQ.
    """

    def __init__(self, overlay):
        self.overlay = overlay
        self.neuron_ip = overlay.sc_neuron_0
        self.source_ip = overlay.sc_source_0

        # Register Map (Hypothetical)
        self.REG_RESET = 0x00
        self.REG_START = 0x04
        self.REG_WEIGHT_BASE = 0x100

    def reset(self):
        self.neuron_ip.write(self.REG_RESET, 1)
        self.neuron_ip.write(self.REG_RESET, 0)

    def start(self):
        self.neuron_ip.write(self.REG_START, 1)

    def set_weights(self, weights):
        for i, w in enumerate(weights):
            # Write 32-bit fixed point or integer weight
            val = int(w * 255)  # Scale 0-1 to 0-255
            offset = self.REG_WEIGHT_BASE + (i * 4)
            self.source_ip.write(offset, val)


def test_hardware_driver_flow():
    """
    Verify that the driver correctly interacts with the (mocked) hardware registers.
    """
    overlay = MockOverlay("sc_neurocore.bit")
    driver = SCNeuroCoreDriver(overlay)

    # 1. Reset
    driver.reset()
    # Check if reset bit was toggled
    # Note: MockMMIO keeps last written value.
    # Real hardware would self-clear or we check the sequence.
    # Here we just verify no crash and '0' is final state
    assert overlay.sc_neuron_0.read(driver.REG_RESET) == 0

    # 2. Set Weights
    weights = [0.1, 0.5, 0.9]
    driver.set_weights(weights)

    # Verify writes
    # 0.1 * 255 = 25
    assert overlay.sc_source_0.read(driver.REG_WEIGHT_BASE) == 25
    # 0.5 * 255 = 127
    assert overlay.sc_source_0.read(driver.REG_WEIGHT_BASE + 4) == 127
    # 0.9 * 255 = 229
    assert overlay.sc_source_0.read(driver.REG_WEIGHT_BASE + 8) == 229

    # 3. Start
    driver.start()
    assert overlay.sc_neuron_0.read(driver.REG_START) == 1

    print("Hardware interaction sequence verified.")
