from enum import IntEnum

from .Mesh import BoneWeights4, SubMesh, VertexData
from .NamedObject import NamedObject
from .PPtr import PPtr
from ..export import SpriteHelper
from ..enums import SpriteMeshType
from ..streams import EndianBinaryWriter


class Sprite(NamedObject):
    @property
    def image(self):
        return SpriteHelper.get_image_from_sprite(self)

    def __init__(self, reader):
        super().__init__(reader=reader)
        version = self.version

        self.m_Rect = reader.read_rectangle_f()
        self.m_Offset = reader.read_vector2()
        if version >= (4, 5):  # 4.5 and up
            self.m_Border = reader.read_vector4()

        self.m_PixelsToUnits = reader.read_float()
        if version >= (5, 4, 2) or (
            version >= (5, 4, 1, 3) and self.build_type.IsPatch
        ):  # 5.4.1p3 and up
            self.m_Pivot = reader.read_vector2()

        self.m_Extrude = reader.read_u_int()
        if version >= (5, 3):  # 5.3 and up
            self.m_IsPolygon = reader.read_boolean()
            reader.align_stream()

        if version >= (2017,):  # 2017 and up
            first = reader.read_bytes(16)  # GUID
            second = reader.read_long()
            self.m_RenderDataKey = (first, second)
            self.m_AtlasTags = reader.read_string_array()
            self.m_SpriteAtlas = PPtr(reader)  # SpriteAtlas

        self.m_RD = SpriteRenderData(reader)

        if version >= (2017,):  # 2017 and up
            m_PhysicsShapeSize = reader.read_int()
            self.m_PhysicsShape = [
                reader.read_vector2_array() for _ in range(m_PhysicsShapeSize)
            ]

        if version >= (2018,):  # 2018 and up
            m_BonesSize = reader.read_int()
            self.m_Bones = [
                reader.read_vector2_array() for _ in range(m_BonesSize)
            ]

    def save(self, writer: EndianBinaryWriter = None):
        if writer is None:
            writer = EndianBinaryWriter(endian=self.reader.endian)
        version = self.version

        super().save(writer)
        writer.write_rectangle_f(self.m_Rect)
        writer.write_vector2(self.m_Offset)
        if version >= (4, 5):  # 4.5 and up
            writer.write_vector4(self.m_Border)

        writer.write_float(self.m_PixelsToUnits)
        if version >= (5, 4, 2) or (
            version >= (5, 4, 1, 3) and self.build_type.IsPatch
        ):  # 5.4.1p3 and up
            writer.write_vector2(self.m_Pivot)

        writer.write_u_int(self.m_Extrude)
        if version >= (5, 3):  # 5.3 and up
            writer.write_boolean(self.m_IsPolygon)
            writer.align_stream()

        if version >= (2017,):  # 2017 and up
            writer.write_bytes(self.m_RenderDataKey[0])  # GUID
            writer.write_long(self.m_RenderDataKey[1])
            writer.write_string_array(self.m_AtlasTags)
            self.m_SpriteAtlas.save(writer)  # SpriteAtlas

        self.m_RD.save(writer, version)

        if version >= (2017,):  # 2017 and up
            writer.write_int(len(self.m_PhysicsShape))
            for phys in self.m_PhysicsShape:
                writer.write_vector2_array(phys)

        if version >= (2018,):  # 2018 and up
            writer.write_int(len(self.m_Bones))
            for bone in self.m_Bones:
                writer.write_vector2_array(bone)

        self.set_raw_data(writer.bytes)


class SecondarySpriteTexture:
    def __init__(self, reader):
        self.texture = PPtr(reader)  # Texture2D
        self.name = reader.read_string_to_null()

    def save(self, writer):
        self.texture.save(writer)
        writer.write_string_to_null(self.name)


class SpritePackingRotation(IntEnum):
    kSPRNone = (0,)
    kSPRFlipHorizontal = (1,)
    kSPRFlipVertical = (2,)
    kSPRRotate180 = (3,)
    kSPRRotate90 = 4


class SpritePackingMode(IntEnum):
    kSPMTight = (0,)
    kSPMRectangle = 1


class SpriteSettings:
    def __init__(self, reader):
        self.value = reader.read_u_int()

    @property
    def value(self):
        return self.m_settingsRaw

    @value.setter
    def value(self, _value):
        self.m_settingsRaw = _value

        self.packed = self.m_settingsRaw & 1  # 1
        self.packingMode = SpritePackingMode((self.m_settingsRaw >> 1) & 1)  # 1
        self.packingRotation = SpritePackingRotation((self.m_settingsRaw >> 2) & 0xF)  # 4
        self.meshType = SpriteMeshType((self.m_settingsRaw >> 6) & 1)  # 1
        # rest of the bits are reserved

    def save(self, writer):
        writer.write_u_int(self.m_settingsRaw)


class SpriteVertex:
    def __init__(self, reader):
        version = reader.version

        self.pos = reader.read_vector3()
        if version[:2] <= (4, 3):  # 4.3 and down
            self.uv = reader.read_vector2()

    def save(self, writer, version):
        writer.write_vector3(self.pos)
        if version[:2] <= (4, 3):  # 4.3 and down
            writer.write__vector2(self.uv)

class SpriteRenderData:
    def __init__(self, reader):
        version = reader.version

        self.texture = PPtr(reader)  # Texture2D
        if version >= (5, 2):  # 5.2 and up
            self.alphaTexture = PPtr(reader)  # Texture2D

        if version >= (2019,):  # 2019 and up
            secondaryTexturesSize = reader.read_int()
            self.secondaryTextures = [
                SecondarySpriteTexture(reader) for _ in range(secondaryTexturesSize)
            ]

        if version >= (5, 6):  # 5.6 and up
            SubMeshesSize = reader.read_int()
            self.m_SubMeshes = [SubMesh(reader) for _ in range(SubMeshesSize)]
            IndexBufferSize = reader.read_int()
            self.m_IndexBuffer = reader.read_bytes(IndexBufferSize)
            reader.align_stream()
            self.m_VertexData = VertexData(reader)
        else:
            verticesSize = reader.read_int()
            self.vertices = [SpriteVertex(reader) for _ in range(verticesSize)]
            self.indices = reader.read_u_short_array()
            reader.align_stream()

        if version >= (2018,):  # 2018 and up
            self.m_Bindpose = reader.read_matrix_array()
            if version < (2018, 2):  # 2018.2 down
                self.m_SourceSkinSize = reader.read_int()
                self.m_SourceSkin = [BoneWeights4(reader)]

        self.textureRect = reader.read_rectangle_f()
        self.textureRectOffset = reader.read_vector2()
        if version >= (5, 6):  # 5.6 and up
            self.atlasRectOffset = reader.read_vector2()

        self.settingsRaw = SpriteSettings(reader)
        if version >= (4, 5):  # 4.5 and up
            self.uvTransform = reader.read_vector4()

        if version >= (2017,):  # 2017 and up
            self.downscaleMultiplier = reader.read_float()

    def save(self, writer, version):
        self.texture.save(writer)  # Texture2D
        if version >= (5, 2):  # 5.2 and up
            self.alphaTexture.save(writer)  # Texture2D

        if version >= (2019,):  # 2019 and up
            writer.write_int(len(self.secondaryTextures))
            for tex in self.secondaryTextures:
                tex.save(writer)

        if version >= (5, 6):  # 5.6 and up
            writer.write_int(len(self.m_SubMeshes))
            for mesh in self.m_SubMeshes:
                mesh.save(writer, version)
            writer.write_int(len(self.m_IndexBuffer))
            writer.write_bytes(self.m_IndexBuffer)
            writer.align_stream()
            self.m_VertexData.save(writer, version)
        else:
            writer.write_int(len(self.vertices))
            for vertex in self.vertices:
                vertex.save(writer, version)
            writer.write_u_short_array(self.indices)
            writer.align_stream()

        if version >= (2018,):  # 2018 and up
            writer.write_matrix_array(self.m_Bindpose)
            if version < (2018, 2):  # 2018.2 down
                writer.write_int(self.m_SourceSkinSize)
                self.m_SourceSkin[0].save(writer)

        writer.write_rectangle_f(self.textureRect)
        writer.write_vector2(self.textureRectOffset)
        if version >= (5, 6):  # 5.6 and up
            writer.write_vector2(self.atlasRectOffset)

        self.settingsRaw.save(writer)
        if version >= (4, 5):  # 4.5 and up
            writer.write_vector4(self.uvTransform)

        if version >= (2017,):  # 2017 and up
            writer.write_float(self.downscaleMultiplier)
