import processing.core.*; 
import processing.xml.*; 

import processing.serial.*; 
import processing.core.*; 
import edu.cmu.sphinx.frontend.util.Utterance; 
import edu.cmu.sphinx.frontend.util.Microphone; 
import edu.cmu.sphinx.recognizer.Recognizer; 
import edu.cmu.sphinx.result.Result; 
import edu.cmu.sphinx.util.props.ConfigurationManager; 
import edu.cmu.sphinx.util.props.PropertyException; 
import java.io.IOException; 
import java.net.URL; 
import java.lang.reflect.*; 

import edu.cmu.sphinx.frontend.frequencywarp.*; 
import edu.cmu.sphinx.jsgf.parser.*; 
import edu.cmu.sphinx.result.*; 
import edu.cmu.sphinx.decoder.*; 
import edu.cmu.sphinx.util.props.*; 
import edu.cmu.sphinx.linguist.language.ngram.large.*; 
import edu.cmu.sphinx.linguist.flat.*; 
import edu.cmu.sphinx.linguist.language.grammar.*; 
import edu.cmu.sphinx.linguist.acoustic.tiedstate.*; 
import edu.cmu.sphinx.linguist.acoustic.trivial.*; 
import edu.cmu.sphinx.decoder.scorer.*; 
import edu.cmu.sphinx.util.*; 
import edu.cmu.sphinx.linguist.language.ngram.*; 
import edu.cmu.sphinx.util.props.tools.*; 
import edu.cmu.sphinx.frontend.databranch.*; 
import edu.cmu.sphinx.frontend.util.*; 
import edu.cmu.sphinx.linguist.dictionary.*; 
import edu.cmu.sphinx.linguist.acoustic.*; 
import edu.cmu.sphinx.linguist.*; 
import edu.cmu.sphinx.frontend.*; 
import edu.cmu.sphinx.instrumentation.*; 
import edu.cmu.sphinx.frontend.endpoint.*; 
import edu.cmu.sphinx.frontend.transform.*; 
import edu.cmu.sphinx.linguist.language.classes.*; 
import edu.cmu.sphinx.frontend.filter.*; 
import edu.cmu.sphinx.decoder.pruner.*; 
import edu.cmu.sphinx.jsgf.*; 
import edu.cmu.sphinx.frontend.window.*; 
import edu.cmu.sphinx.jsgf.rule.*; 
import edu.cmu.sphinx.recognizer.*; 
import edu.cmu.sphinx.linguist.dflat.*; 
import edu.cmu.sphinx.decoder.search.*; 
import edu.cmu.sphinx.linguist.util.*; 
import edu.cmu.sphinx.frontend.feature.*; 
import edu.cmu.sphinx.decoder.search.stats.*; 
import edu.cmu.sphinx.util.machlearn.*; 
import edu.cmu.sphinx.linguist.acoustic.tiedstate.HTK.*; 
import edu.cmu.sphinx.linguist.lextree.*; 

import java.applet.*; 
import java.awt.Dimension; 
import java.awt.Frame; 
import java.awt.event.MouseEvent; 
import java.awt.event.KeyEvent; 
import java.awt.event.FocusEvent; 
import java.awt.Image; 
import java.io.*; 
import java.net.*; 
import java.text.*; 
import java.util.*; 
import java.util.zip.*; 
import java.util.regex.*; 

public class sphinxAdditionsTest extends PApplet {



Sphinx listener;
String s = ""; // variable for sphinx results

Serial myPort;  // port for receipt printer

static int LF = 10;
static boolean doprint = true;
static int silenttime = 60000;

int lastevent = 0;
boolean waiting;


public void setup() {
  size(400, 400);
  background(0);

  // setup printer if necessary
  if(doprint)
    setupPrinter();

  // setup speech recognition
  listener = new Sphinx(this,"sphinx.config.xml");
  lastevent = 0; // for intra-utterance timing
  waiting = true;
}

public void setupPrinter() {
  // serial init
  println(Serial.list());
  // I know that the first port in the serial list on my mac
  // is always my  Keyspan adapter, so I open Serial.list()[0].
  // Open whatever port is the one you're using.
  myPort = new Serial(this, Serial.list()[0], 4800);

  //init receipt printer
  myPort.write(ESC);
  myPort.write("@");

  // fast feed
  myPort.write(ESC);
  myPort.write('d');
  myPort.write(8);

  // line feed
  myPort.write(LF);

  // cut paper
  myPort.write(ESC);
  myPort.write('i');

  delay(1000);
}

public void dispose() {
  // clean up listener threads
  listener.dispose();
}

public void draw() { 
  stroke(255);

  // check how long it has been silent
  if(!waiting && ((millis()-lastevent) > silenttime)) {
    doCut();
    waiting = true;
  }
}

public void doCut() {
  // advance print and cut paper
  // fast feed
  myPort.write(ESC);
  myPort.write('d');
  myPort.write(8); // feed this many lines

  // cut
  myPort.write(ESC);
  myPort.write('i');
}

public void doQuit() {

  if(doprint)
    doCut();

  // close and dispose
  exit();
}

public void SphinxEvent(Sphinx _l) {
  int now = millis();
  waiting = false;
  
  s = _l.readString(); // returns the recognized string

    // echo to screen 
  //  System.out.print("["+now+"] sphinx heard: "+s);
  System.out.print("["+now+"] "+s);

  // intra-utterance timing
  System.out.println("  ("+(now-lastevent)+" since last utterance)");
  lastevent=now;

  // send to printer if necessary
  if(doprint) {
    myPort.write(s);
    myPort.write(LF);
  }

  // check for stop command
  if((s.indexOf("quit") >= 0) || (s.indexOf("exit") >= 0) || ((s.indexOf("stop") >= 0) && (s.indexOf("dystopian") < 0))) {
    doQuit();
  }
}

/* 
 
 Sphinx wrapper for use in Processing.  
 Works with Sphinx-4 1.0beta5
 
 build from Hellow World example in Sphinx-4 Application Programmers Guide:
 http://cmusphinx.sourceforge.net/sphinx4/doc/ProgrammersGuide.html#helloworld
 
 with reference to Listen.java from Chatter project by Mie Sorensen et al.:
 http://www.lilwondermat.com/downloads/Chatter.zip
 
 rtwomey@u.washington.edu
 
 */

// processing stuff


// sphinx stuff







// java stuff





public class Sphinx implements Runnable {
  Recognizer recognizer;
  Microphone microphone;
  PApplet parent;
  Method SphinxEventMethod;

  Thread t;
  String resultText;
  String config;
  String path;
  Recognizer.State READY = Recognizer.State.READY;

  public Sphinx(PApplet _p, String _c) {
    this.parent = _p;
    this.config = _c;
    resultText = "";
    init();

    parent.registerDispose(this);

    t = new Thread(this);
    t.start();
  }

  private void init() {
    path = parent.dataPath("");
    //System.out.println("Microphone off test");
    try {
      SphinxEventMethod = parent.getClass().getMethod("SphinxEvent", new Class[] { 
        Sphinx.class
      }
      );

      // Initialize the voice recognition      
      System.out.println("Initializing Sphinx-4:");      
      System.out.println("  data directory " + path);
      System.out.println("  config file " + config);
      URL url = new URL("file:///" + path + config);

      System.out.print("loading configuration...");
      ConfigurationManager cm = new ConfigurationManager(url);
      System.out.println("loaded");

      recognizer = (Recognizer) cm.lookup("recognizer");
      System.out.print("allocating recognizer (note this may take some time)... ");
      try {
        recognizer.allocate();
      } 
      catch (Exception e) {
        e.printStackTrace();
      };
      System.out.println("allocated");

      microphone = (Microphone) cm.lookup("microphone");
      // start recording / recognizing
      System.out.print("microphone recording... ");
      if (!microphone.startRecording()) {
        System.out.println("Cannot start microphone.");
        recognizer.deallocate();
      }
      System.out.println("started");
    } 
    catch (IOException ioe) {
      ioe.printStackTrace();
    } 
    catch (PropertyException pe) {
      pe.printStackTrace();
    } 
    catch (Exception e) {
      e.printStackTrace();
    }
  }

  public void run() {
    // Sphinx thread, main loop
    System.out.println("main recognition loop started");
    while (true) {
      try {        
        // ask the recognizer to recognize text in the recording
        if(recognizer.getState()==Recognizer.State.READY) {
          Result result = recognizer.recognize(); 
          // got a result
          if (result != null) {
            resultText = result.getBestFinalResultNoFiller();
            if(resultText.length()>0) {
              //              System.out.println("["+result.getStartFrame()+","+result.getEndFrame()+"]");
//              System.out.println(result.getTimedBestResult(false, true));
              makeEvent();
            }
          }
        }
      }
      catch (Exception e) { 
        System.out.println("exception Occured ");  
        e.printStackTrace(); 
        System.exit(1);
      }
    }
  }

  public String readString() {
    return resultText;
  }

  public void makeEvent() {
    if (SphinxEventMethod != null) {
      try {
        SphinxEventMethod.invoke(parent, new Object[] { 
          this
        }
        );
      } 
      catch (Exception e) {
        e.printStackTrace();
        SphinxEventMethod = null;
      }
    }
  }

  public void dispose() {
    microphone.stopRecording();
    while(recognizer.getState()==Recognizer.State.RECOGNIZING) {
      // wait till finished recognizing
    };
    recognizer.deallocate();
  }
}

  static public void main(String args[]) {
    PApplet.main(new String[] { "--bgcolor=#FFFFFF", "sphinxAdditionsTest" });
  }
}
