package nxm.audio.prim;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.TargetDataLine;
import javax.swing.JButton;
import javax.swing.JFrame;

import org.xiph.speex.spi.SpeexAudioFileWriter;
import org.xiph.speex.spi.SpeexEncoding;
import org.xiph.speex.spi.SpeexFormatConvertionProvider;

import nxm.sys.lib.FileName;
import nxm.sys.lib.MidasException;
import nxm.sys.lib.Primitive;

/** SPEEX audio recorder

 @author David Zimmerman

 */
public class sourcespeexaudio extends Primitive implements ActionListener{

  private float rate;
  private int bits=16,frameSize=1,bufsize=8000,channels=2,quality=5;
  private boolean bige=false,signed=true;

  private FileOutputStream speexFile;
  private AudioInputStream audiostr;
  private TargetDataLine audioLine;
  private SpeexAudioFileWriter fileout;
  private JFrame topframe=new JFrame("Speex Recorder");

  /** default constructor */
  public sourcespeexaudio () {}

  public int open() {
    File spxfile;
    rate   = MA.getF("RATE");
    quality= MA.getI("QUALITY");
    try {
// 2010-01-28 NTN: orig code to get output file (only looks under current directory
// OLD     if ((!((MA.getCS("OUT")).endsWith(".spx")))&&(!((MA.getS("OUT")).endsWith(".SPX"))))
// OLD       spxfile = new File(MA.getS("OUT")+".spx");
// OLD     else spxfile=new File(MA.getS("OUT"));
      FileName fn = MA.getFileName("OUT");
      if (fn.getExt().equalsIgnoreCase("spx")) spxfile=new File(fn.getFullName());
      else                                     spxfile=new File(fn.getFullName()+".spx");
      spxfile.createNewFile();
      speexFile=new FileOutputStream(spxfile);
    }
    catch(IOException e) {
      throw new MidasException("Could not get FileOutputStream", e);
    }

    openAudio();

    xfer = MA.getL("/TL", (int)(0.25*rate) );
    xfer = (xfer/frameSize) * frameSize;
    JButton stopbutton=new JButton("Stop");
    stopbutton.addActionListener(this);
    topframe.addWindowListener(new WindowAdapter(){
      public void windowClosing(WindowEvent w) {
        topframe.setVisible(false);
        closeAudio();
      }
    });
    topframe.setBounds(300,300,205,65);
    topframe.setLayout(new BorderLayout());
    topframe.add(stopbutton);
    topframe.setVisible(true);
    return NORMAL;
  }

  public int process () {
    int avail = audioLine.available();
    if (avail<xfer) return NOOP;
    fileout=new SpeexAudioFileWriter();
    try {
      M.info(SpeexAudioFileWriter.SPEEX_FORMAT[0].toString());
      fileout.write(audiostr,SpeexAudioFileWriter.SPEEX_FORMAT[0],speexFile);
    }
    catch(IOException e) {
      throw new MidasException("Error during write", e);
    }
    return FINISH;
  }

  public int close () {
    closeAudio();
    return NORMAL;
  }

  private void openAudio () {
    AudioFormat audioFormat = null;
    SpeexEncoding speexEnc = null;
    SpeexFormatConvertionProvider convert = null;
    try {
      audioFormat = new AudioFormat (rate, bits, channels, signed, bige);

      switch (quality) {
        case 1: speexEnc=SpeexEncoding.SPEEX_Q1; break;
        case 2: speexEnc=SpeexEncoding.SPEEX_Q2; break;
        case 3: speexEnc=SpeexEncoding.SPEEX_Q3; break;
        case 4: speexEnc=SpeexEncoding.SPEEX_Q4; break;
        case 5: speexEnc=SpeexEncoding.SPEEX_Q5; break;
        case 6: speexEnc=SpeexEncoding.SPEEX_Q6; break;
        case 7: speexEnc=SpeexEncoding.SPEEX_Q7; break;
        case 8: speexEnc=SpeexEncoding.SPEEX_Q8; break;
        case 9: speexEnc=SpeexEncoding.SPEEX_Q9; break;
        case 10:speexEnc=SpeexEncoding.SPEEX_Q10; break;
        default:speexEnc=SpeexEncoding.SPEEX_Q5;
      }

      frameSize = audioFormat.getFrameSize();
      if (verbose) M.info("Audio format = "+audioFormat+", FS="+frameSize);
      DataLine.Info lineInfo = new DataLine.Info(TargetDataLine.class,audioFormat);
      if (!AudioSystem.isLineSupported(lineInfo)) M.error("Audio "+lineInfo+" not supported");
      audioLine = (TargetDataLine)AudioSystem.getLine(lineInfo);
      AudioInputStream audiostrconvert=new AudioInputStream(audioLine);
      audioLine.open(audioFormat,bufsize);
      audioLine.start();
      convert=new SpeexFormatConvertionProvider();
      System.out.println(audiostrconvert.getFormat());
      audiostr=convert.getAudioInputStream(speexEnc,audiostrconvert);
    }
    catch (Exception e) {
      M.printStackTrace(e);
      throw new MidasException("Open failed", e);
    }
  }

  private void closeAudio () {
    audioLine.stop();
    audioLine.close();
  }

  public void actionPerformed (ActionEvent ae) {
    topframe.setVisible(false);
    closeAudio();
  }
}
