"""These classes simply hold MIDI data in a convenient form.

"""
from __future__ import print_function

from .utilities import key_number_to_key_name


class Note(object):
    """A note event.

    Parameters
    ----------
    velocity : int
        Note velocity.
    pitch : int
        Note pitch, as a MIDI note number.
    start : float
        Note on time, absolute, in seconds.
    end : float
        Note off time, absolute, in seconds.

    """

    def __init__(self, velocity, pitch, start, end):
        self.velocity = velocity
        self.pitch = pitch
        self.start = start
        self.end = end

    def get_duration(self):
        """Get the duration of the note in seconds."""
        return self.end - self.start

    @property
    def duration(self):
        return self.get_duration()

    def __repr__(self):
        return 'Note(start={:f}, end={:f}, pitch={}, velocity={})'.format(
            self.start, self.end, self.pitch, self.velocity)


class PitchBend(object):
    """A pitch bend event.

    Parameters
    ----------
    pitch : int
        MIDI pitch bend amount, in the range ``[-8192, 8191]``.
    time : float
        Time where the pitch bend occurs.

    """

    def __init__(self, pitch, time):
        self.pitch = pitch
        self.time = time

    def __repr__(self):
        return 'PitchBend(pitch={:d}, time={:f})'.format(self.pitch, self.time)


class ControlChange(object):
    """A control change event.

    Parameters
    ----------
    number : int
        The control change number, in ``[0, 127]``.
    value : int
        The value of the control change, in ``[0, 127]``.
    time : float
        Time where the control change occurs.

    """

    def __init__(self, number, value, time):
        self.number = number
        self.value = value
        self.time = time

    def __repr__(self):
        return ('ControlChange(number={:d}, value={:d}, '
                'time={:f})'.format(self.number, self.value, self.time))


class TimeSignature(object):
    """Container for a Time Signature event, which contains the time signature
    numerator, denominator and the event time in seconds.

    Attributes
    ----------
    numerator : int
        Numerator of time signature.
    denominator : int
        Denominator of time signature.
    time : float
        Time of event in seconds.

    Examples
    --------
    Instantiate a TimeSignature object with 6/8 time signature at 3.14 seconds:

    >>> ts = TimeSignature(6, 8, 3.14)
    >>> print(ts)
    6/8 at 3.14 seconds

    """

    def __init__(self, numerator, denominator, time):
        if not (isinstance(numerator, int) and numerator > 0):
            raise ValueError(
                '{} is not a valid `numerator` type or value'.format(
                    numerator))
        if not (isinstance(denominator, int) and denominator > 0):
            raise ValueError(
                '{} is not a valid `denominator` type or value'.format(
                    denominator))
        if not (isinstance(time, (int, float)) and time >= 0):
            raise ValueError(
                '{} is not a valid `time` type or value'.format(time))

        self.numerator = numerator
        self.denominator = denominator
        self.time = time

    def __repr__(self):
        return "TimeSignature(numerator={}, denominator={}, time={})".format(
            self.numerator, self.denominator, self.time)

    def __str__(self):
        return '{}/{} at {:.2f} seconds'.format(
            self.numerator, self.denominator, self.time)


class KeySignature(object):
    """Contains the key signature and the event time in seconds.
    Only supports major and minor keys.

    Attributes
    ----------
    key_number : int
        Key number according to ``[0, 11]`` Major, ``[12, 23]`` minor.
        For example, 0 is C Major, 12 is C minor.
    time : float
        Time of event in seconds.

    Examples
    --------
    Instantiate a C# minor KeySignature object at 3.14 seconds:

    >>> ks = KeySignature(13, 3.14)
    >>> print(ks)
    C# minor at 3.14 seconds
    """

    def __init__(self, key_number, time):
        if not all((isinstance(key_number, int),
                    key_number >= 0,
                    key_number < 24)):
            raise ValueError(
                '{} is not a valid `key_number` type or value'.format(
                    key_number))
        if not (isinstance(time, (int, float)) and time >= 0):
            raise ValueError(
                '{} is not a valid `time` type or value'.format(time))

        self.key_number = key_number
        self.time = time

    def __repr__(self):
        return "KeySignature(key_number={}, time={})".format(
            self.key_number, self.time)

    def __str__(self):
        return '{} at {:.2f} seconds'.format(
            key_number_to_key_name(self.key_number), self.time)


class Lyric(object):
    """Timestamped lyric text.

    Attributes
    ----------
    text : str
        The text of the lyric.
    time : float
        The time in seconds of the lyric.
    """

    def __init__(self, text, time):
        self.text = text
        self.time = time

    def __repr__(self):
        return 'Lyric(text="{}", time={})'.format(
            self.text.replace('"', r'\"'), self.time)

    def __str__(self):
        return '"{}" at {:.2f} seconds'.format(self.text, self.time)
