class TypeReaderPlugin(Plugin):
    """
    type reader plugins
    """ class BaseTypeReader(object):
    target_type = None
    reader_name = None
    is_value_type = False
    is_generic_type = False
    is_enum_type = False
    file_platform = None
    file_version = None

    def __init__(self, stream=None, version=None):
        self.stream = stream
        self.version = version

    def __str__(self):
        return self.reader_name

    def read(self):
        raise ReaderError("Unimplemented type reader: '{}'".format(self.reader_name))

    def init_reader(self, file_platform=None, file_version=None):
        self.file_platform = file_platform
        self.file_version = file_version class ValueTypeReader(BaseTypeReader):
    is_value_type = True class Rectangle(_Rectangle):
    __slots__ = ()

    def xml(self, parent):
        root = ET.SubElement(parent, 'Rectangle')
        root.set('x', str(self.x))
        root.set('y', str(self.y))
        root.set('w', str(self.w))
        root.set('h', str(self.h))
        return root class Point(_Point):
    __slots__ = ()

    def xml(self, parent):
        root = ET.SubElement(parent, 'Point')
        root.set('x', str(self.x))
        root.set('y', str(self.y))
        return root class Plane(_Plane):
    __slots__ = ()

    def xml(self, parent):
        root = ET.SubElement(parent, 'Plane')
        root.set('d', str(self.d))
        self.normal.xml(root)
        return root class BoundingBox(_BoundingBox):
    __slots__ = ()

    def xml(self, parent):
        root = ET.SubElement(parent, 'BoundingBox')
        self.min.xml(root)
        self.max.xml(root)
        return root class BoundingSphere(_BoundingSphere):
    __slots__ = ()

    def xml(self, parent):
        root = ET.SubElement(parent, 'BoundingSphere')
        root.set('radius', str(self.radius))
        self.center.xml(root)
        return root class Ray(_Ray):
    __slots__ = ()

    def xml(self, parent):
        root = ET.SubElement(parent, 'Ray')
        self.pos.xml(root)
        self.dir.xml(root)
        return root class BoundingFrustum(_BoundingFrustum):
    __slots__ = ()

    def xml(self, parent):
        root = ET.SubElement(parent, 'BoundingFrustum')
        self.v.xml(root)
        return root class Vector2(_Vector2):
    __slots__ = ()

    def xml(self, parent):
        root = ET.SubElement(parent, 'Vector2')
        root.set('x', str(self.x))
        root.set('y', str(self.y))
        return root class Vector3(_Vector3):
    __slots__ = ()

    def xml(self, parent):
        root = ET.SubElement(parent, 'Vector3')
        root.set('x', str(self.x))
        root.set('y', str(self.y))
        root.set('z', str(self.z))
        return root class Vector4(_Vector4):
    __slots__ = ()

    def xml(self, parent):
        root = ET.SubElement(parent, 'Vector4')
        root.set('x', str(self.x))
        root.set('y', str(self.y))
        root.set('z', str(self.z))
        root.set('w', str(self.w))
        return root class Matrix(object):
    __slots__ = ('value',)

    def __init__(self, value):
        if len(value) != 16:
            raise ValueError("Invalid Matrix")
        self.value = value

    def __repr__(self):
        return 'Matrix(' + ','.join([str(v) for v in self.value]) + ')'

    def xml(self, parent):
        root = self.value.xml(parent, 'Matrix', 'Cell')
        return root class Quaternion(_Quarternion):
    __slots__ = ()

    def xml(self, parent):
        root = ET.SubElement(parent, 'Quaternion')
        root.set('x', str(self.x))
        root.set('y', str(self.y))
        root.set('z', str(self.z))
        root.set('w', str(self.w))
        return root class Color(_Color):
    __slots__ = ()

    def to_packed(self):
        return self.r | self.g << 8 | self.b << 16 | self.a << 24

    @staticmethod
    def from_packed(data):
        v_r = data & 0xff
        v_g = data >> 8 & 0xff
        v_b = data >> 16 & 0xff
        v_a = data >> 24 & 0xff
        return Color(v_r, v_g, v_b, v_a)

    @staticmethod
    def from_string(data):
        clean_data = data
        if clean_data.startswith('#'):
            clean_data = clean_data[1:]
        if len(clean_data) == 6:
            clean_data = 'ff' + clean_data
        if len(clean_data) != 8:
            raise ValueError("Invalid color: '{}'".format(data))
        v_a = int(clean_data[0:2], 16)
        v_r = int(clean_data[2:4], 16)
        v_g = int(clean_data[4:6], 16)
        v_b = int(clean_data[6:8], 16)
        return Color(v_r, v_g, v_b, v_a)

    def attrib(self):
        return "#{:02X}{:02X}{:02X}{:02X}".format(self.a, self.r, self.g, self.b)

    def xml(self, parent):
        root = ET.SubElement(parent, 'Color')
        root.set('c', self.attrib())
        return root class XNAList(list):
    __slots__ = ()

    def xml(self, parent=None, xml_tag='List', xml_entry='Entry', attrib=None):
        if sys.version < '3':
            conv = unicode
        else:
            conv = str
        if parent is None:
            root = ET.Element(xml_tag)
        else:
            root = ET.SubElement(parent, xml_tag)
        for cur_value in self:
            if hasattr(cur_value, 'xml'):
                cur_value.xml(root)
            elif attrib is not None:
                cur_tag = ET.SubElement(root, xml_entry)
                cur_tag.set(attrib, conv(cur_value))
            else:
                cur_tag = ET.SubElement(root, xml_entry)
                cur_tag.text = conv(cur_value)
        return root
from xnb_parse.type_reader import TypeReaderPlugin, BaseTypeReader, ValueTypeReader
from xnb_parse.xna_types.xna_math import (Rectangle, Point, Plane, BoundingBox, BoundingSphere, Ray, BoundingFrustum,
                                          Vector2, Vector3, Vector4, Matrix, Quaternion, Color)
from xnb_parse.xna_types.xna_system import XNAList
"""
math type readers
"""

from __future__ import print_function



class Vector2Reader(ValueTypeReader, TypeReaderPlugin):
    target_type = 'Microsoft.Xna.Framework.Vector2'
    reader_name = 'Microsoft.Xna.Framework.Content.Vector2Reader'

    def read(self):
        return Vector2._make(self.stream.unpack('2f'))


class Vector3Reader(ValueTypeReader, TypeReaderPlugin):
    target_type = 'Microsoft.Xna.Framework.Vector3'
    reader_name = 'Microsoft.Xna.Framework.Content.Vector3Reader'

    def read(self):
        return Vector3._make(self.stream.unpack('3f'))


class Vector4Reader(ValueTypeReader, TypeReaderPlugin):
    target_type = 'Microsoft.Xna.Framework.Vector4'
    reader_name = 'Microsoft.Xna.Framework.Content.Vector4Reader'

    def read(self):
        return Vector4._make(self.stream.unpack('4f'))


class MatrixReader(ValueTypeReader, TypeReaderPlugin):
    target_type = 'Microsoft.Xna.Framework.Matrix'
    reader_name = 'Microsoft.Xna.Framework.Content.MatrixReader'

    def read(self):
        return Matrix(XNAList(self.stream.unpack('16f')))


class QuaternionReader(ValueTypeReader, TypeReaderPlugin):
    target_type = 'Microsoft.Xna.Framework.Quaternion'
    reader_name = 'Microsoft.Xna.Framework.Content.QuaternionReader'

    def read(self):
        return Quaternion._make(self.stream.unpack('4f'))


class ColorReader(ValueTypeReader, TypeReaderPlugin):
    target_type = 'Microsoft.Xna.Framework.Graphics.Color'
    reader_name = 'Microsoft.Xna.Framework.Content.ColorReader'

    def read(self):
        return Color._make(self.stream.unpack('4B'))


class PlaneReader(ValueTypeReader, TypeReaderPlugin):
    target_type = 'Microsoft.Xna.Framework.Plane'
    reader_name = 'Microsoft.Xna.Framework.Content.PlaneReader'

    def read(self):
        values = self.stream.unpack('3f f')
        plane_normal = Vector3._make(values[0:3])
        plane_d = values[3]
        return Plane(plane_normal, plane_d)


class PointReader(ValueTypeReader, TypeReaderPlugin):
    target_type = 'Microsoft.Xna.Framework.Point'
    reader_name = 'Microsoft.Xna.Framework.Content.PointReader'

    def read(self):
        return Point._make(self.stream.unpack('2i'))


class RectangleReader(ValueTypeReader, TypeReaderPlugin):
    target_type = 'Microsoft.Xna.Framework.Rectangle'
    reader_name = 'Microsoft.Xna.Framework.Content.RectangleReader'

    def read(self):
        return Rectangle._make(self.stream.unpack('4i'))


class BoundingBoxReader(ValueTypeReader, TypeReaderPlugin):
    target_type = 'Microsoft.Xna.Framework.BoundingBox'
    reader_name = 'Microsoft.Xna.Framework.Content.BoundingBoxReader'

    def read(self):
        values = self.stream.unpack('3f 3f')
        v_min = Vector3._make(values[0:3])
        v_max = Vector3._make(values[3:6])
        return BoundingBox(v_min, v_max)


class BoundingSphereReader(ValueTypeReader, TypeReaderPlugin):
    target_type = 'Microsoft.Xna.Framework.BoundingSphere'
    reader_name = 'Microsoft.Xna.Framework.Content.BoundingSphereReader'

    def read(self):
        values = self.stream.unpack('3f f')
        v_centre = Vector3._make(values[0:3])
        v_radius = values[3]
        return BoundingSphere(v_centre, v_radius)


class BoundingFrustumReader(BaseTypeReader, TypeReaderPlugin):
    target_type = 'Microsoft.Xna.Framework.BoundingFrustum'
    reader_name = 'Microsoft.Xna.Framework.Content.BoundingFrustumReader'

    def read(self):

        return BoundingFrustum._make(Matrix(XNAList(self.stream.unpack('16f'))))