class Control(wiring.Component):
    class Cmd(csr.Register, access = 'w'):
        start: csr.Field(csr.action.W, 1)
        stop: csr.Field(csr.action.W, 1)

    class Status(csr.Register, access = 'r'):
        state: csr.Field(csr.action.R, 2)

    class Level(csr.Register, access = 'r'):
        def __init__(self, depth):
            super().__init__({
                'level': csr.Field(csr.action.R, range(depth + 1)),
            })

    class Data(csr.Register, access = 'r'):
        data: csr.Field(csr.action.R, 8)

    class Annotation(meta.Annotation):
        schema = {
            "$schema": "https://json-schema.org/draft/2020-12/schema",
            "$id": "https://zyp.no/schema/foo/0.1/ila.json",
            "type": "object",
            "properties": {
                "buffer_width": {
                    "type": "integer",
                },
                "buffer_depth": {
                    "type": "integer",
                },
                "signals": {
                    "type": "array",
                    "items": {
                        "type": "object",
                        "properties": {
                            "name": {
                                "type": "string",
                            },
                            "width": {
                                "type": "integer",
                            },
                        },
                        "additionalProperties": False,
                        "required": [
                            "name",
                            "width",
                        ],
                    },
                },
            },
            "additionalProperties": False,
            "required": [
                "buffer_width",
                "buffer_depth",
            ],
        }

        def __init__(self, origin):
            self._origin = origin

        @property
        def origin(self):
            return self._origin

        def as_json(self):
            instance = {
                'buffer_width': self.origin.ila_capture.width,
                'buffer_depth': self.origin.ila_capture.depth,
                'signals': [
                    {
                        'name': name,
                        'width': len(signal),
                    } for name, signal in self.origin.ila_capture._signals.items()
                ],
            }
            self.validate(instance)
            return instance

    def __init__(self, ila_capture):
        super().__init__({
            'bus': wiring.In(csr.Signature(addr_width = 8, data_width = 8)),
        })

        regs = csr.Builder(addr_width = 8, data_width = 8)

        self._cmd = regs.add('cmd', self.Cmd())
        self._status = regs.add('status', self.Status())
        self._level = regs.add('level', self.Level(ila_capture.depth))
        self._data = regs.add('data', self.Data())

        self._bridge = csr.Bridge(regs.as_memory_map())
        self.bus.memory_map = self._bridge.bus.memory_map

        self.bus.memory_map.add_annotation(self.Annotation(self))

        self.ila_capture = ila_capture

    def elaborate(self, platform):
        m = Module()

        m.submodules.serializer = serializer = Serializer(data.ArrayLayout(self.bus.data_width, (self.ila_capture.width + self.bus.data_width - 1) // self.bus.data_width))

        m.d.comb += [
            self.ila_capture.output.ready.eq(serializer.input.ready),
            serializer.input.valid.eq(self.ila_capture.output.valid),
            serializer.input.payload.eq(self.ila_capture.output.payload),
        ]

        m.submodules.bridge = self._bridge
        wiring.connect(m, wiring.flipped(self.bus), self._bridge.bus)

        m.d.comb += [
            self.ila_capture.start.eq(self._cmd.f.start.w_stb & self._cmd.f.start.w_data),
            self.ila_capture.stop.eq(self._cmd.f.stop.w_stb & self._cmd.f.stop.w_data),

            self._status.f.state.r_data.eq(self.ila_capture.state),

            self._level.f.level.r_data.eq(self.ila_capture.storage.level),

            self._data.f.data.r_data.eq(serializer.output.payload),
            serializer.output.ready.eq(self._data.f.data.r_stb),
        ]

        return m