import itertools
from midi_to_statematrix import UPPER_BOUND, LOWER_BOUND


def startSentinel():
    def noteSentinel(note):
        position = note
        part_position = [position]

        pitchclass = (note + LOWER_BOUND) % 12
        part_pitchclass = [int(i == pitchclass) for i in range(12)]

        return part_position + part_pitchclass + [0] * 66 + [1]

    return [noteSentinel(note) for note in range(UPPER_BOUND - LOWER_BOUND)]


def getOrDefault(l, i, d):
    try:
        return l[i]
    except IndexError:
        return d


def buildContext(state):
    context = [0] * 12
    for note, notestate in enumerate(state):
        if notestate[0] == 1:
            pitchclass = (note + LOWER_BOUND) % 12
            context[pitchclass] += 1
    return context


def buildBeat(time):
    return [
        2 * x - 1 for x in [time % 2, (time // 2) % 2, (time // 4) % 2, (time // 8) % 2]
    ]


def noteInputForm(note, state, context, beat):
    position = note
    part_position = [position]

    pitchclass = (note + LOWER_BOUND) % 12
    part_pitchclass = [int(i == pitchclass) for i in range(12)]
    # Concatenate the note states for the previous vicinity
    part_prev_vicinity = list(
        itertools.chain.from_iterable(
            (getOrDefault(state, note + i, [0, 0]) for i in range(-12, 13))
        )
    )

    part_context = context[pitchclass:] + context[:pitchclass]

    return (
        part_position + part_pitchclass + part_prev_vicinity + part_context + beat + [0]
    )


def noteStateSingleToInputForm(state, time):
    beat = buildBeat(time)
    context = buildContext(state)
    return [noteInputForm(note, state, context, beat) for note in range(len(state))]


def noteStateMatrixToInputForm(statematrix):
    # NOTE: May have to transpose this or transform it in some way to make Theano like it
    # [startSentinel()] +

    inputform = [
        noteStateSingleToInputForm(state, time)
        for time, state in enumerate(statematrix)
    ]
    return inputform

[]