package com.javadaw.midi;

import com.javadaw.data.clipData;
import com.javadaw.data.noteData;
import com.javadaw.data.rulerData;
import com.javadaw.gui.*;

import java.io.File;
import javax.sound.midi.ControllerEventListener;
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MetaMessage;
import javax.sound.midi.MidiEvent;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.Sequence;
import javax.sound.midi.ShortMessage;
import javax.sound.midi.Track;

public class midiEngine {
  
  private javaMain parent;
  private handleControlEvents hce;
  private Thread thread;

  
  public midiEngine(javaMain parent) {
       this.parent = parent;
  }
  public void createEvent(int type, Track track, int chan, int num, long tick, int velocity) { 
       ShortMessage message = new ShortMessage(); 
       try { 
             message.setMessage(type, chan, num, velocity);
             MidiEvent event = new MidiEvent( message, tick ); 
             track.add(event); 
       } catch (Exception ex) { ex.printStackTrace(); } 
  }
  public  void createControlEvent(Track track, int chan, int data1, int data2, long tick) { 
       ShortMessage message = new ShortMessage( ); 
       try {
             message.setMessage(ShortMessage.CONTROL_CHANGE, chan, data1, data2);
             MidiEvent event = new MidiEvent( message, tick ); 
             track.add(event); 
       } catch (Exception ex) { ex.printStackTrace(); } 
  } 
  public void addEvent(Track track, long tick, int tempo) { 

       final int TEMPO = 0x51; 
       
       int tempoInMPQ = tempo;
       byte[] data = new byte[3]; 
       data[0] = (byte)((tempoInMPQ >> 16) & 0xFF); 
       data[1] = (byte)((tempoInMPQ >> 8)  & 0xFF); 
       data[2] = (byte) (tempoInMPQ & 0xFF); 
       MetaMessage message = new MetaMessage(); 
       try { 
             message.setMessage(TEMPO, data, data.length); 
             MidiEvent event = new MidiEvent( message, tick); 
             track.add(event); 
       } catch (InvalidMidiDataException e) { e.printStackTrace(); 
       } 
  }
  public void stopMIDI() {
      parent.seqr.stop();
  }
  public void startMIDI() {
      parent.seqr.start();
  }
  public void setAllVolumes() {
      for (int channel = 0; channel <= this.parent.max_channels; ++channel) {
           float masterVolume = 1.0F * parent.mvolume[0] / 127.0F;
           try { if (parent.audiotype.equals("asio")) parent.as.setVolume(channel, masterVolume * parent.svolume[channel] / 127.0F); 
                 else                                 parent.au.setVolume(channel, masterVolume * parent.svolume[channel] / 127.0F); 
           } catch (Throwable t) { } 
      }
  }
  public void setLoop() {

      rulerData r = (rulerData) parent.rulerdata.get(0);

      parent.seqr.setLoopEndPoint(parent.endOfTrack);
      parent.seqr.setLoopStartPoint(r.x_loop_clip_start);
      if (parent.loop) {
          parent.seqr.setLoopEndPoint(r.x_loop_clip_end);
          parent.seqr.setLoopCount(100);
      } else { parent.seqr.setLoopEndPoint(parent.endOfTrack);
               parent.seqr.setLoopCount(0);
      }
      parent.seqr.setTickPosition(r.x_loop_clip_start);
  }
  public void initSeq(int endoftrack) {
        
      try {  int ppq = 100;
             
             parent.seq = new Sequence(Sequence.PPQ, ppq);
               
             for (int i = 0; i <= parent.max_channels; i++) {
                  parent.tracks[i] = parent.seq.createTrack();
             }
               
             if (parent.max_channels == -1) parent.tracks[0] = parent.seq.createTrack();
               
             parent.midiengine.addEvent(parent.tracks[0], endoftrack,  600000);
             
             parent.seqr.setSequence(parent.seq);
            
      } catch (Throwable t) { System.out.println("Error in setloop: " + t); }

  }
  public void initMIDI() {

      try {   parent.seqr = MidiSystem.getSequencer(false);
              parent.seqr.open();
              int[] anControllers = new int[128];
              for (int i = 0; i < anControllers.length; i++) {
                   anControllers[i] = i;
              }
              hce = new handleControlEvents(parent);
              parent.seqr.addControllerEventListener(new ControllerEventListener() { 
                                                         public void controlChange(ShortMessage msg) {
                                                             hce.setMessage(msg);
                                                         }
              }, anControllers);
              parent.tempoInBPM = (int)(parent.seqr.getTempoInBPM());
      } catch (Throwable t) {}     
  }
  public void saveTrackMidiFile(File file) { 
      
       initSeq(parent.selectOf);
      
       try {  for (int id = 0; id <= parent.max_noteid; id++) { 
                   noteData n = (noteData) parent.notedata.get(id);
                   if (n.isVisible1 == true && n.channel == parent.b_channel_selected) { 
                       if (file != null) if (n.pitch  >  2) createEvent(144,   parent.tracks[n.channel] , n.channel, n.pitch , n.xhandleOn        , n.velocity        );
                       if (file != null) if (n.pitch  >  2) createEvent(128,   parent.tracks[n.channel] , n.channel, n.pitch , n.xhandleOn + n.wa , 0                 ); 
                       if (file != null) if (n.pitch  == 0) createControlEvent(parent.tracks[0]         , n.channel, 64      , n.sustain          , n.xhandleOn       );
                       if (file != null) if (n.pitch  == 1) createControlEvent(parent.tracks[0]         , n.channel, 1       , n.modulation       , n.xhandleOn       );
                   }
              }
         
              try { if (file != null) MidiSystem.write(parent.seq, 1, file); 
              } catch (Throwable t) { System.out.println("Error in midifile write: " + t); }
           
     } catch (Throwable t) { System.out.println("Error in loadmidi: " + t); }
  }
  public void saveClipMidiFile(File file) { 
        
       initSeq(parent.selectOf);
        
       try {  for (int id = 0; id <= parent.max_noteid; id++) { 
                   noteData n = (noteData) parent.notedata.get(id);
                   if (n.isVisible1 == true && n.clipid == parent.cc.clipid) { 
                       if (file != null) if (n.pitch  >  2) createEvent(144,   parent.tracks[n.channel] , n.channel, n.pitch , n.xhandleOn        , n.velocity        );
                       if (file != null) if (n.pitch  >  2) createEvent(128,   parent.tracks[n.channel] , n.channel, n.pitch , n.xhandleOn + n.wa , 0                 ); 
                       if (file != null) if (n.pitch  == 0) createControlEvent(parent.tracks[0]         , n.channel, 64      , n.sustain          , n.xhandleOn       );
                       if (file != null) if (n.pitch  == 1) createControlEvent(parent.tracks[0]         , n.channel, 1       , n.modulation       , n.xhandleOn       );
                   }
              }
           
              try { if (file != null) MidiSystem.write(parent.seq, 1, file); 
              } catch (Throwable t) { System.out.println("Error in midifile write: " + t); }
             
     } catch (Throwable t) { System.out.println("Error in loadmidi: " + t); }
  }
  public void loadMidi() { 

      try {  rulerData r = (rulerData) parent.rulerdata.get(0);
        
             for (int x = 0; x < parent.midi_size; x = x + parent.clock/parent.clockresolution_clip) {
                  if (r.x_pos_1 [x] == 1) createControlEvent(parent.tracks[0], 0, 1, 0, x);
             }
             for (int id = 0; id <= parent.max_noteid; id++) { 
                  noteData n = (noteData) parent.notedata.get(id);
                  if ((n.isVisible1 == true)) { 
                       if (n.pitch  == 0) createControlEvent(parent.tracks[0]         , n.channel, 0       , n.sustain          , n.xhandleOn       );
                       if (n.pitch  == 1) createControlEvent(parent.tracks[0]         , n.channel, 1       , n.modulation       , n.xhandleOn       );
                                          createControlEvent(parent.tracks[0]         , n.channel, n.pitch , n.velocity         , n.xhandleOn       );
                                          createControlEvent(parent.tracks[0]         , n.channel, n.pitch , 0                  , n.xhandleOn + n.wa);
                  }
             }
      } catch (Throwable t) { System.out.println("Error in loadmidi: " + t); }
  }
}
