# 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 MinecraftNbt(KaitaiStruct):
    """A structured binary format native to Minecraft for saving game data and transferring
    it over the network (in multiplayer), such as player data
    ([`<player>.dat`](https://minecraft.gamepedia.com/Player.dat_format); contains
    e.g. player's inventory and location), saved worlds
    ([`level.dat`](
      https://minecraft.gamepedia.com/Java_Edition_level_format#level.dat_format
    ) and [Chunk format](https://minecraft.gamepedia.com/Chunk_format#NBT_structure)),
    list of saved multiplayer servers
    ([`servers.dat`](https://minecraft.gamepedia.com/Servers.dat_format)) and so on -
    see <https://minecraft.gamepedia.com/NBT_format#Uses>.
    
    The entire file should be _gzip_-compressed (in accordance with the original
    specification [NBT.txt](
      https://web.archive.org/web/20110723210920/https://www.minecraft.net/docs/NBT.txt
    ) by Notch), but can also be compressed with _zlib_ or uncompressed.
    
    This spec can only handle uncompressed NBT data, so be sure to first detect
    what type of data you are dealing with. You can use the Unix `file` command
    to do this (`file-5.20` or later is required; older versions do not recognize
    _zlib_-compressed data and return `application/octet-stream` instead):
    
    ```shell
    file --brief --mime-type input-unknown.nbt
    ```
    
    If it says:
    
      * `application/x-gzip` or `application/gzip` (since `file-5.37`), you can decompress it by
        * `gunzip -c input-gzip.nbt > output.nbt` or
        * `python3 -c "import sys, gzip; sys.stdout.buffer.write(
          gzip.decompress(sys.stdin.buffer.read()) )" < input-gzip.nbt > output.nbt`
      * `application/zlib`, you can use
        * `openssl zlib -d -in input-zlib.nbt -out output.nbt` (does not work on most systems)
        * `python3 -c "import sys, zlib; sys.stdout.buffer.write(
          zlib.decompress(sys.stdin.buffer.read()) )" < input-zlib.nbt > output.nbt`
      * something else (especially `image/x-pcx` and `application/octet-stream`),
        it is most likely already uncompressed.
    
    The file `output.nbt` generated by one of the above commands can already be
    processed with this Kaitai Struct specification.
    
    This spec **only** implements the Java edition format. There is also
    a [Bedrock edition](https://wiki.vg/NBT#Bedrock_edition) NBT format,
    which uses little-endian encoding and has a few other differences, but it isn't
    as popular as the Java edition format.
    
    **Implementation note:** strings in `TAG_String` are incorrectly decoded with
    standard UTF-8, while they are encoded in [**Modified UTF-8**](
      https://docs.oracle.com/javase/8/docs/api/java/io/DataInput.html#modified-utf-8
    ) (MUTF-8). That's because MUTF-8 is not supported natively by most target
    languages, and thus one must use external libraries to achieve a fully-compliant
    decoder. But decoding in standard UTF-8 is still better than nothing, and
    it usually works fine.
    
    All Unicode code points with incompatible representations in MUTF-8 and UTF-8 are
    U+0000 (_NUL_), U+D800-U+DFFF (_High_ and _Low Surrogates_) and U+10000-U+10FFFF
    (all _Supplementary_ Planes; includes e.g. emoticons, pictograms).
    A _MUTF-8_-encoded string containing these code points cannot be successfully
    decoded as UTF-8. The behavior in this case depends on the target language -
    usually an exception is thrown, or the bytes that are not valid UTF-8
    are replaced or ignored.
    
    **Sample files:**
    
      * <https://wiki.vg/NBT#Download>
      * <https://github.com/twoolie/NBT/blob/f9e892e/tests/world_test/data/scoreboard.dat>
      * <https://github.com/chmod222/cNBT/tree/3f74b69/testdata>
      * <https://github.com/PistonDevelopers/hematite_nbt/tree/0b85f89/tests>
    
    .. seealso::
       Source - https://wiki.vg/NBT
    
    
    .. seealso::
       Source - https://web.archive.org/web/20110723210920/https://www.minecraft.net/docs/NBT.txt
    
    
    .. seealso::
       Source - https://minecraft.gamepedia.com/NBT_format
    """

    class Tag(Enum):
        end = 0
        byte = 1
        short = 2
        int = 3
        long = 4
        float = 5
        double = 6
        byte_array = 7
        string = 8
        list = 9
        compound = 10
        int_array = 11
        long_array = 12
    SEQ_FIELDS = ["root_check", "root"]
    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):
        if  ((self.root_type == MinecraftNbt.Tag.end) and (False)) :
            self._debug['root_check']['start'] = self._io.pos()
            self.root_check = self._io.read_bytes(0)
            self._debug['root_check']['end'] = self._io.pos()

        self._debug['root']['start'] = self._io.pos()
        self.root = MinecraftNbt.NamedTag(self._io, self, self._root)
        self.root._read()
        self._debug['root']['end'] = self._io.pos()

    class TagLongArray(KaitaiStruct):
        SEQ_FIELDS = ["num_tags", "tags"]
        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['num_tags']['start'] = self._io.pos()
            self.num_tags = self._io.read_s4be()
            self._debug['num_tags']['end'] = self._io.pos()
            self._debug['tags']['start'] = self._io.pos()
            self.tags = [None] * (self.num_tags)
            for i in range(self.num_tags):
                if not 'arr' in self._debug['tags']:
                    self._debug['tags']['arr'] = []
                self._debug['tags']['arr'].append({'start': self._io.pos()})
                self.tags[i] = self._io.read_s8be()
                self._debug['tags']['arr'][i]['end'] = self._io.pos()

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

        @property
        def tags_type(self):
            if hasattr(self, '_m_tags_type'):
                return self._m_tags_type if hasattr(self, '_m_tags_type') else None

            self._m_tags_type = MinecraftNbt.Tag.long
            return self._m_tags_type if hasattr(self, '_m_tags_type') else None


    class TagByteArray(KaitaiStruct):
        SEQ_FIELDS = ["len_data", "data"]
        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['len_data']['start'] = self._io.pos()
            self.len_data = self._io.read_s4be()
            self._debug['len_data']['end'] = self._io.pos()
            self._debug['data']['start'] = self._io.pos()
            self.data = self._io.read_bytes(self.len_data)
            self._debug['data']['end'] = self._io.pos()


    class TagIntArray(KaitaiStruct):
        SEQ_FIELDS = ["num_tags", "tags"]
        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['num_tags']['start'] = self._io.pos()
            self.num_tags = self._io.read_s4be()
            self._debug['num_tags']['end'] = self._io.pos()
            self._debug['tags']['start'] = self._io.pos()
            self.tags = [None] * (self.num_tags)
            for i in range(self.num_tags):
                if not 'arr' in self._debug['tags']:
                    self._debug['tags']['arr'] = []
                self._debug['tags']['arr'].append({'start': self._io.pos()})
                self.tags[i] = self._io.read_s4be()
                self._debug['tags']['arr'][i]['end'] = self._io.pos()

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

        @property
        def tags_type(self):
            if hasattr(self, '_m_tags_type'):
                return self._m_tags_type if hasattr(self, '_m_tags_type') else None

            self._m_tags_type = MinecraftNbt.Tag.int
            return self._m_tags_type if hasattr(self, '_m_tags_type') else None


    class TagList(KaitaiStruct):
        SEQ_FIELDS = ["tags_type", "num_tags", "tags"]
        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['tags_type']['start'] = self._io.pos()
            self.tags_type = KaitaiStream.resolve_enum(MinecraftNbt.Tag, self._io.read_u1())
            self._debug['tags_type']['end'] = self._io.pos()
            self._debug['num_tags']['start'] = self._io.pos()
            self.num_tags = self._io.read_s4be()
            self._debug['num_tags']['end'] = self._io.pos()
            self._debug['tags']['start'] = self._io.pos()
            self.tags = [None] * (self.num_tags)
            for i in range(self.num_tags):
                if not 'arr' in self._debug['tags']:
                    self._debug['tags']['arr'] = []
                self._debug['tags']['arr'].append({'start': self._io.pos()})
                _on = self.tags_type
                if _on == MinecraftNbt.Tag.long_array:
                    if not 'arr' in self._debug['tags']:
                        self._debug['tags']['arr'] = []
                    self._debug['tags']['arr'].append({'start': self._io.pos()})
                    _t_tags = MinecraftNbt.TagLongArray(self._io, self, self._root)
                    _t_tags._read()
                    self.tags[i] = _t_tags
                    self._debug['tags']['arr'][i]['end'] = self._io.pos()
                elif _on == MinecraftNbt.Tag.compound:
                    if not 'arr' in self._debug['tags']:
                        self._debug['tags']['arr'] = []
                    self._debug['tags']['arr'].append({'start': self._io.pos()})
                    _t_tags = MinecraftNbt.TagCompound(self._io, self, self._root)
                    _t_tags._read()
                    self.tags[i] = _t_tags
                    self._debug['tags']['arr'][i]['end'] = self._io.pos()
                elif _on == MinecraftNbt.Tag.double:
                    if not 'arr' in self._debug['tags']:
                        self._debug['tags']['arr'] = []
                    self._debug['tags']['arr'].append({'start': self._io.pos()})
                    self.tags[i] = self._io.read_f8be()
                    self._debug['tags']['arr'][i]['end'] = self._io.pos()
                elif _on == MinecraftNbt.Tag.list:
                    if not 'arr' in self._debug['tags']:
                        self._debug['tags']['arr'] = []
                    self._debug['tags']['arr'].append({'start': self._io.pos()})
                    _t_tags = MinecraftNbt.TagList(self._io, self, self._root)
                    _t_tags._read()
                    self.tags[i] = _t_tags
                    self._debug['tags']['arr'][i]['end'] = self._io.pos()
                elif _on == MinecraftNbt.Tag.float:
                    if not 'arr' in self._debug['tags']:
                        self._debug['tags']['arr'] = []
                    self._debug['tags']['arr'].append({'start': self._io.pos()})
                    self.tags[i] = self._io.read_f4be()
                    self._debug['tags']['arr'][i]['end'] = self._io.pos()
                elif _on == MinecraftNbt.Tag.short:
                    if not 'arr' in self._debug['tags']:
                        self._debug['tags']['arr'] = []
                    self._debug['tags']['arr'].append({'start': self._io.pos()})
                    self.tags[i] = self._io.read_s2be()
                    self._debug['tags']['arr'][i]['end'] = self._io.pos()
                elif _on == MinecraftNbt.Tag.int:
                    if not 'arr' in self._debug['tags']:
                        self._debug['tags']['arr'] = []
                    self._debug['tags']['arr'].append({'start': self._io.pos()})
                    self.tags[i] = self._io.read_s4be()
                    self._debug['tags']['arr'][i]['end'] = self._io.pos()
                elif _on == MinecraftNbt.Tag.byte_array:
                    if not 'arr' in self._debug['tags']:
                        self._debug['tags']['arr'] = []
                    self._debug['tags']['arr'].append({'start': self._io.pos()})
                    _t_tags = MinecraftNbt.TagByteArray(self._io, self, self._root)
                    _t_tags._read()
                    self.tags[i] = _t_tags
                    self._debug['tags']['arr'][i]['end'] = self._io.pos()
                elif _on == MinecraftNbt.Tag.byte:
                    if not 'arr' in self._debug['tags']:
                        self._debug['tags']['arr'] = []
                    self._debug['tags']['arr'].append({'start': self._io.pos()})
                    self.tags[i] = self._io.read_s1()
                    self._debug['tags']['arr'][i]['end'] = self._io.pos()
                elif _on == MinecraftNbt.Tag.int_array:
                    if not 'arr' in self._debug['tags']:
                        self._debug['tags']['arr'] = []
                    self._debug['tags']['arr'].append({'start': self._io.pos()})
                    _t_tags = MinecraftNbt.TagIntArray(self._io, self, self._root)
                    _t_tags._read()
                    self.tags[i] = _t_tags
                    self._debug['tags']['arr'][i]['end'] = self._io.pos()
                elif _on == MinecraftNbt.Tag.string:
                    if not 'arr' in self._debug['tags']:
                        self._debug['tags']['arr'] = []
                    self._debug['tags']['arr'].append({'start': self._io.pos()})
                    _t_tags = MinecraftNbt.TagString(self._io, self, self._root)
                    _t_tags._read()
                    self.tags[i] = _t_tags
                    self._debug['tags']['arr'][i]['end'] = self._io.pos()
                elif _on == MinecraftNbt.Tag.long:
                    if not 'arr' in self._debug['tags']:
                        self._debug['tags']['arr'] = []
                    self._debug['tags']['arr'].append({'start': self._io.pos()})
                    self.tags[i] = self._io.read_s8be()
                    self._debug['tags']['arr'][i]['end'] = self._io.pos()
                self._debug['tags']['arr'][i]['end'] = self._io.pos()

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


    class TagString(KaitaiStruct):
        SEQ_FIELDS = ["len_data", "data"]
        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['len_data']['start'] = self._io.pos()
            self.len_data = self._io.read_u2be()
            self._debug['len_data']['end'] = self._io.pos()
            self._debug['data']['start'] = self._io.pos()
            self.data = (self._io.read_bytes(self.len_data)).decode(u"utf-8")
            self._debug['data']['end'] = self._io.pos()


    class TagCompound(KaitaiStruct):
        SEQ_FIELDS = ["tags"]
        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['tags']['start'] = self._io.pos()
            self.tags = []
            i = 0
            while True:
                if not 'arr' in self._debug['tags']:
                    self._debug['tags']['arr'] = []
                self._debug['tags']['arr'].append({'start': self._io.pos()})
                _t_tags = MinecraftNbt.NamedTag(self._io, self, self._root)
                _t_tags._read()
                _ = _t_tags
                self.tags.append(_)
                self._debug['tags']['arr'][len(self.tags) - 1]['end'] = self._io.pos()
                if _.is_tag_end:
                    break
                i += 1
            self._debug['tags']['end'] = self._io.pos()

        @property
        def dump_num_tags(self):
            if hasattr(self, '_m_dump_num_tags'):
                return self._m_dump_num_tags if hasattr(self, '_m_dump_num_tags') else None

            self._m_dump_num_tags = (len(self.tags) - (1 if  ((len(self.tags) >= 1) and (self.tags[-1].is_tag_end))  else 0))
            return self._m_dump_num_tags if hasattr(self, '_m_dump_num_tags') else None


    class NamedTag(KaitaiStruct):
        SEQ_FIELDS = ["type", "name", "payload"]
        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['type']['start'] = self._io.pos()
            self.type = KaitaiStream.resolve_enum(MinecraftNbt.Tag, self._io.read_u1())
            self._debug['type']['end'] = self._io.pos()
            if not (self.is_tag_end):
                self._debug['name']['start'] = self._io.pos()
                self.name = MinecraftNbt.TagString(self._io, self, self._root)
                self.name._read()
                self._debug['name']['end'] = self._io.pos()

            if not (self.is_tag_end):
                self._debug['payload']['start'] = self._io.pos()
                _on = self.type
                if _on == MinecraftNbt.Tag.long_array:
                    self.payload = MinecraftNbt.TagLongArray(self._io, self, self._root)
                    self.payload._read()
                elif _on == MinecraftNbt.Tag.compound:
                    self.payload = MinecraftNbt.TagCompound(self._io, self, self._root)
                    self.payload._read()
                elif _on == MinecraftNbt.Tag.double:
                    self.payload = self._io.read_f8be()
                elif _on == MinecraftNbt.Tag.list:
                    self.payload = MinecraftNbt.TagList(self._io, self, self._root)
                    self.payload._read()
                elif _on == MinecraftNbt.Tag.float:
                    self.payload = self._io.read_f4be()
                elif _on == MinecraftNbt.Tag.short:
                    self.payload = self._io.read_s2be()
                elif _on == MinecraftNbt.Tag.int:
                    self.payload = self._io.read_s4be()
                elif _on == MinecraftNbt.Tag.byte_array:
                    self.payload = MinecraftNbt.TagByteArray(self._io, self, self._root)
                    self.payload._read()
                elif _on == MinecraftNbt.Tag.byte:
                    self.payload = self._io.read_s1()
                elif _on == MinecraftNbt.Tag.int_array:
                    self.payload = MinecraftNbt.TagIntArray(self._io, self, self._root)
                    self.payload._read()
                elif _on == MinecraftNbt.Tag.string:
                    self.payload = MinecraftNbt.TagString(self._io, self, self._root)
                    self.payload._read()
                elif _on == MinecraftNbt.Tag.long:
                    self.payload = self._io.read_s8be()
                self._debug['payload']['end'] = self._io.pos()


        @property
        def is_tag_end(self):
            if hasattr(self, '_m_is_tag_end'):
                return self._m_is_tag_end if hasattr(self, '_m_is_tag_end') else None

            self._m_is_tag_end = self.type == MinecraftNbt.Tag.end
            return self._m_is_tag_end if hasattr(self, '_m_is_tag_end') else None


    @property
    def root_type(self):
        if hasattr(self, '_m_root_type'):
            return self._m_root_type if hasattr(self, '_m_root_type') else None

        _pos = self._io.pos()
        self._io.seek(0)
        self._debug['_m_root_type']['start'] = self._io.pos()
        self._m_root_type = KaitaiStream.resolve_enum(MinecraftNbt.Tag, self._io.read_u1())
        self._debug['_m_root_type']['end'] = self._io.pos()
        self._io.seek(_pos)
        if not self.root_type == MinecraftNbt.Tag.compound:
            raise kaitaistruct.ValidationNotEqualError(MinecraftNbt.Tag.compound, self.root_type, self._io, u"/instances/root_type")
        return self._m_root_type if hasattr(self, '_m_root_type') else None


