class StructTemplate(_Template):

    has_length = False

    def __init__(self, type, name, parent, parameters=None, length=None, align=None):
        self._parameters = parameters or {}
        self.type = type
        if length:
            self._set_length(length)
        self._align = int(align or 1)
        _Template.__init__(self, name, parent)

    def _set_length(self, length):
        self.has_length = True
        self.length = Length(length)

    def get_static_length(self):
        return sum(field.get_static_length() for field in self._fields.values())

    def decode(self, data, parent=None, name=None, little_endian=False):
        if self.has_length:
            length = self.length.decode(parent)
            data = data[:length]
        return _Template.decode(self, data, parent, name, little_endian)

    def encode(self, message_params, parent=None, name=None, little_endian=False):
        struct = self._get_struct(name, parent)
        self._add_struct_params(message_params)
        self._encode_fields(struct,
                            self._get_params_sub_tree(message_params, name),
                            little_endian=little_endian)
        if self.has_length:
            length, aligned_length = self.length.find_length_and_set_if_necessary(parent, len(struct))
            if len(struct) != length:
                raise AssertionError('Length of struct %s does not match defined length. defined length:%s Struct:\n%s' % (self.name, length, repr(struct)))
        return struct

    # TODO: Cleanup setting the parent to constructor of message -elements
    def _get_struct(self, name, parent):
        struct = Struct(name or self.name, self.type, align=self._align)
        struct._parent = parent
        return struct

    def validate(self, parent, message_fields, name=None):
        self._add_struct_params(message_fields)
        errors = []
        name = name or self.name
        message = parent[name]
        if self.has_length:
            length = self.length.decode(message)
            if len(message) != length:
                errors.append('Length of struct %s does not match defined length. defined length:%s struct length:%s' % (message._name, length, len(message)))
        return errors + _Template.validate(self, message, self._get_params_sub_tree(message_fields, name))

    def _add_struct_params(self, params):
        for key in self._parameters.keys():
            params[key] = self._parameters.pop(key) if key not in params else params[key]
 def __init__(self, length, name, parent):
     self.length = Length(length)
     _Template.__init__(self, name, parent)
class ListTemplate(_Template):

    param_pattern = re.compile(r'([^.]*?)\[(.*?)\](.*)')
    has_length = True
    type = 'List'

    def __init__(self, length, name, parent):
        self.length = Length(length)
        _Template.__init__(self, name, parent)

    def get_static_length(self):
        return self.length.value * self.field.get_static_length()

    def encode(self, message_params, parent, name=None, little_endian=False):
        name = name or self.name
        params_subtree = self._get_params_sub_tree(message_params, name)
        list = self._get_struct(name, parent)
        for index in range(self.length.decode(parent)):
            list[str(index)] = self.field.encode(params_subtree,
                                                 parent,
                                                 name=str(index),
                                                 little_endian=little_endian)
        self._check_params_empty(params_subtree, name)
        return list

    @property
    def field(self):
        return self._fields.values()[0]

    def _get_struct(self, name=None, parent=None):
        ls = List(name or self.name, self.field.type)
        ls._parent = parent
        return ls

    def decode(self, data, parent, name=None, little_endian=False):
        name = name or self.name
        message = self._get_struct(name, parent)
        data_index = 0
        # maximum_length is given for free length (*) to limit the absolute maximum number of entries
        for index in range(0, self.length.decode(parent, maximum_length=len(data))):
            message[str(index)] = self.field.decode(data[data_index:], message, name=str(index), little_endian=little_endian)
            data_index += len(message[index])
            if self.length.free and data_index == len(data):
                break
        return message

    def validate(self, parent, message_fields, name=None):
        name = name or self.name
        params_subtree = self._get_params_sub_tree(message_fields, name)
        list = parent[name]
        errors = []
        for index in range(list.len):
            errors += self.field.validate(list, params_subtree, name=str(index))
        self._check_params_empty(params_subtree, name)
        return errors

    def _get_params_sub_tree(self, params, name=None):
        result = OrderedDict({'*': params['*']} if '*' in params else {})
        name = name or self.name
        for key in params.keys():
            self._consume_params_with_brackets(name, params, result, key)
            self._consume_dot_syntax(name, params, result, key)
        return result

    def _consume_params_with_brackets(self, name, params, result, key):
        match = self.param_pattern.match(key)
        if match:
            prefix, child_name, ending = match.groups()
            if prefix == name:
                result[child_name + ending] = params.pop(key)
            elif prefix == '*':
                result[child_name + ending] = params[key]

    def _consume_dot_syntax(self, name, params, result, key):
        prefix, _, ending = key.partition('.')
        if prefix == name:
            result[ending] = params.pop(key)
        elif prefix == '*' and ending:
            result[ending] = params[key]
 def _set_length(self, length):
     self.has_length = True
     self.length = Length(length)