package nxm.audio.prim;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.TargetDataLine;

import nxm.sys.lib.Data;
import nxm.sys.lib.DataFile;
import nxm.sys.lib.Primitive;

/**
  Simple audio recorder using javax.sound utilities.

  @author Jeff Schoen
  @since NeXtMidas 2.1.1
*/
public class sourceaudio extends Primitive {

  private DataFile ho;
  private Data data;
  private boolean ulaw;
  private float rate;
  private int bits, frameSize=1, bufsize=8000, channels;
  private double length, size, gain=0, pan=0;
  private boolean bige=false, signed=true, stopped=false;
  private boolean nodrain=false;

  private TargetDataLine audioLine;
  private String format;

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

  public int open() {

    format = MA.getS("FORMAT");
    rate   = MA.getF("RATE");
    length = MA.getD("LENGTH", (M.macro==null)? 1 : -1);
    size   = (length<0)? 1e6 : length * rate;

    ho = MA.getDataFile ("OUT","1000",format,0);
    ho.setXDelta(1.0/(double)rate);
    ho.setSize(size);
    ho.open(DataFile.OUTPUT);

    bits = ho.bps*8;
    channels = ho.spa;

    bige = MA.getState("/BIGE");
    ulaw = MA.getState("/ULAW");
    gain = MA.getD("/GAIN",gain);
    pan  = MA.getD("/PAN",pan);
    nodrain = MA.find("/NODRAIN")? MA.getState("/NODRAIN",true) : false;

    openAudio();

    xfer = MA.getL("/TL", (int)(0.25*rate) );
    xfer = (xfer/frameSize) * frameSize;
    data = ho.getDataBuffer(xfer);

    return (NORMAL);
  }

  public int process() {
    if (stopped) return (FINISH);
    int avail = audioLine.available();
    if (avail<xfer) return (NOOP);
    acquireAudio (data.buf,xfer*data.bpe);
    ho.write(data);
    if (length>0 && ho.seek()>=size) return (FINISH);
    return (NORMAL);
  }

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

  private void openAudio() {
    try {
      AudioFormat audioFormat;
      if (ulaw) audioFormat = new AudioFormat (AudioFormat.Encoding.ULAW,
        rate, bits, channels, frameSize, rate/frameSize, bige);
      else audioFormat = new AudioFormat (rate, bits, channels, signed, bige);
      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);
      audioLine.open(audioFormat,bufsize);
//      setGain(gain);
//      setPan(pan);
      audioLine.start();
    } catch (Exception e) { M.error(e); }
  }

  private void acquireAudio (byte[] buffer, int bytes) {
    int numBytesRead = audioLine.read(buffer,0,bytes);
  }

  private void closeAudio() {
    stopped = true;
    if (!nodrain) audioLine.drain();
    audioLine.stop();
    audioLine.close();
  }

  /** Set the mixer gain in dB
   *  @param valueDB the mixer gain in dB
   */
  public void setGain (double valueDB) {
    try {
      FloatControl fc = (FloatControl) audioLine.getControl(FloatControl.Type.MASTER_GAIN);
      if (verbose) M.info("Gain = "+valueDB+"  ["+fc.getMinimum()+","+fc.getMaximum()+"]");
      gain = Math.max(fc.getMinimum(),Math.min(fc.getMaximum(),valueDB));
      fc.setValue((float)gain);
    } catch (Exception e) { e.printStackTrace(); }
  }
  /** Get the mixer gain in dB
   *  @return the mixer gain in dB
   */
  public double getGain() { return gain; }

  /** Set the mixer balance
   *  @param valueLR the mixer balance (-1=Left 0=Center 1=Right)
   */
  public void setPan (double valueLR) {
    try {
      FloatControl fc = (FloatControl) audioLine.getControl(FloatControl.Type.PAN);
      if (verbose) M.info("Pan = "+valueLR+"  ["+fc.getMinimum()+","+fc.getMaximum()+"]");
      pan = Math.max(fc.getMinimum(),Math.min(fc.getMaximum(),valueLR));
      fc.setValue((float)pan);
    } catch (Exception e) { e.printStackTrace(); }
  }
  /** Get the mixer balance
   *  @return the mixer balance (-1=Left 0=Center 1=Right)
   */
  public double getPan() { return pan; }

}