/*
 * {{ soc_name }}_bindings.cpp
 * Generated by PeakRDL-pybind11
 * 
 * PyBind11 bindings for SystemRDL register map
 */

#include <pybind11/pybind11.h>
#include <pybind11/functional.h>
#include <pybind11/stl.h>
#include "{{ soc_name }}_descriptors.hpp"

namespace py = pybind11;
using namespace {{ soc_name }};

// Trampoline class for Master to allow Python inheritance
class PyMaster : public Master {
public:
    using Master::Master;
    
    uint64_t read(uint64_t address, size_t width) override {
        PYBIND11_OVERRIDE_PURE(
            uint64_t,
            Master,
            read,
            address,
            width
        );
    }
    
    void write(uint64_t address, uint64_t value, size_t width) override {
        PYBIND11_OVERRIDE_PURE(
            void,
            Master,
            write,
            address,
            value,
            width
        );
    }
};

PYBIND11_MODULE(_{{ soc_name }}_native, m) {
    m.doc() = "{{ soc_name }} register map generated from SystemRDL";
    
    // Master interface with trampoline for Python inheritance
    py::class_<Master, PyMaster>(m, "Master")
        .def(py::init<>())
        .def("read", &Master::read, "Read from address")
        .def("write", &Master::write, "Write to address")
        .def("__repr__", &Master::__repr__)
        .def("__str__", &Master::__repr__);
    
    // FieldBase
    py::class_<FieldBase>(m, "FieldBase")
        .def_property_readonly("name", &FieldBase::name)
        .def_property_readonly("offset", &FieldBase::offset)
        .def_property_readonly("lsb", &FieldBase::lsb)
        .def_property_readonly("msb", &FieldBase::msb)
        .def_property_readonly("width", &FieldBase::width)
        .def_property_readonly("is_readable", &FieldBase::is_readable)
        .def_property_readonly("is_writable", &FieldBase::is_writable)
        .def_property_readonly("mask", &FieldBase::mask)
        .def("__repr__", &FieldBase::__repr__)
        .def("__str__", &FieldBase::__repr__);
    
    // RegisterBase
    py::class_<RegisterBase>(m, "RegisterBase")
        .def_property_readonly("name", &RegisterBase::name)
        .def_property_readonly("offset", &RegisterBase::offset)
        .def_property_readonly("width", &RegisterBase::width)
        .def("read", &RegisterBase::read, "Read register value")
        .def("write", &RegisterBase::write, "Write register value")
        .def("modify", &RegisterBase::modify, "Read-modify-write operation")
        .def("__enter__", &RegisterBase::__enter__, "Enter context manager")
        .def("__exit__", [](RegisterBase& self, py::object exc_type, py::object exc_value, py::object traceback) {
            self.exit_context();
        }, "Exit context manager")
        .def("__repr__", &RegisterBase::__repr__)
        .def("__str__", &RegisterBase::__repr__);
    
    // NodeBase
    py::class_<NodeBase>(m, "NodeBase")
        .def_property_readonly("name", &NodeBase::name)
        .def_property_readonly("offset", &NodeBase::offset)
        .def("__repr__", &NodeBase::__repr__)
        .def("__str__", &NodeBase::__repr__);
    
    {% for reg in nodes.regs %}
    // Register class: {{ reg.inst_name }}
    py::class_<{{ reg.inst_name }}_t, RegisterBase>(m, "{{ reg.inst_name }}_t"{% if reg.get_property('desc') %}, "{{ reg.get_html_desc() }}"{% endif %})
        .def(py::init<uint64_t>())
        {% for field in reg.fields() %}
        .def_readonly("{{ field.inst_name }}", &{{ reg.inst_name }}_t::{{ field.inst_name }})
        {% endfor %}
        ;
    
    {% for field in reg.fields() %}
    // Field class: {{ reg.inst_name }}.{{ field.inst_name }}
    py::class_<{{ reg.inst_name }}_t::{{ field.inst_name }}_field, FieldBase>(m, "{{ reg.inst_name }}_{{ field.inst_name }}_field"{% if field.get_property('desc') %}, "{{ field.get_html_desc() }}"{% endif %})
        .def("read", &{{ reg.inst_name }}_t::{{ field.inst_name }}_field::read, "Read field value")
        .def("write", &{{ reg.inst_name }}_t::{{ field.inst_name }}_field::write, "Write field value");
    {% endfor %}
    {% endfor %}
    
    {% for regfile in nodes.regfiles %}
    // Regfile class: {{ regfile.inst_name }}
    py::class_<{{ regfile.inst_name }}_t, NodeBase>(m, "{{ regfile.inst_name }}_t"{% if regfile.get_property('desc') %}, "{{ regfile.get_html_desc() }}"{% endif %})
        .def(py::init<uint64_t>())
        {% for child in regfile.children() %}
        {% if child.inst_name %}
        .def_readonly("{{ child.inst_name }}", &{{ regfile.inst_name }}_t::{{ child.inst_name }})
        {% endif %}
        {% endfor %}
        ;
    {% endfor %}
    
    {% for addrmap in nodes.addrmaps %}
    {% if addrmap != top_node %}
    // Addrmap class: {{ addrmap.inst_name }}
    py::class_<{{ addrmap.inst_name }}_t, NodeBase>(m, "{{ addrmap.inst_name }}_t"{% if addrmap.get_property('desc') %}, "{{ addrmap.get_html_desc() }}"{% endif %})
        .def(py::init<uint64_t>())
        {% for child in addrmap.children() %}
        {% if child.inst_name %}
        .def_readonly("{{ child.inst_name }}", &{{ addrmap.inst_name }}_t::{{ child.inst_name }})
        {% endif %}
        {% endfor %}
        ;
    {% endif %}
    {% endfor %}
    
    {% for regfile in nodes.regfiles %}
    // Regfile class: {{ regfile.inst_name }}
    py::class_<{{ regfile.inst_name }}_t, NodeBase>(m, "{{ regfile.inst_name }}_t"{% if regfile.get_property('desc') %}, "{{ regfile.get_html_desc() }}"{% endif %})
        .def(py::init<uint64_t>())
        {% for child in regfile.children() %}
        {% if child.inst_name %}
        .def_readonly("{{ child.inst_name }}", &{{ regfile.inst_name }}_t::{{ child.inst_name }})
        {% endif %}
        {% endfor %}
        ;
    {% endfor %}
    
    {% for addrmap in nodes.addrmaps %}
    {% if addrmap != top_node %}
    // Addrmap class: {{ addrmap.inst_name }}
    py::class_<{{ addrmap.inst_name }}_t, NodeBase>(m, "{{ addrmap.inst_name }}_t"{% if addrmap.get_property('desc') %}, "{{ addrmap.get_html_desc() }}"{% endif %})
        .def(py::init<uint64_t>())
        {% for child in addrmap.children() %}
        {% if child.inst_name %}
        .def_readonly("{{ child.inst_name }}", &{{ addrmap.inst_name }}_t::{{ child.inst_name }})
        {% endif %}
        {% endfor %}
        ;
    {% endif %}
    {% endfor %}
    
    {% for mem in nodes.mems %}
    {%- set entry_reg = mem.children()|list|first %}
    // Memory class: {{ mem.inst_name }}
    py::class_<{{ mem.inst_name }}_t, NodeBase>(m, "{{ mem.inst_name }}_t"{% if mem.get_property('desc') %}, "{{ mem.get_html_desc() }}"{% endif %})
        .def(py::init<uint64_t>())
        .def("__len__", &{{ mem.inst_name }}_t::size, "Get number of entries")
        .def("__getitem__", 
            []({{ mem.inst_name }}_t &mem, size_t i) -> {{ entry_reg.inst_name }}_t& {
                if (i >= mem.size()) {
                    throw py::index_error("Memory index out of range");
                }
                return mem[i];
            },
            py::return_value_policy::reference_internal,
            "Access memory entry by index")
        .def("__getitem__",
            []({{ mem.inst_name }}_t &mem, py::slice slice) -> py::list {
                size_t start, stop, step, slicelength;
                if (!slice.compute(mem.size(), &start, &stop, &step, &slicelength)) {
                    throw py::error_already_set();
                }
                py::list result;
                for (size_t i = 0; i < slicelength; ++i) {
                    result.append(py::cast(mem[start], py::return_value_policy::reference_internal));
                    start += step;
                }
                return result;
            },
            "Access memory entries by slice")
        .def("__iter__",
            []({{ mem.inst_name }}_t &mem) {
                return py::make_iterator(mem.begin(), mem.end());
            },
            py::keep_alive<0, 1>(),
            "Iterate over memory entries");
    {% endfor %}
    
    // Top-level SoC class
    py::class_<{{ soc_name }}_t, NodeBase>(m, "{{ soc_name }}_t")
        .def(py::init<>())
        .def("attach_master", &{{ soc_name }}_t::attach_master, "Attach a master interface")
        {% for child in top_node.children() %}
        {% if child.inst_name %}
        .def_readonly("{{ child.inst_name }}", &{{ soc_name }}_t::{{ child.inst_name }})
        {% endif %}
        {% endfor %}
        ;
}
