package jp.crestmuse.cmx;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.util.List;

import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Sequencer;

import jp.crestmuse.cmx.misc.*;
import jp.crestmuse.cmx.amusaj.filewrappers.*;
import jp.crestmuse.cmx.amusaj.sp.*;
import jp.crestmuse.cmx.inference.AccompanimentCalculator;
import jp.crestmuse.cmx.inference.NaiveBassCalculator;
import jp.crestmuse.cmx.inference.MusicRepresentation;
import jp.crestmuse.cmx.inference.AccompanimentGenerator;
import jp.crestmuse.cmx.inference.MusicRepresentationModule;
import jp.crestmuse.cmx.inference.NaiveVoicingCalculator;
import jp.crestmuse.cmx.inference.MusicRepresentation.MusicElement;
import jp.crestmuse.cmx.inference.MusicRepresentation.Type;
import jp.crestmuse.cmx.sound.SequencerManager;

public class AutoAccompanimentSystem {

    static MusicRepresentation mr ;

  public static void main(String[] args) {
    try {

	if (args.length < 2) {
	    System.err.println("Usage: java -jar BayesianBand.jar <model file> <midi file> <options>");
	    System.exit(1);
	}

	int length = 8;
	String modelFile = args[0];
	String chordMIDIFile = args[1];
	String firstChord = "C";
	String inputSMF = null;
	boolean writeLog = false;
	for (int i = 2; i < args.length; i++) {
	    if (args[i].equals("-length")) {
		length = Integer.parseInt(args[i+1]);
		i++;
	    } else if (args[i].equals("-start")) {
		firstChord = args[i+1];
		i++;
	    } else if (args[i].equals("-smf")) {
		inputSMF = args[i+1];
		i++;
	    } else if (args[i].equals("-v")) {
		writeLog = true;
	    }
	}

      // MusicRepresentation周り
      mr = new MusicRepresentation(length, 8);
      mr.setTiedChordLength(8);
      mr.setTiedVoicingLength(8);
      mr.setTiedBassLength(2);
      AccompanimentCalculator ac = new AccompanimentCalculator(mr, new BayesNetWrapper(modelFile));
      //NaiveBassCalculator bc = new NaiveBassCalculator(mr);
      NaiveVoicingCalculator vc = new NaiveVoicingCalculator(mr);
      mr.addCalculator(ac);
      //mr.addCalculator(bc);
      mr.addCalculator(vc);
      MusicElement chord = mr.addChordElement(0);
      chord.setProb(chord.indexOf(firstChord), 1.0);
      mr.update(Type.Chord, chord, 0);
      //mr.setPredict(1, 0, new ChordElement("C"));

      // SP周り
      MidiInputModule mi;
      Sequencer seqr = null;
      if(inputSMF != null){
        seqr = MidiSystem.getSequencer(false);
        seqr.setSequence(MidiSystem.getSequence(new File(inputSMF)));
        mi = new MidiInputModule(seqr);
      }else
        mi = new MidiInputModule(getMidiDevice(true));
      SPExecutor sp = new SPExecutor(null, 1);
      //MidiInputModule mi = new MidiInputModule(MidiSystem.getMidiDevice(MidiSystem.getMidiDeviceInfo()[0]));
      MusicRepresentationModule mrm = new MusicRepresentationModule(mr, writeLog);
      MidiDevice device = getMidiDevice(false);
      device.open();
      MidiOutputModule mo = new MidiOutputModule(device.getReceiver());

      // SequencerManager周り
      SequencerManager sm = new SequencerManager(device.getReceiver());
      if(inputSMF != null)
        seqr.setMasterSyncMode(sm.getSequencer().getMasterSyncMode());
      AccompanimentGenerator mrg = new AccompanimentGenerator(mr, chordMIDIFile);
      sm.addGeneratable(mrg);

      //MusicRepresentationViewer mrv = new MusicRepresentationViewer();

      mi.setTickTimer(sm);
      sp.addSPModule(mi);
      sp.addSPModule(mo);
      sp.addSPModule(mrm);
      //sp.addSPModule(mrv);
      sp.connect(mi, 0, mo, 0);
      sp.connect(mi, 0, mrm, 0);
      //sp.connect(mrm, 0, mrv, 0);


      // 起動
      sm.start();
      if(inputSMF != null) seqr.start();
      sp.start();

      // 終了
      System.err.println("press button to exit...");
      System.in.read();
      sp.stop();
      device.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
    System.exit(0);
  }

  public static MidiDevice getMidiDevice(boolean trans) {
    MidiDevice.Info[] info = MidiSystem.getMidiDeviceInfo();
    MidiDevice device = null;

    for (int i = 0; i < info.length; i++) {
      try {
        device = MidiSystem.getMidiDevice(info[i]);
        if(trans && device.getMaxTransmitters() == 0) continue;
        if(!trans && device.getMaxReceivers() == 0) continue;
        System.err.println("*** " + i + " ***");
        System.err.println("  Description:" + info[i].getDescription());
        System.err.println("  Name:" + info[i].getName());
        System.err.println("  Vendor:" + info[i].getVendor());
        System.err.println();
      } catch (MidiUnavailableException e) {
        e.printStackTrace();
      }
    }

    try {
      BufferedReader r = new BufferedReader(new InputStreamReader(System.in), 1);
      if(trans) System.err.print("Using Input Device Number: ");
      else System.err.print("Using Output Device Number: ");
      String s = r.readLine();
      device = MidiSystem.getMidiDevice(info[Integer.parseInt(s)]);
    } catch (Exception e) {
      e.printStackTrace();
    }
    return device;
  }

    /*
    private static class MusicRepresentationViewer extends SPModule<SPDummyObject,SPDummyObject> {
	public void execute(List<QueueReader<SPDummyObject>> src, 
			    List<TimeSeriesCompatible<SPDummyObject>> dest) {
	    
	}

	public int getInputChannels() {
	    return 1;
	}

	public int getOutputChannels() {
	    return 0;
	}

    }
    */

}
