import numpy
from music21 import note, instrument, chord, stream
from model_training import ModelTraining


class MusicGeneration(ModelTraining):
    def __init__(self, distinct_notes_chords, integer_mapped_inputs, notes_to_generate, x_train, x_test, y_train, y_test):
        super().__init__(x_train, x_test, y_train, y_test)
        self.int_to_note = dict((number, note_itr) for number, note_itr in enumerate(distinct_notes_chords))
        self.notes_to_generate = notes_to_generate
        self.integer_mapped_inputs = integer_mapped_inputs
        self.random_sequence_index = numpy.random.randint(0, len(x_test) - 1)
        self.random_pattern = x_train[self.random_sequence_index]
        self.prediction_output = []

    def generate_new_notes(self, model):
        for note_itr in range(self.notes_to_generate):
            prediction_input = numpy.reshape(self.random_pattern, (1, len(self.random_pattern), 1))
            prediction_input = prediction_input / float(len(self.integer_mapped_inputs))
            prediction = model.predict(prediction_input)

            index = numpy.argmax(prediction)
            result = self.int_to_note[index]
            self.prediction_output.append(result)
            self.random_pattern = numpy.append(self.random_pattern, prediction[0][index])
            self.random_pattern = self.random_pattern[1:len(self.random_pattern)]

    def decoder(self):
        offset = 0
        output_notes = []
        # create note and chord objects based on the values generated by the model
        for pattern in self.prediction_output:
            # pattern is a chord
            if (',' in pattern) or pattern.isdigit():
                notes_in_chord = pattern.split(',')
                notes = []
                for current_note in notes_in_chord:
                    new_note = note.Note(current_note)
                    new_note.storedInstrument = instrument.Piano()
                    notes.append(new_note)
                new_chord = chord.Chord(notes)
                new_chord.offset = offset
                output_notes.append(new_chord)
            # pattern is a note
            else:
                new_note = note.Note(pattern)
                new_note.offset = offset
                new_note.storedInstrument = instrument.Piano()
                output_notes.append(new_note)
            # increase offset each iteration so that notes do not stack
            offset += 0.5

        return output_notes

    @staticmethod
    def create_output_file(melody_notes):
        midi_stream = stream.Stream(melody_notes)
        midi_stream.write('midi', fp='test_output.mid')

import numpy
from music21 import note, instrument, chord, stream
from model_training import ModelTraining


class MusicGeneration(ModelTraining):
    def __init__(self, distinct_notes_chords, integer_mapped_inputs, notes_to_generate, x_train, x_test, y_train, y_test):
        super().__init__(x_train, x_test, y_train, y_test)
        self.int_to_note = dict((number, note_itr) for number, note_itr in enumerate(distinct_notes_chords))
        self.notes_to_generate = notes_to_generate
        self.integer_mapped_inputs = integer_mapped_inputs
        self.random_sequence_index = numpy.random.randint(0, len(x_test) - 1)
        self.random_pattern = x_train[self.random_sequence_index]
        self.prediction_output = []

    def generate_new_notes(self, model):
        for note_itr in range(self.notes_to_generate):
            prediction_input = numpy.reshape(self.random_pattern, (1, len(self.random_pattern), 1))
            prediction_input = prediction_input / float(len(self.integer_mapped_inputs))
            prediction = model.predict(prediction_input)

            index = numpy.argmax(prediction)
            result = self.int_to_note[index]
            self.prediction_output.append(result)
            self.random_pattern = numpy.append(self.random_pattern, prediction[0][index])
            self.random_pattern = self.random_pattern[1:len(self.random_pattern)]

    def decoder(self):
        offset = 0
        output_notes = []
        # create note and chord objects based on the values generated by the model
        for pattern in self.prediction_output:
            # pattern is a chord
            if (',' in pattern) or pattern.isdigit():
                notes_in_chord = pattern.split(',')
                notes = []
                for current_note in notes_in_chord:
                    new_note = note.Note(current_note)
                    new_note.storedInstrument = instrument.Piano()
                    notes.append(new_note)
                new_chord = chord.Chord(notes)
                new_chord.offset = offset
                output_notes.append(new_chord)
            # pattern is a note
            else:
                new_note = note.Note(pattern)
                new_note.offset = offset
                new_note.storedInstrument = instrument.Piano()
                output_notes.append(new_note)
            # increase offset each iteration so that notes do not stack
            offset += 0.5

        return output_notes

    @staticmethod
    def create_output_file(melody_notes):
        midi_stream = stream.Stream(melody_notes)
        midi_stream.write('midi', fp='test_output.mid')

# create note and chord objects based on the values generated by the model # pattern is a chord # pattern is a note # increase offset each iteration so that notes do not stack