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 EnumTypeReader(ValueTypeReader):
    is_enum_type = True
    enum_type = None
    enum_type4 = None

    def read(self):
        value = self.stream.read_int32()
        if self.file_version == VERSION_40 and self.enum_type4 is not None:
            enum_type = self.enum_type4
        else:
            enum_type = self.enum_type
        if callable(enum_type):
            return enum_type(value)
        else:
            return value class Int32Reader(ValueTypeReader, TypeReaderPlugin):
    target_type = 'System.Int32'
    reader_name = 'Microsoft.Xna.Framework.Content.Int32Reader'

    def read(self):
        return self.stream.read_int32() class ListReader(GenericTypeReader, TypeReaderPlugin):
    generic_target_type = 'System.Collections.Generic.List`1'
    generic_reader_name = 'Microsoft.Xna.Framework.Content.ListReader`1'

    def read(self):
        elements = self.stream.read_int32()
        if self.readers[0].is_value_type:
            return XNAList([self.readers[0].read() for _ in range(elements)])
        else:
            return XNAList([self.stream.read_object(self.readers[0]) for _ in range(elements)]) class ArrayReader(GenericTypeReader, TypeReaderPlugin):
    generic_target_type = 'System.Array`1'
    generic_reader_name = 'Microsoft.Xna.Framework.Content.ArrayReader`1'

    def read(self):
        elements = self.stream.read_int32()
        if self.readers[0].is_value_type:
            return XNAList([self.readers[0].read() for _ in range(elements)])
        else:
            return XNAList([self.stream.read_object(self.readers[0]) for _ in range(elements)]) class ShardNotes(Enum):
    __slots__ = ()
    enum_values = dict(enumerate(['C2', 'Csharp2', 'D2', 'Dsharp2', 'E2', 'F2', 'Fsharp2', 'G2', 'Gsharp2', 'A2',
                                  'Asharp2', 'B2', 'C3', 'Csharp3', 'D3', 'Dsharp3', 'E3', 'F3', 'Fsharp3', 'G3',
                                  'Gsharp3', 'A3', 'Asharp3', 'B3', 'C4']))
    xml_tag = 'Note' class AssembleChords(Enum):
    __slots__ = ()
    enum_values = dict(enumerate(['C_maj', 'Csharp_maj', 'D_maj', 'Dsharp_maj', 'E_maj', 'F_maj', 'Fsharp_maj', 'G_maj',
                                  'Gsharp_maj', 'A_maj', 'Asharp_maj', 'B_maj']))
    xml_tag = 'Chord' class TrackedSong(object):
    def __init__(self, loops, name, tempo, time_signature, notes, assemble_chord, random_ordering, custom_ordering):
        self.loops = loops
        self.name = name
        self.tempo = tempo
        self.time_signature = time_signature
        self.notes = notes
        self.assemble_chord = assemble_chord
        self.random_ordering = random_ordering
        self.custom_ordering = custom_ordering

    def __str__(self):
        return "TrackedSong '{}'".format(self.name)

    def xml(self, parent=None):
        if parent is None:
            root = ET.Element('TrackedSong')
        else:
            root = ET.SubElement(parent, 'TrackedSong')
        root.set('name', self.name)
        root.set('tempo', str(self.tempo))
        root.set('timeSignature', str(self.time_signature))
        if self.assemble_chord is not None:
            self.assemble_chord.xml(root)
        if self.notes is not None:
            self.notes.xml(root, 'Notes')
        if self.loops is not None:
            self.loops.xml(root, 'Loops')
        if self.random_ordering is not None:
            root.set('randomOrdering', str(self.random_ordering))
        if self.custom_ordering is not None:
            self.custom_ordering.xml(root, 'CustomOrdering', 'Order')
        return root class Loop(object):
    def __init__(self, duration, loop_times_from, loop_times_to, name, trigger_from, trigger_to, delay, night, day,
                 dusk, dawn, fractional_time, one_at_a_time, cut_off_tail):
        self.duration = duration
        self.loop_times_from = loop_times_from
        self.loop_times_to = loop_times_to
        self.name = name
        self.trigger_from = trigger_from
        self.trigger_to = trigger_to
        self.delay = delay
        self.night = night
        self.day = day
        self.dusk = dusk
        self.dawn = dawn
        self.fractional_time = fractional_time
        self.one_at_a_time = one_at_a_time
        self.cut_off_tail = cut_off_tail

    def __str__(self):
        return "Loop '{}' d:{}".format(self.name, self.duration)

    def xml(self, parent):
        root = ET.SubElement(parent, 'Loop')
        root.set('name', self.name)
        root.set('duration', str(self.duration))
        root.set('loopTimesFrom', str(self.loop_times_from))
        root.set('loopTimesTo', str(self.loop_times_to))
        root.set('triggerFrom', str(self.trigger_from))
        root.set('triggerTo', str(self.trigger_to))
        root.set('delay', str(self.delay))
        root.set('fractionalTime', str(self.fractional_time))
        root.set('oneAtATime', str(self.one_at_a_time))
        root.set('cutOffTail', str(self.cut_off_tail))
        root.set('night', str(self.night))
        root.set('day', str(self.day))
        root.set('dusk', str(self.dusk))
        root.set('dawn', str(self.dawn))
        return root
from xnb_parse.type_reader import TypeReaderPlugin, BaseTypeReader, EnumTypeReader
from xnb_parse.type_readers.xna_primitive import Int32Reader
from xnb_parse.type_readers.xna_system import ListReader, ArrayReader
from xnb_parse.xna_types.fez.fez_music import ShardNotes, AssembleChords, TrackedSong, Loop
"""
FEZ music type readers
"""

from __future__ import print_function



class TrackedSongReader(BaseTypeReader, TypeReaderPlugin):
    target_type = 'FezEngine.Structure.TrackedSong'
    reader_name = 'FezEngine.Readers.TrackedSongReader'

    def read(self):
        loops = self.stream.read_object(ListReader, [LoopReader])
        name = self.stream.read_string()
        tempo = self.stream.read_int32()
        time_signature = self.stream.read_int32()
        notes = self.stream.read_object(ArrayReader, [ShardNotesReader])
        assemble_chord = self.stream.read_object(AssembleChordsReader)
        random_ordering = self.stream.read_boolean()
        custom_ordering = self.stream.read_object(ArrayReader, [Int32Reader])
        return TrackedSong(loops, name, tempo, time_signature, notes, assemble_chord, random_ordering, custom_ordering)


class LoopReader(BaseTypeReader, TypeReaderPlugin):
    target_type = 'FezEngine.Structure.Loop'
    reader_name = 'FezEngine.Readers.LoopReader'

    def read(self):
        duration = self.stream.read_int32()
        loop_times_from = self.stream.read_int32()
        loop_times_to = self.stream.read_int32()
        name = self.stream.read_string()
        trigger_from = self.stream.read_int32()
        trigger_to = self.stream.read_int32()
        delay = self.stream.read_int32()
        night = self.stream.read_boolean()
        day = self.stream.read_boolean()
        dusk = self.stream.read_boolean()
        dawn = self.stream.read_boolean()
        fractional_time = self.stream.read_boolean()
        one_at_a_time = self.stream.read_boolean()
        cut_off_tail = self.stream.read_boolean()

        return Loop(duration, loop_times_from, loop_times_to, name, trigger_from, trigger_to, delay, night, day, dusk,