# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0

from typing import Sequence

from reggen.access import JsonEnum
from reggen.bits import Bits
from reggen.lib import check_keys, check_name, check_str, check_int, check_bool, check_list
from reggen.signal import Signal


class IntrType(JsonEnum):
    Event = 1
    Status = 2


_intr_type_map = {'event': IntrType.Event, 'status': IntrType.Status}


class Interrupt(Signal):

    def __init__(self, name: str, desc: str, bits: Bits, auto_split: bool, intr_type: IntrType):
        super().__init__(name, desc, bits)
        self.intr_type = intr_type
        self.auto_split = auto_split

    @staticmethod
    def from_raw(what: str, lsb: int, raw: object) -> 'Interrupt':
        rd = check_keys(raw, what, ['name', 'desc'], ['width', 'type', 'auto_split'])

        name = check_name(rd['name'], 'name field of ' + what)
        desc = check_str(rd['desc'], 'desc field of ' + what)
        width = check_int(rd.get('width', 1), 'width field of ' + what)
        if width <= 0:
            raise ValueError('The width field of signal {} ({}) '
                             'has value {}, but should be positive.'.format(
                                 name, what, width))
        bits = Bits(lsb + width - 1, lsb)
        intr_type_str = check_str(rd.get('type', 'event'),
                                  'intr_type field of ' + what)
        auto_split = check_bool(rd.get('auto_split', False), 'auto_split of ' + what)

        try:
            intr_type = _intr_type_map[intr_type_str.lower()]
        except KeyError:
            raise ValueError(
                'The intr_type field of signal {} ({}) '
                'has value {}, but should be either event or status.'.format(
                    name, what, intr_type_str))

        return Interrupt(name, desc, bits, auto_split, intr_type,)

    @staticmethod
    def from_raw_list(what: str, raw: object) -> Sequence['Interrupt']:
        lsb = 0
        ret = []
        for idx, entry in enumerate(check_list(raw, what)):
            entry_what = 'entry {} of {}'.format(idx, what)
            interrupt = Interrupt.from_raw(entry_what, lsb, entry)
            ret.append(interrupt)
            lsb += interrupt.bits.width()
        return ret
