# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

from pkg_resources import parse_version
import kaitaistruct
from kaitaistruct import KaitaiStruct, KaitaiStream, BytesIO
from enum import Enum
import collections


if parse_version(kaitaistruct.__version__) < parse_version('0.9'):
    raise Exception("Incompatible Kaitai Struct Python API: 0.9 or later is required, but you have %s" % (kaitaistruct.__version__))

class BlenderBlend(KaitaiStruct):
    """Blender is an open source suite for 3D modelling, sculpting,
    animation, compositing, rendering, preparation of assets for its own
    game engine and exporting to others, etc. `.blend` is its own binary
    format that saves whole state of suite: current scene, animations,
    all software settings, extensions, etc.
    
    Internally, .blend format is a hybrid semi-self-descriptive
    format. On top level, it contains a simple header and a sequence of
    file blocks, which more or less follow typical [TLV
    pattern](https://en.wikipedia.org/wiki/Type-length-value). Pre-last
    block would be a structure with code `DNA1`, which is a essentially
    a machine-readable schema of all other structures used in this file.
    """

    class PtrSize(Enum):
        bits_64 = 45
        bits_32 = 95

    class Endian(Enum):
        be = 86
        le = 118
    SEQ_FIELDS = ["hdr", "blocks"]
    def __init__(self, _io, _parent=None, _root=None):
        self._io = _io
        self._parent = _parent
        self._root = _root if _root else self
        self._debug = collections.defaultdict(dict)

    def _read(self):
        self._debug['hdr']['start'] = self._io.pos()
        self.hdr = BlenderBlend.Header(self._io, self, self._root)
        self.hdr._read()
        self._debug['hdr']['end'] = self._io.pos()
        self._debug['blocks']['start'] = self._io.pos()
        self.blocks = []
        i = 0
        while not self._io.is_eof():
            if not 'arr' in self._debug['blocks']:
                self._debug['blocks']['arr'] = []
            self._debug['blocks']['arr'].append({'start': self._io.pos()})
            _t_blocks = BlenderBlend.FileBlock(self._io, self, self._root)
            _t_blocks._read()
            self.blocks.append(_t_blocks)
            self._debug['blocks']['arr'][len(self.blocks) - 1]['end'] = self._io.pos()
            i += 1

        self._debug['blocks']['end'] = self._io.pos()

    class DnaStruct(KaitaiStruct):
        """DNA struct contains a `type` (type name), which is specified as
        an index in types table, and sequence of fields.
        """
        SEQ_FIELDS = ["idx_type", "num_fields", "fields"]
        def __init__(self, _io, _parent=None, _root=None):
            self._io = _io
            self._parent = _parent
            self._root = _root if _root else self
            self._debug = collections.defaultdict(dict)

        def _read(self):
            self._debug['idx_type']['start'] = self._io.pos()
            self.idx_type = self._io.read_u2le()
            self._debug['idx_type']['end'] = self._io.pos()
            self._debug['num_fields']['start'] = self._io.pos()
            self.num_fields = self._io.read_u2le()
            self._debug['num_fields']['end'] = self._io.pos()
            self._debug['fields']['start'] = self._io.pos()
            self.fields = [None] * (self.num_fields)
            for i in range(self.num_fields):
                if not 'arr' in self._debug['fields']:
                    self._debug['fields']['arr'] = []
                self._debug['fields']['arr'].append({'start': self._io.pos()})
                _t_fields = BlenderBlend.DnaField(self._io, self, self._root)
                _t_fields._read()
                self.fields[i] = _t_fields
                self._debug['fields']['arr'][i]['end'] = self._io.pos()

            self._debug['fields']['end'] = self._io.pos()

        @property
        def type(self):
            if hasattr(self, '_m_type'):
                return self._m_type if hasattr(self, '_m_type') else None

            self._m_type = self._parent.types[self.idx_type]
            return self._m_type if hasattr(self, '_m_type') else None


    class FileBlock(KaitaiStruct):
        SEQ_FIELDS = ["code", "len_body", "mem_addr", "sdna_index", "count", "body"]
        def __init__(self, _io, _parent=None, _root=None):
            self._io = _io
            self._parent = _parent
            self._root = _root if _root else self
            self._debug = collections.defaultdict(dict)

        def _read(self):
            self._debug['code']['start'] = self._io.pos()
            self.code = (self._io.read_bytes(4)).decode(u"ASCII")
            self._debug['code']['end'] = self._io.pos()
            self._debug['len_body']['start'] = self._io.pos()
            self.len_body = self._io.read_u4le()
            self._debug['len_body']['end'] = self._io.pos()
            self._debug['mem_addr']['start'] = self._io.pos()
            self.mem_addr = self._io.read_bytes(self._root.hdr.psize)
            self._debug['mem_addr']['end'] = self._io.pos()
            self._debug['sdna_index']['start'] = self._io.pos()
            self.sdna_index = self._io.read_u4le()
            self._debug['sdna_index']['end'] = self._io.pos()
            self._debug['count']['start'] = self._io.pos()
            self.count = self._io.read_u4le()
            self._debug['count']['end'] = self._io.pos()
            self._debug['body']['start'] = self._io.pos()
            _on = self.code
            if _on == u"DNA1":
                self._raw_body = self._io.read_bytes(self.len_body)
                _io__raw_body = KaitaiStream(BytesIO(self._raw_body))
                self.body = BlenderBlend.Dna1Body(_io__raw_body, self, self._root)
                self.body._read()
            else:
                self.body = self._io.read_bytes(self.len_body)
            self._debug['body']['end'] = self._io.pos()

        @property
        def sdna_struct(self):
            if hasattr(self, '_m_sdna_struct'):
                return self._m_sdna_struct if hasattr(self, '_m_sdna_struct') else None

            if self.sdna_index != 0:
                self._m_sdna_struct = self._root.sdna_structs[self.sdna_index]

            return self._m_sdna_struct if hasattr(self, '_m_sdna_struct') else None


    class Dna1Body(KaitaiStruct):
        """DNA1, also known as "Structure DNA", is a special block in
        .blend file, which contains machine-readable specifications of
        all other structures used in this .blend file.
        
        Effectively, this block contains:
        
        * a sequence of "names" (strings which represent field names)
        * a sequence of "types" (strings which represent type name)
        * a sequence of "type lengths"
        * a sequence of "structs" (which describe contents of every
          structure, referring to types and names by index)
        
        .. seealso::
           Source - https://en.blender.org/index.php/Dev:Source/Architecture/File_Format#Structure_DNA
        """
        SEQ_FIELDS = ["id", "name_magic", "num_names", "names", "padding_1", "type_magic", "num_types", "types", "padding_2", "tlen_magic", "lengths", "padding_3", "strc_magic", "num_structs", "structs"]
        def __init__(self, _io, _parent=None, _root=None):
            self._io = _io
            self._parent = _parent
            self._root = _root if _root else self
            self._debug = collections.defaultdict(dict)

        def _read(self):
            self._debug['id']['start'] = self._io.pos()
            self.id = self._io.read_bytes(4)
            self._debug['id']['end'] = self._io.pos()
            if not self.id == b"\x53\x44\x4E\x41":
                raise kaitaistruct.ValidationNotEqualError(b"\x53\x44\x4E\x41", self.id, self._io, u"/types/dna1_body/seq/0")
            self._debug['name_magic']['start'] = self._io.pos()
            self.name_magic = self._io.read_bytes(4)
            self._debug['name_magic']['end'] = self._io.pos()
            if not self.name_magic == b"\x4E\x41\x4D\x45":
                raise kaitaistruct.ValidationNotEqualError(b"\x4E\x41\x4D\x45", self.name_magic, self._io, u"/types/dna1_body/seq/1")
            self._debug['num_names']['start'] = self._io.pos()
            self.num_names = self._io.read_u4le()
            self._debug['num_names']['end'] = self._io.pos()
            self._debug['names']['start'] = self._io.pos()
            self.names = [None] * (self.num_names)
            for i in range(self.num_names):
                if not 'arr' in self._debug['names']:
                    self._debug['names']['arr'] = []
                self._debug['names']['arr'].append({'start': self._io.pos()})
                self.names[i] = (self._io.read_bytes_term(0, False, True, True)).decode(u"UTF-8")
                self._debug['names']['arr'][i]['end'] = self._io.pos()

            self._debug['names']['end'] = self._io.pos()
            self._debug['padding_1']['start'] = self._io.pos()
            self.padding_1 = self._io.read_bytes(((4 - self._io.pos()) % 4))
            self._debug['padding_1']['end'] = self._io.pos()
            self._debug['type_magic']['start'] = self._io.pos()
            self.type_magic = self._io.read_bytes(4)
            self._debug['type_magic']['end'] = self._io.pos()
            if not self.type_magic == b"\x54\x59\x50\x45":
                raise kaitaistruct.ValidationNotEqualError(b"\x54\x59\x50\x45", self.type_magic, self._io, u"/types/dna1_body/seq/5")
            self._debug['num_types']['start'] = self._io.pos()
            self.num_types = self._io.read_u4le()
            self._debug['num_types']['end'] = self._io.pos()
            self._debug['types']['start'] = self._io.pos()
            self.types = [None] * (self.num_types)
            for i in range(self.num_types):
                if not 'arr' in self._debug['types']:
                    self._debug['types']['arr'] = []
                self._debug['types']['arr'].append({'start': self._io.pos()})
                self.types[i] = (self._io.read_bytes_term(0, False, True, True)).decode(u"UTF-8")
                self._debug['types']['arr'][i]['end'] = self._io.pos()

            self._debug['types']['end'] = self._io.pos()
            self._debug['padding_2']['start'] = self._io.pos()
            self.padding_2 = self._io.read_bytes(((4 - self._io.pos()) % 4))
            self._debug['padding_2']['end'] = self._io.pos()
            self._debug['tlen_magic']['start'] = self._io.pos()
            self.tlen_magic = self._io.read_bytes(4)
            self._debug['tlen_magic']['end'] = self._io.pos()
            if not self.tlen_magic == b"\x54\x4C\x45\x4E":
                raise kaitaistruct.ValidationNotEqualError(b"\x54\x4C\x45\x4E", self.tlen_magic, self._io, u"/types/dna1_body/seq/9")
            self._debug['lengths']['start'] = self._io.pos()
            self.lengths = [None] * (self.num_types)
            for i in range(self.num_types):
                if not 'arr' in self._debug['lengths']:
                    self._debug['lengths']['arr'] = []
                self._debug['lengths']['arr'].append({'start': self._io.pos()})
                self.lengths[i] = self._io.read_u2le()
                self._debug['lengths']['arr'][i]['end'] = self._io.pos()

            self._debug['lengths']['end'] = self._io.pos()
            self._debug['padding_3']['start'] = self._io.pos()
            self.padding_3 = self._io.read_bytes(((4 - self._io.pos()) % 4))
            self._debug['padding_3']['end'] = self._io.pos()
            self._debug['strc_magic']['start'] = self._io.pos()
            self.strc_magic = self._io.read_bytes(4)
            self._debug['strc_magic']['end'] = self._io.pos()
            if not self.strc_magic == b"\x53\x54\x52\x43":
                raise kaitaistruct.ValidationNotEqualError(b"\x53\x54\x52\x43", self.strc_magic, self._io, u"/types/dna1_body/seq/12")
            self._debug['num_structs']['start'] = self._io.pos()
            self.num_structs = self._io.read_u4le()
            self._debug['num_structs']['end'] = self._io.pos()
            self._debug['structs']['start'] = self._io.pos()
            self.structs = [None] * (self.num_structs)
            for i in range(self.num_structs):
                if not 'arr' in self._debug['structs']:
                    self._debug['structs']['arr'] = []
                self._debug['structs']['arr'].append({'start': self._io.pos()})
                _t_structs = BlenderBlend.DnaStruct(self._io, self, self._root)
                _t_structs._read()
                self.structs[i] = _t_structs
                self._debug['structs']['arr'][i]['end'] = self._io.pos()

            self._debug['structs']['end'] = self._io.pos()


    class Header(KaitaiStruct):
        SEQ_FIELDS = ["magic", "ptr_size_id", "endian", "version"]
        def __init__(self, _io, _parent=None, _root=None):
            self._io = _io
            self._parent = _parent
            self._root = _root if _root else self
            self._debug = collections.defaultdict(dict)

        def _read(self):
            self._debug['magic']['start'] = self._io.pos()
            self.magic = self._io.read_bytes(7)
            self._debug['magic']['end'] = self._io.pos()
            if not self.magic == b"\x42\x4C\x45\x4E\x44\x45\x52":
                raise kaitaistruct.ValidationNotEqualError(b"\x42\x4C\x45\x4E\x44\x45\x52", self.magic, self._io, u"/types/header/seq/0")
            self._debug['ptr_size_id']['start'] = self._io.pos()
            self.ptr_size_id = KaitaiStream.resolve_enum(BlenderBlend.PtrSize, self._io.read_u1())
            self._debug['ptr_size_id']['end'] = self._io.pos()
            self._debug['endian']['start'] = self._io.pos()
            self.endian = KaitaiStream.resolve_enum(BlenderBlend.Endian, self._io.read_u1())
            self._debug['endian']['end'] = self._io.pos()
            self._debug['version']['start'] = self._io.pos()
            self.version = (self._io.read_bytes(3)).decode(u"ASCII")
            self._debug['version']['end'] = self._io.pos()

        @property
        def psize(self):
            """Number of bytes that a pointer occupies."""
            if hasattr(self, '_m_psize'):
                return self._m_psize if hasattr(self, '_m_psize') else None

            self._m_psize = (8 if self.ptr_size_id == BlenderBlend.PtrSize.bits_64 else 4)
            return self._m_psize if hasattr(self, '_m_psize') else None


    class DnaField(KaitaiStruct):
        SEQ_FIELDS = ["idx_type", "idx_name"]
        def __init__(self, _io, _parent=None, _root=None):
            self._io = _io
            self._parent = _parent
            self._root = _root if _root else self
            self._debug = collections.defaultdict(dict)

        def _read(self):
            self._debug['idx_type']['start'] = self._io.pos()
            self.idx_type = self._io.read_u2le()
            self._debug['idx_type']['end'] = self._io.pos()
            self._debug['idx_name']['start'] = self._io.pos()
            self.idx_name = self._io.read_u2le()
            self._debug['idx_name']['end'] = self._io.pos()

        @property
        def type(self):
            if hasattr(self, '_m_type'):
                return self._m_type if hasattr(self, '_m_type') else None

            self._m_type = self._parent._parent.types[self.idx_type]
            return self._m_type if hasattr(self, '_m_type') else None

        @property
        def name(self):
            if hasattr(self, '_m_name'):
                return self._m_name if hasattr(self, '_m_name') else None

            self._m_name = self._parent._parent.names[self.idx_name]
            return self._m_name if hasattr(self, '_m_name') else None


    @property
    def sdna_structs(self):
        if hasattr(self, '_m_sdna_structs'):
            return self._m_sdna_structs if hasattr(self, '_m_sdna_structs') else None

        self._m_sdna_structs = self.blocks[(len(self.blocks) - 2)].body.structs
        return self._m_sdna_structs if hasattr(self, '_m_sdna_structs') else None


