
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Reader;

import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MetaMessage;
import javax.sound.midi.MidiEvent;
import javax.sound.midi.MidiMessage;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Receiver;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
import javax.sound.midi.ShortMessage;
import javax.sound.midi.Synthesizer;
import javax.sound.midi.Track;
import javax.sound.midi.Transmitter;

import org.apache.regexp.RE;

import sun.rmi.runtime.GetThreadPoolAction;

import kr.util.Util;

public class JavaxMidiEditor {
	//line '      0: 0xff 0x03 0x08 firstcut'
	RE midiMessageData = new RE("^([0-9]+):(( 0x[0-9a-f][0-9a-f])+) ?(.*)$"); //^(0-9)+: (0x[0-9a-f][0-9a-f] )+(.*)$
	

	public static void main(String[] args) throws MidiUnavailableException,
			InvalidMidiDataException, IOException {
		JavaxMidiEditor e = new JavaxMidiEditor();
		
		if(args[0].equals("-w"))
		{
			e.write(new FileReader(args[1]), new FileOutputStream(args[2]));
		}
		else if(args[0].equals("-r"))
		{
			String [] files = new String [args.length-1];
			System.arraycopy(args, 1, files, 0, files.length);
			e.read(files);
		}
	}
	
	private void write(Reader reader, OutputStream stream) throws IOException, InvalidMidiDataException {
		BufferedReader br = new BufferedReader(reader);
		
		Sequence seq = new Sequence(Sequence.PPQ, 480);
		
		Track currTrack = null;
		
		String line;
		while((line = br.readLine()) != null)
		{
			line = line.trim();
			
			if(line.startsWith("File:"))
				continue;
			else if(line.startsWith("Track "))
			{
				currTrack = seq.createTrack();
			}
			else if(midiMessageData.match(line))
			{
				long ticks = Long.parseLong(midiMessageData.getParen(1));
				byte [] hexData = Util.parseHex(midiMessageData.getParen(2));
				byte [] textData = midiMessageData.getParen(4).getBytes();

				MidiMessage m;
				
				
				if(hexData[0] == -1)
				{
					if(hexData[1] == 0x2f)
						continue; //end of track (inserted automatically)
					
/*					byte [] msgData = new byte[2 + textData.length];
					System.arraycopy(hexData, 2, msgData, 0, 2);
					System.arraycopy(textData, 0, msgData, 2, textData.length);*/
					m = new MetaMessage();
					if(textData.length > 0)
						((MetaMessage)m).setMessage(hexData[1], textData, textData.length);
					else
					{
						byte [] msgData = new byte[hexData.length-3];
						System.arraycopy(hexData, 3, msgData, 0, msgData.length);
						((MetaMessage)m).setMessage(hexData[1], msgData, msgData.length);
					}
				}
				else
				{
					m = new ShortMessage();
					if(hexData.length == 2)
						((ShortMessage)m).setMessage(hexData[1]);
					else if(hexData.length == 3)
						((ShortMessage)m).setMessage(hexData[0] & 0xF0, hexData[0] & 0x0F, hexData[1], hexData[2]);
					else if(hexData.length == 4)
						((ShortMessage)m).setMessage(hexData[1], hexData[2], hexData[3]);
					else
						System.err.println("invalid number of bytes for line, skipping: "+line);
				}
							
				MidiEvent e = new MidiEvent(m, ticks);
				currTrack.add(e);
			}
			else
				System.err.println("can't understand line: "+line);
		}
		
		MidiSystem.write(seq, 1, stream);
		reader.close();
		stream.close();
	}

	/**
	 * Reads and displays data
	 * @throws IOException 
	 * @throws InvalidMidiDataException 
	 */
	public void read(String [] args) throws InvalidMidiDataException, IOException
	{
		Sequence sequence = null;

		byte minNote = 127;
		byte maxNote = 0;
		byte maxDiff = 0;

		for (String fileName : args) {
			File file = new File(fileName);
			System.out.println("File: " + file);

			sequence = MidiSystem.getSequence(file);
			
			System.out.println("Timing Resolution: "+sequence.getResolution());
			

			byte songMinNote = 127;
			byte songMaxNote = 0;

			for (int i = 0; i < sequence.getTracks().length; i++) {
				System.out.println("Track " + i);

				Track t = sequence.getTracks()[i];

				for (int j = 0; j < t.size(); j++) {
					//hack
					//if(i==2)
//						System.out.print("t2 ");
					
					MidiEvent e = t.get(j);

					byte[] dat = e.getMessage().getMessage();

					System.out.print(String.format("%7d: ", e.getTick()));

					// if text
					if (dat[0] == -1) {
						if(dat[1] == 0x51 ||  //tempo info
								dat[1] == 0x58 //time signature
								)
						{
							System.out.println(Util.toHex(dat));
						}
						else
							System.out.println(Util.toHex(dat, 0, 3) + " "
								+ new String(dat, 3, dat.length - 3));
					} else {
						if ((dat[0] & 0xF0) == 0x90 || (dat[0] & 0xF0) == 0x80) {
							byte note = dat[1];
							if (note > maxNote)
								maxNote = note;
							if (note < minNote)
								minNote = note;
							if (note > songMaxNote)
								songMaxNote = note;
							if (note < songMinNote)
								songMinNote = note;
							System.out.println(Util.toHex(dat));
						}
						else
							System.out.println(Util.toHex(dat));
					}
				} //read all events from a track
			} //read all tracks from a file
			
			byte diff = (byte)(songMaxNote - songMinNote);
			if(diff > maxDiff)
				maxDiff = diff;
		} //read all files
		/*
		 * Sequencer sequencer = MidiSystem.getSequencer();
		 * sequencer.setSequence(sequence); sequencer.open();
		 */

		/*
		 * if (!(sequencer instanceof Synthesizer)) { Synthesizer synthesizer =
		 * MidiSystem.getSynthesizer(); synthesizer.open(); Receiver receiver =
		 * synthesizer.getReceiver(); Transmitter transmitter =
		 * sequencer.getTransmitter(); transmitter.setReceiver(receiver); }
		 */
		// sequencer.start();

		System.out.println("Min note is "+minNote);
		System.out.println("Max note is "+maxNote);
		System.out.println("Max diff is "+maxDiff);
	} 
	
	

}
