from enum import IntEnum

from .Mesh import BoneWeights4, SubMesh, VertexData
from .NamedObject import NamedObject
from .PPtr import PPtr, save_ptr
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, version) 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)
