import io
import struct

class Writer:
    def __init__(self, file, chunk_id: str, parent=None):
        self.file = file
        self.size: int = 0
        self.size_pos = None
        self.parent = parent

        self.file.write(bytes(chunk_id[0:4], "ascii"))

    def __enter__(self):
        self.size_pos = self.file.tell()
        self.file.write(struct.pack(f"<I", 0))

        return self

    def __exit__(self, exc_type, exc_value, traceback):
        if self.size > self.MAX_SIZE:
            raise OverflowError(f".msh file overflowed max size. size = {self.size} MAX_SIZE = {self.MAX_SIZE}")

        if (self.size % 4) > 0:
            padding = 4 - (self.size % 4)
            self.write_bytes(bytes([0 for i in range(padding)]))

        head_pos = self.file.tell()
        self.file.seek(self.size_pos)
        self.file.write(struct.pack(f"<I", self.size))
        self.file.seek(head_pos)

        if self.parent is not None:
            self.parent.size += self.size

    def write_bytes(self, packed_bytes):
        self.size += len(packed_bytes)
        self.file.write(packed_bytes)

    def write_string(self, string: str):
        self.write_bytes(bytes(string, "utf-8"))
        self.write_bytes(b'\0')

    def write_i8(self, *ints):
        self.write_bytes(struct.pack(f"<{len(ints)}b", *ints))

    def write_u8(self, *ints):
        self.write_bytes(struct.pack(f"<{len(ints)}B", *ints))

    def write_i16(self, *ints):
        self.write_bytes(struct.pack(f"<{len(ints)}h", *ints))

    def write_u16(self, *ints):
        self.write_bytes(struct.pack(f"<{len(ints)}H", *ints))

    def write_i32(self, *ints):
        self.write_bytes(struct.pack(f"<{len(ints)}i", *ints))

    def write_u32(self, *ints):
        self.write_bytes(struct.pack(f"<{len(ints)}I", *ints))

    def write_f32(self, *floats):
        self.write_bytes(struct.pack(f"<{len(floats)}f", *floats))

    def create_child(self, child_id: str):
        child = Writer(self.file, chunk_id=child_id, parent=self)
        self.size += 8

        return child

    MAX_SIZE: int = 2147483647 - 8