package ch.usi.inf.sp.dbi.profiler;

import javax.sound.midi.*;
import javax.swing.*;

public class Profiler {

    private static final DefaultListModel<String> model = new DefaultListModel<>();
    private static int size = 0;

    static {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("Profiler");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            model.add(0, "hello");
            JList<String> list = new JList<>(model);
            JScrollPane p = new JScrollPane(list);
            frame.add(p);
            frame.pack();
            frame.setVisible(true);
        });

    }

    private static MidiEvent createProgramChangeEvent(int channel, int program) throws InvalidMidiDataException {
        ShortMessage message = new ShortMessage();
        message.setMessage(ShortMessage.PROGRAM_CHANGE, channel, program, 0);
        return new MidiEvent(message, 0);
    }

    public static void play() throws InterruptedException {
        final Thread midi = new Thread(() -> {
            try {
                Sequencer player = MidiSystem.getSequencer();
                player.open();

                Sequence seq = new Sequence(Sequence.PPQ, 4);

                Track track = seq.createTrack();

                int channel = 1;

                int pitch = size * 2;
                pitch = 20 + pitch % 118;

                track.add(createProgramChangeEvent(channel, 54));

                ShortMessage a = new ShortMessage();
                a.setMessage(ShortMessage.NOTE_ON, channel, pitch, 100);
                MidiEvent noteOn = new MidiEvent(a, 0);
                track.add(noteOn);

                ShortMessage b = new ShortMessage();
                b.setMessage(ShortMessage.NOTE_OFF, channel, pitch, 100);
                MidiEvent noteOff = new MidiEvent(b, 2);
                track.add(noteOff);

                player.setSequence(seq);
                player.start();

            } catch (Exception ex) {
                ex.printStackTrace();
            }
        });

        midi.start();
        midi.join();
    }

    public static void entry(String clazz, String method, String desc) {
        size++;
        model.add(0, clazz + "." + method + desc);
        log(clazz, method, desc, "entry");
    }

    public static void exit(String clazz, String method, String desc) {
        size--;
        model.remove(0);
        log(clazz, method, desc, "exit");
    }

    private static void log(String clazz, String method, String desc, String type) {
        try {
            System.err.println("Profiler: " + type + " " + clazz + "." + method + desc);
            play();

            try {
                Thread.sleep(50);
            } catch (InterruptedException ignored) {
            }
        } catch (Exception e) {
            e.printStackTrace(System.err);
        }
    }
}
