'''
Rhythm helpers
'''
import mido


BASE_RESOLUTION = (2 ** 6) * 3

class AbstractTimeUnit(object):
    unit_multiplier = -1
    resolution = BASE_RESOLUTION

    def __init__(self):
        self._dots = 0
        self._joined_notes = []

    def dot(self):
        '''
        A dot adds 1/2 of the prior unit
        ala
        . + 1/2
        .. + 1/2 + 1/4
        ... + 1/2 + 1/4 + 1/8
        '''
        self._dots += 1

        return self

    def _sum_dots(self, tick):
        if self._dots is not 0:
            return sum([tick/(2.0**i) for i in xrange(1, self._dots +1)])

        return 0

    def join(self, tu):
        self._joined_notes.append(tu)

        return self

    def _sum_joined(self):
        return sum([tu.get_tick(as_int=False) for tu in self._joined_notes])

    def get_tick(self, resolution=None, as_int=True):
        '''
        Gets the tick + ticks generated by dots()
        '''
        if resolution is None: resolution = self.resolution

        tick = self.unit_multiplier * resolution
        tick += self._sum_dots(tick)
        tick += self._sum_joined()

        if as_int:
            return int(round(tick))
        return tick


class AbstractRest(AbstractTimeUnit):
    pass


class AbstractNote(AbstractTimeUnit, list):
    def __init__(self, pitch=None, velocity=100, channel=0):
        super(AbstractNote, self).__init__()
        self.pitch = pitch
        self.velocity = velocity
        self.channel = channel


    def __iter__(self):
        '''
        We want to wait till the last moment possible to 
        calculate the note on and off events

        this gives us a chance to add in dots
        '''

        len_of_note = self.get_tick(self.resolution)

        if hasattr(self.pitch, 'asc'):
            # turn notes on
            for offset in self.pitch.asc:
                self.append(
                    #midi.NoteOnEvent(tick=0, velocity=self.velocity, pitch=self.pitch._root + offset)
                    mido.Message('note_on', channel=self.channel, time=0, velocity=self.velocity, note=self.pitch._root + offset)
                )

            # turn notes off
            for offset in self.pitch.asc:
                self.append(
                    #midi.NoteOffEvent(tick=len_of_note, pitch=self.pitch._root + offset)
                    mido.Message('note_off', channel=self.channel, time=len_of_note, note=self.pitch._root + offset)
                )
                # we want to turn off all of the remaining notes right away
                len_of_note = 0

        else:
            # turn notes on
            self.append(
                #midi.NoteOnEvent(tick=0, velocity=self.velocity, pitch=self.pitch)
                mido.Message('note_on', channel=self.channel, time=0, velocity=self.velocity, note=self.pitch)
            )

            # turn notes off
            self.append(
                #midi.NoteOffEvent(tick=len_of_note, pitch=self.pitch),
                mido.Message('note_off', channel=self.channel, time=len_of_note, note=self.pitch)
            )

        return super(AbstractNote, self).__iter__()


class WholeNote(AbstractNote):
    unit_multiplier = 4.0


class HalfNote(AbstractNote):
    unit_multiplier = 2.0


class QuarterNote(AbstractNote):
    unit_multiplier = 1.0


class QuarterNoteTriplet(AbstractNote):
    unit_multiplier = 1.0 / 3


class EigthNote(AbstractNote):
    unit_multiplier = 1.0 / (2 ** 1)


class EigthNoteTriplet(AbstractNote):
    unit_multiplier = 1.0 / ((2 ** 1) * 3)


class SixteenthNote(AbstractNote):
    unit_multiplier = 1.0 / (2 ** 2)


class SixteenthNoteTriplet(AbstractNote):
    unit_multiplier = 1.0 / ((2 ** 2) * 3)


class ThirtySecondNote(AbstractNote):
    unit_multiplier = 1.0 / (2 ** 3)


class ThirtySecondNoteTriplet(AbstractNote):
    unit_multiplier = 1.0 / ((2 ** 3) * 3)


class SixtyForthNote(AbstractNote):
    unit_multiplier = 1.0 / (2 ** 4)


class SixtyForthNoteTriplet(AbstractNote):
    unit_multiplier = 1.0 / ((2 ** 4) * 3)


class OneHundredTwentyEightNote(AbstractNote):
    unit_multiplier = 1.0 / (2 ** 5)


class OneHundredTwentyEightNoteTriplet(AbstractNote):
    unit_multiplier = 1.0 / ((2 ** 5) * 3)


class TwoHundredFiftyForthNote(AbstractNote):
    unit_multiplier = 1.0 / (2 ** 6)


class TwoHundredFiftyForthNoteTriplet(AbstractNote):
    unit_multiplier = 1.0 / ((2 ** 6) * 3)


# Rests
class WholeRest(AbstractRest):
    unit_multiplier = 4.0


class HalfRest(AbstractRest):
    unit_multiplier = 2.0


class QuarterRest(AbstractRest):
    unit_multiplier = 1.0


class QuarterRestTriplet(AbstractRest):
    unit_multiplier = 1.0 / 3


class EigthRest(AbstractRest):
    unit_multiplier = 1.0 / (2 ** 1)


class EigthRestTriplet(AbstractRest):
    unit_multiplier = 1.0 / ((2 ** 1) * 3)


class SixteenthRest(AbstractRest):
    unit_multiplier = 1.0 / (2 ** 2)


class SixteenthRestTriplet(AbstractRest):
    unit_multiplier = 1.0 / ((2 ** 2) * 3)


class ThirtySecondRest(AbstractRest):
    unit_multiplier = 1.0 / (2 ** 3)


class ThirtySecondRestTriplet(AbstractRest):
    unit_multiplier = 1.0 / ((2 ** 3) * 3)


class SixtyForthRest(AbstractRest):
    unit_multiplier = 1.0 / (2 ** 4)


class SixtyForthRestTriplet(AbstractRest):
    unit_multiplier = 1.0 / ((2 ** 4) * 3)


class OneHundredTwentyEightRest(AbstractRest):
    unit_multiplier = 1.0 / (2 ** 5)


class OneHundredTwentyEightRestTriplet(AbstractRest):
    unit_multiplier = 1.0 / ((2 ** 5) * 3)


class TwoHundredFiftyForthRest(AbstractRest):
    unit_multiplier = 1.0 / (2 ** 6)


class TwoHundredFiftyForthRestTriplet(AbstractRest):
    unit_multiplier = 1.0 / ((2 ** 6) * 3)


class NewRhythmResolution(object):
    def __init__(self, resolution=BASE_RESOLUTION):
        class BaseNote(AbstractNote):
            def __init__(selfie, pitch=None, velocity=100):
                super(BaseNote, selfie).__init__(pitch=pitch, velocity=velocity)
                selfie.resolution = resolution
        self.BaseNote = BaseNote

        class BaseRest(AbstractRest):
            def __init__(selfie):
                super(BaseRest, selfie).__init__()
                selfie.resolution = resolution
        self.BaseRest = BaseRest

        class WholeNoteNRR(BaseNote, WholeNote):
            pass
        self.WholeNote = WholeNoteNRR

        class HalfNoteNRR(BaseNote, HalfNote):
            pass
        self.HalfNote = HalfNoteNRR

        class QuarterNoteNRR(BaseNote, QuarterNote):
            pass
        self.QuarterNote = QuarterNoteNRR

        class QuarterNoteTripletNRR(BaseNote, QuarterNoteTriplet):
            pass
        self.QuarterNoteTriplet = QuarterNoteTripletNRR

        class EigthNoteNRR(BaseNote, EigthNote):
            pass
        self.EigthNote = EigthNoteNRR

        class EigthNoteTripletNRR(BaseNote, EigthNoteTriplet):
            pass
        self.EigthNoteTriplet = EigthNoteTripletNRR

        class SixteenthNoteNRR(BaseNote, SixteenthNote):
            pass
        self.SixteenthNote = SixteenthNoteNRR

        class SixteenthNoteTripletNRR(BaseNote, SixteenthNoteTriplet):
            pass
        self.SixteenthNoteTriplet = SixteenthNoteTripletNRR

        class ThirtySecondNoteNRR(BaseNote, ThirtySecondNote):
            pass
        self.ThirtySecondNote = ThirtySecondNoteNRR

        class ThirtySecondNoteTripletNRR(BaseNote, ThirtySecondNoteTriplet):
            pass
        self.ThirtySecondNoteTriplet = ThirtySecondNoteTripletNRR

        class SixtyForthNoteNRR(BaseNote, SixtyForthNote):
            pass
        self.SixtyForthNote = SixtyForthNoteNRR

        class SixtyForthNoteTripletNRR(BaseNote, SixtyForthNoteTriplet):
            pass
        self.SixtyForthNoteTriplet = SixtyForthNoteTripletNRR

        class OneHundredTwentyEightNoteNRR(BaseNote, OneHundredTwentyEightNote):
            pass
        self.OneHundredTwentyEightNote = OneHundredTwentyEightNoteNRR

        class OneHundredTwentyEightNoteTripletNRR(BaseNote, OneHundredTwentyEightNoteTriplet):
            pass
        self.OneHundredTwentyEightNoteTriplet = OneHundredTwentyEightNoteTripletNRR

        class TwoHundredFiftyForthNoteNRR(BaseNote, TwoHundredFiftyForthNote):
            pass
        self.TwoHundredFiftyForthNote = TwoHundredFiftyForthNoteNRR

        class TwoHundredFiftyForthNoteTripletNRR(BaseNote, TwoHundredFiftyForthNoteTriplet):
            pass
        self.TwoHundredFiftyForthNoteTriplet = TwoHundredFiftyForthNoteTripletNRR

        # Rests
        class WholeRestNRR(BaseRest, WholeRest):
            pass
        self.WholeRest = WholeRestNRR

        class HalfRestNRR(BaseRest, HalfRest):
            pass
        self.HalfRest = HalfRestNRR

        class QuarterRestNRR(BaseRest, QuarterRest):
            pass
        self.QuarterRest = QuarterRestNRR

        class QuarterRestTripletNRR(BaseRest, QuarterRestTriplet):
            pass
        self.QuarterRestTriplet = QuarterRestTripletNRR

        class EigthRestNRR(BaseRest, EigthRest):
            pass
        self.EigthRest = EigthRestNRR

        class EigthRestTripletNRR(BaseRest, EigthRestTriplet):
            pass
        self.EigthRestTriplet = EigthRestTripletNRR

        class SixteenthRestNRR(BaseRest, SixteenthRest):
            pass
        self.SixteenthRest = SixteenthRestNRR

        class SixteenthRestTripletNRR(BaseRest, SixteenthRestTriplet):
            pass
        self.SixteenthRestTriplet = SixteenthRestTripletNRR

        class ThirtySecondRestNRR(BaseRest, ThirtySecondRest):
            pass
        self.ThirtySecondRest = ThirtySecondRestNRR

        class ThirtySecondRestTripletNRR(BaseRest, ThirtySecondRestTriplet):
            pass
        self.ThirtySecondRestTriplet = ThirtySecondRestTripletNRR

        class SixtyForthRestNRR(BaseRest, SixtyForthRest):
            pass
        self.SixtyForthRest = SixtyForthRestNRR

        class SixtyForthRestTripletNRR(BaseRest, SixtyForthRestTriplet):
            pass
        self.SixtyForthRestTriplet = SixtyForthRestTripletNRR

        class OneHundredTwentyEightRestNRR(BaseRest, OneHundredTwentyEightRest):
            pass
        self.OneHundredTwentyEightRest = OneHundredTwentyEightRestNRR

        class OneHundredTwentyEightRestTripletNRR(BaseRest, OneHundredTwentyEightRestTriplet):
            pass
        self.OneHundredTwentyEightRestTriplet = OneHundredTwentyEightRestTripletNRR

        class TwoHundredFiftyForthRestNRR(BaseRest, TwoHundredFiftyForthRest):
            pass
        self.TwoHundredFiftyForthRest = TwoHundredFiftyForthRestNRR

        class TwoHundredFiftyForthRestTripletNRR(BaseRest, TwoHundredFiftyForthRestTriplet):
            pass
        self.TwoHundredFiftyForthRestTriplet = TwoHundredFiftyForthRestTripletNRR
