from hachoir.field import FieldSet, ParserError


class StaticFieldSet(FieldSet):
    """
    Static field set: format class attribute is a tuple of all fields
    in syntax like:
       format = (
          (TYPE1, ARG1, ARG2, ...),
          (TYPE2, ARG1, ARG2, ..., {KEY1=VALUE1, ...}),
          ...
       )

    Types with dynamic size are forbidden, eg. CString, PascalString8, etc.
    """
    format = None  # You have to redefine this class variable
    _class = None

    def __new__(cls, *args, **kw):
        assert cls.format is not None, "Class attribute 'format' is not set"
        if cls._class is not cls.__name__:
            cls._class = cls.__name__
            cls.static_size = cls._computeStaticSize()
        return object.__new__(cls)

    @staticmethod
    def _computeItemSize(item):
        item_class = item[0]
        if item_class.static_size is None:
            raise ParserError("Unable to get static size of field type: %s"
                              % item_class.__name__)
        if callable(item_class.static_size):
            if isinstance(item[-1], dict):
                return item_class.static_size(*item[1:-1], **item[-1])
            else:
                return item_class.static_size(*item[1:])
        else:
            assert isinstance(item_class.static_size, int)
            return item_class.static_size

    def createFields(self):
        for item in self.format:
            if isinstance(item[-1], dict):
                yield item[0](self, *item[1:-1], **item[-1])
            else:
                yield item[0](self, *item[1:])

    @classmethod
    def _computeStaticSize(cls, *args):
        return sum(cls._computeItemSize(item) for item in cls.format)

    # Initial value of static_size, it changes when first instance
    # is created (see __new__)
    static_size = _computeStaticSize
