#include "testApp.h"

extern GlobalForces globalForces;
extern GlobalUI globalUI;
extern ScanPath scanPath;
//--------------------------------------------------------------
void testApp::setup(){	
    theMesh = NULL;
	ofxiPhoneSetOrientation(OFXIPHONE_ORIENTATION_LANDSCAPE_RIGHT);
	ofxAccelerometer.setup();

  	// listen on the given port
	cout << "listening for osc messages on port " << INPORT << "\n";
	receiver.setup( INPORT );
    sender.setup( HOST, OUTPORT );

    ofBackground(0, 0, 0);
	ofSetFullscreen(true);
	ofSetFrameRate(60);
    
	timeStep = 1/ofGetFrameRate();
    
	ofEnableSmoothing();
    ofEnableAlphaBlending();
    
    
    
    pitch = 60.0;
    phasorIncr = pitch/SAMPLE_RATE;
    globalForces.gravityAmt = 0.0;
    globalForces.avFilterAmt = 0.01;
    
    // 
	ofSoundStreamSetup(2,0,this, SAMPLE_RATE,256, 2);
    
    // if we want things to start immediately
    paused = false;
    //audioOn = true;
	drawingOn = true;
    audioAccessFlag = false;
    meshConstructionFlag = false;
    setupGui();
    
    
    setupMesh();
    scanPath.init();
    
    globalUI.touchMode = globalUI.GRAB;
    scanPath.scanMode = scanPath.DISPLACEMENT;
}
//--------------------------------------------------------------
void testApp::setupMesh(){
    // SET UP THE MESH STRUCTURE
    if(audioAccessFlag) return; // or rather wait !
    meshConstructionFlag = TRUE;
    static int type = 0;
    int numTypes = 6;
    
    // TODO: Just empty vectors

    
    if (type % numTypes == 0){
    // different for iphone
	if(ofGetWidth() == 480){ //PHONE
        theMesh = new SpiderCrossMesh(60,5);
    }else{  //PAD
        //theMesh = new SpiderCrossMesh(140,5);

        theMesh = new SpiderCrossMesh(100,9);
    }
    }else if(type % numTypes ==1){

       theMesh = new SpiderCrossMesh(30,30);
    }else if(type % numTypes ==2){

        theMesh = new SpiderCrossMesh(6,6);
    }else if(type % numTypes ==3){
        
        theMesh = new TriangleMesh(34,34);
    }else if(type % numTypes ==4){

        theMesh = new SquareCrossMesh(28,28);
    }else if(type % numTypes ==5){
        
        theMesh = new DropletMesh(128);
    }
    type++;
    //theMesh = new LineMesh(123);
    //theMesh = new GroundedLineMesh(400);
    //theMesh = new TriangleMesh(40,40);
    //theMesh = new LineMesh(100);
    
    // SET CONSTANTS UP FOR MESH
    theMesh->setSpringConstant(0.8);
    theMesh->setMass(1);
    theMesh->setFriction(0.99991);
    

    numTouches = 0;
    meshConstructionFlag = FALSE;
    ofSoundStreamStart();
    drawingOn = true;
    paused = false;
    audioOn = true;
    cout << "MESH REGENERATED\n";
}
//--------------------------------------------------------------
//--------------------------------------------------------------
// GUI stuff
//--------------------------------------------------------------
//--------------------------------------------------------------
void testApp::loadLogXML(){
    ofxXmlSettings XMLlog;

    string message;
    if( XMLlog.loadFile(ofxiPhoneGetDocumentsDirectory() + "wabletlogs.xml") ){
        message = "mySettings.xml loaded from documents folder!";

    }else if( XMLlog.loadFile("Logs/wabletlogs.xml") ){
        message = "mySettings.xml loaded from data folder!";


    }else{
        message = "unable to load mySettings.xml check data/ folder";
        return;
    }
    cout << message << "\n";

    // get all the values from log
    timesOpened = XMLlog.getValue("wabletlogs:timesOpened", 0);
    timesOpened++;
  
}
//--------------------------------------------------------------
void testApp::saveLogXML(){
    ofxXmlSettings XMLlog;
    
    XMLlog.setValue("wabletlogs:timesOpened", timesOpened);
    
    if(XMLlog.saveFile(ofxiPhoneGetDocumentsDirectory() + "wabletlogs.xml")){
        cout << "Saved wabletlogs.XML in iphone documents OK";
    }else if(XMLlog.saveFile("Logs/wabletlogs.xml")){
            cout << "Saved wabletlogs.XML in data folder";
    }else{
        cout << "wabletlogs file did not save :(";
    }
    

    
}
//--------------------------------------------------------------
void testApp::setupGui(){
    float xInit = OFX_UI_GLOBAL_WIDGET_SPACING;
    float length = 128-xInit*2;
    

    float dim = 42;
    
    // LEFT GUI ------------------
    guiL = new ofxUICanvas(0,0,128,ofGetHeight());
    guiL->addSpacer(length-xInit, 2);
    guiL->addWidgetDown(new ofxUILabel("Physics", OFX_UI_FONT_LARGE));
    
    ofxUIWidget *slider;
    slider = guiL->addWidgetDown(new ofxUISlider(length,dim,0.0,0.8,0.4,"SPRING K"));
    slider->setDrawPadding(true);
    slider->setColorFill(ofColor(0,0,255));
    slider->setColorFillHighlight(ofColor(0,0,255));
    
    slider = guiL->addWidgetDown(new ofxUISlider(length,dim, 0.0, 4.0, 0.0, "GRAVITY"));
    slider->setDrawPadding(true);
    slider->setColorFill(ofColor(0,0,255));
    slider->setColorFillHighlight(ofColor(0,0,255));
    
    slider = guiL->addWidgetDown(new ofxUISlider(length,dim,0.0,0.3,0.0,"HOMING"));
    slider->setDrawPadding(true);
    slider->setColorFill(ofColor(0,0,255));
    slider->setColorFillHighlight(ofColor(0,0,255));

    slider = guiL->addWidgetDown(new ofxUISlider(length,dim, 0.0, 0.5, 0.01, "SMOOTHING"));
    slider->setDrawPadding(true);
    slider->setColorFill(ofColor(0,0,255));
    slider->setColorFillHighlight(ofColor(0,0,255));
    

    
    guiL->addSpacer(length-xInit, 2);

    
    guiL->setWidgetPosition(OFX_UI_WIDGET_POSITION_DOWN);
	slider = guiL->addSlider("PITCH", 2.0, 100.0, 80.0, length, 370);
    slider->setDrawPadding(true);
    slider->setColorFill(ofColor(0,0,255));
    slider->setColorFillHighlight(ofColor(0,0,255));
    
    ofAddListener(guiL->newGUIEvent, this, &testApp::guiLEvent);
    guiL->loadSettings("GUI/guiSettings.xml");
    
    // RIGHT GUI -----------------------
    
    guiR = new ofxUICanvas(ofGetWidth()-128, 0, 128, ofGetHeight());

    
    guiR->addSpacer(length-xInit, 2);
    
    vector<string> names;
	names.push_back("GRAB");
	names.push_back("FORCE");
    names.push_back("SINE");
	names.push_back("STICK");
    names.push_back("UNSTICK");
    names.push_back("NEWPATH");
    
    
    ofxUIRadio* radio;
    radio = guiR->addRadio("TOUCH MODE", names, OFX_UI_ORIENTATION_VERTICAL, dim, dim);
    radio->setDrawPadding(true);
    radio->setColorFill(ofColor(0,0,255));
    radio->setColorFillHighlight(ofColor(0,0,255));
    
    slider = guiR->addWidgetDown(new ofxUISlider(length,dim, -0.4, 4.0, 1.0, "TOUCH AMT"));
    slider->setDrawPadding(true);
    slider->setColorFill(ofColor(0,0,255));
    slider->setColorFillHighlight(ofColor(0,0,255));
    
    guiR->addSpacer(length-xInit, 2);
    
    guiR->addToggle("PAUSE", false, dim, dim);
    guiR->addButton("RESET", false, dim, dim);
    guiR->addButton("NEW", false, dim, dim);
    
    
    ofAddListener(guiR->newGUIEvent, this, &testApp::guiREvent);
    guiR->loadSettings(ofxiPhoneGetDocumentsDirectory() + "guiSettings.xml");
    radio->activateToggle("GRAB");
    
    // SETUP MESH GUI---------------------------
    
    
    // load gui settings?? 
    loadLogXML();
}
//--------------------------------------------------------------
void testApp::UIcallBack(int buttID){
    cout << " BUTT ID " << buttID << "\n";
    
}
//--------------------------------------------------------------
void testApp::guiREvent(ofxUIEventArgs &e)
{
    if(e.widget->getName() == "TOUCH MODE")
    {
        cout << "TOUCH MODE";
        
    }
    if(e.widget->getName() == "GRAB"){
        cout << "GRAB";
        globalUI.touchMode = globalUI.GRAB;
    }else if(e.widget->getName() == "FORCE"){

        globalUI.touchMode = globalUI.FORCE_FIELD;
    }else if(e.widget->getName() == "STICK"){
        
        globalUI.touchMode = globalUI.CONSTRAIN;
    }else if(e.widget->getName() == "UNSTICK"){
        
        globalUI.touchMode = globalUI.UNCONSTRAIN;
    }else if(e.widget->getName() == "NEWPATH"){
        if(globalUI.touchMode != globalUI.INSCRIBE_PATH){
            theMesh->resetPositions();
            theMesh->resetVelocities();
            globalUI.touchMode = globalUI.INSCRIBE_PATH;
            theMesh->clearScanPath();
            theMesh->update();
        }
    }else if(e.widget->getName() == "SINE"){
        globalUI.touchMode = globalUI.SPATIAL_HARMONIC;
    }else if(e.widget->getName() == "RESET"){
        theMesh->resetAll();
        numTouches = 0;
    }else if(e.widget->getName() == "NEW"){
        // ????
        
        cout << ((ofxUIButton *)e.widget)->getValue();
        
        if( ((ofxUIButton *)e.widget)->getValue() == 1) {
            cout << "new down";
            deleteMesh();
            
        }
        if( ((ofxUIButton *)e.widget)->getValue() == 0) setupMesh();
    }else if(e.widget->getName() == "PAUSE"){
        paused = !paused;
    }else if(e.widget->getName() == "TOUCH AMT"){
        ofxUISlider *slider = (ofxUISlider *) e.widget;
        slider->setDrawPadding(true);
        globalForces.touchStrength = slider->getScaledValue();
    }
    
}
//--------------------------------------------------------------
void testApp::guiLEvent(ofxUIEventArgs &e)
{
    if(e.widget->getName() == "SPRING K")
    {
        ofxUISlider *slider = (ofxUISlider *) e.widget;

        theMesh->setSpringConstant( slider->getScaledValue() ); 
    }else if(e.widget->getName() == "GRAVITY")
    {
        ofxUISlider *slider = (ofxUISlider *) e.widget;
        slider->setDrawPadding(true);
        globalForces.gravityAmt = slider->getScaledValue(); 
        //radio->getScaledValue() );
    }else if(e.widget->getName() == "HOMING")
    {
        ofxUISlider *slider = (ofxUISlider *) e.widget;
        globalForces.homingAmt = slider->getScaledValue();
        //radio->getScaledValue() );
    }else if(e.widget->getName() == "SMOOTHING")
    {
        
        
        ofxUISlider *slider = (ofxUISlider *) e.widget;
        globalForces.avFilterAmt = slider->getScaledValue();
        //radio->getScaledValue() );
    }else if(e.widget->getName() == "FRICTION")
    {
        
        
        ofxUISlider *slider = (ofxUISlider *) e.widget;
        theMesh->setFriction(slider->getScaledValue());
        //radio->getScaledValue() );
    }else if(e.widget->getName() == "PITCH")
    {
        
        
        ofxUISlider *slider = (ofxUISlider *) e.widget;
        pitch = slider->getScaledValue();
        //radio->getScaledValue() );
    }
    
    
}
//--------------------------------------------------------------
//--------------------------------------------------------------
// App running stuff
//--------------------------------------------------------------
//--------------------------------------------------------------
//--------------------------------------------------------------
void testApp::exit(){
	ofSoundStreamClose();
    
    // save everything...
    guiL->saveSettings(ofxiPhoneGetDocumentsDirectory() + "guiSettings.xml");
    delete guiL;
    saveLogXML();
    
    if(theMesh != NULL){
        scanPath.clear();
        delete theMesh;
    }
    
}

//--------------------------------------------------------------
void testApp::update(){
    

    
	if(!paused){
        if (theMesh != NULL){
            theMesh->update();
            
        }
        // BASIC STRING basicString->update();
    }
     handleMessages(); // !?!??   
    //cout << "UPDATE frame rate = " << ofGetFrameRate() << " sec\n";
}

//--------------------------------------------------------------
void testApp::draw(){
	if(drawingOn){
        if(theMesh != NULL)
            theMesh->draw();

	}
	//cout << "frame rate = " << ofGetFrameRate() << " sec\n";
    //drawMessages();

    drawSidePanels();
}
//--------------------------------------------------------------
// background for UI
void testApp::drawSidePanels(){
    ofSetColor(123, 123, 123);
    ofRect(0, 0, 128, ofGetHeight());
    ofRect(ofGetWidth()-128, 0, 128, ofGetHeight());
}
//--------------------------------------------------------------
void testApp::drawMessages(){
    for ( int i=0; i<NUM_MSG_STRINGS; i++ )
    {
        ofDrawBitmapString( msg_strings[i], 10, 40+15*i );
    }
}

//--------------------------------------------------------------
//--------------------------------------------------------------
//------------------------CHANGING MESH----------------------------
//--------------------------------------------------------------
//--------------------------------------------------------------
//--------------------------------------------------------------

void testApp::deleteMesh(){
     // TODO - OTHER THREADS FUCK THIS UP
    if(audioAccessFlag) return; // or rather wait !
    //stop everything
    ofSoundStreamStop();
    
    paused = true;
    audioOn = false;
    drawingOn = false;
    
    scanPath.clear();
    // trash it
    delete theMesh;
    theMesh = NULL;
    
    cout << "MESH DELETED\n";
}
//--------------------------------------------------------------
void testApp::regenerateMesh(string meshType, int dim1, int dim2){
    // TODO - OTHER THREADS FUCK THIS UP??
    if(audioAccessFlag) return; // or rather wait !
    
    if (theMesh != NULL) return;
    
    if (meshType == "LineMesh"){
     
        theMesh = new LineMesh(dim1);
    }else if (meshType == "DropletMesh"){
      
        theMesh = new DropletMesh(dim1);
    }else if (meshType == "SpiderMesh"){
      
        theMesh = new SpiderMesh(dim1,dim2);
    }else if (meshType == "SpiderCrossMesh"){
        
        theMesh = new SpiderCrossMesh(dim1,dim2);
    }else if (meshType == "SquareCrossMesh"){
    
        theMesh = new SquareCrossMesh(dim1,dim2);
    }else if (meshType == "TriangleMesh"){
        
        theMesh = new TriangleMesh(dim1,dim2);
    }else if (meshType == "GroundedLineMesh"){
     
        theMesh = new GroundedLineMesh(dim1);
    }else{
        cout << "OSC message error: unrecognised mesh type" << endl;
        return;
    }
    ofSoundStreamStart();
    drawingOn = true;
    paused = false;
    audioOn = true;
    cout << "MESH REGENERATED\n";

}



//--------------------------------------------------------------
//--------------------------------------------------------------
//------------------------KEYS (OSX)----------------------------
//--------------------------------------------------------------
//--------------------------------------------------------------
// all this is now osc messages
void testApp::keyPressed(int key){
    
	if (key == 'p'){
        
		paused = !paused;
	}
	if (key == 's'){
		theMesh->toggleSyrup(true);
	}
	if (key == 'f'){
		theMesh->toggleSpringForces(false);
	}	
	if (key == '='){
		theMesh->increasePropagationSpeed();
	}		
	if (key == '-'){
		theMesh->decreasePropagationSpeed();
	}	
	if (key == 'a'){
        if(audioOn){
            audioOn = false;
        }else{
            audioOn = true;
        }
	}	
	if (key == 'r'){
		theMesh->resetPositions();
        theMesh->resetVelocities();

	}	
	if (key == 'e'){
		theMesh->constrain(.0,.0,Mesh::CONSTRAIN_EDGES);
	}
	if (key == 'c'){
		theMesh->constrain(.0,.0,Mesh::CONSTRAIN_CORNERS);
	}
	if (key == 'u'){
		theMesh->unconstrain();
	}
	if (key == 'l'){
		theMesh->setRestLength();
	}
	if (key == '0'){
		theMesh->zeroRestLength();
	}
	if (key == 'd'){
		drawingOn = !drawingOn;
	}
}

//---------------------------------------------------------------

void testApp::keyReleased(int key){
	if (key == 's'){
		theMesh->toggleSyrup(false);
	}
	if (key == 'f'){
		theMesh->toggleSpringForces(true);
	}	
}



//--------------------------------------------------------------
//--------------------------------------------------------------
//------------------------TOUCH---------------------------------
//--------------------------------------------------------------
//--------------------------------------------------------------




void testApp::touchDown(ofTouchEventArgs &touch){
    if(theMesh == NULL) return;
    
	double dax, day;
	// touchDown
    //cout << "touchDown ID: " << touch.id << endl;
    if(globalUI.handleTouchDown(touch.x, touch.y)) return;

    
    addTouch();


	
    dax = (double(touch.x) - globalUI.borderSize)/ofGetHeight();
	day = double(touch.y)/ofGetHeight();
    
    switch (globalUI.touchMode){
        case globalUI.GRAB:
            theMesh->grab(dax,day,touch.id);
            break;
        case globalUI.INSCRIBE_PATH:
            // start a new path, with touch id? ie:polyphonic paths!??!
            break;
        case globalUI.FORCE_FIELD:
            globalForces.createForceTouchPoint(dax,day, touch.id);
            break;
        case globalUI.SPATIAL_HARMONIC:
            // work out 
            theMesh->spatialHarmonic(numTouches, 0);
            break;
        case globalUI.CONSTRAIN:
            theMesh->constrain(dax,day,Mesh::CONSTRAIN_GRAB_REGION);
            break;
        case globalUI.UNCONSTRAIN:
            theMesh->unconstrain(dax,day,Mesh::CONSTRAIN_GRAB_REGION);
            break;
        case globalUI.VIBRATE:
            break;
        default:
            
            break;            
    }

    
}

//--------------------------------------------------------------
void testApp::touchMoved(ofTouchEventArgs &touch){
    if(theMesh == NULL) return;
    if(globalUI.handleTouchMove(touch.x, touch.y)) return;
    //cout << "touchMoved ID: " << touch.id << endl;
    

	double dax, day;
	dax = (double(touch.x) - globalUI.borderSize)/ofGetHeight();
	day = double(touch.y)/ofGetHeight();
    
    
    
    /*
    if(kslider->checkForTouch(touch.x, touch.y)){
        cout << "kslider touched";
       kslider->adjust(touch.x,touch.y);
        return;
    }
     */

    switch (globalUI.touchMode){
        case globalUI.GRAB:
            theMesh->drag(dax,day,touch.id);
            break;
        case globalUI.INSCRIBE_PATH:
            theMesh->inscribeScanPath(dax,day);
            break;
        case globalUI.FORCE_FIELD:
            //theMesh->forceField(dax,day,touch.id);
            cout << "move force tp\n";
            globalForces.moveForceTouchPoint(dax,day, touch.id);
            break;
        case globalUI.SPATIAL_HARMONIC:
            // makes no sense
            break;
        case globalUI.CONSTRAIN:
            theMesh->constrain(dax,day,Mesh::CONSTRAIN_GRAB_REGION);
            break;
        case globalUI.UNCONSTRAIN:
            theMesh->unconstrain(dax,day,Mesh::CONSTRAIN_GRAB_REGION);
            break;
        case globalUI.VIBRATE:
            break;
        default:
            
            break;            
    }
}

//--------------------------------------------------------------
void testApp::touchUp(ofTouchEventArgs &touch){
    if(theMesh == NULL) return;
    if(globalUI.handleTouchUp(touch.x, touch.y)) return;
    
    
    removeTouch();
    

    switch (globalUI.touchMode){
        case globalUI.GRAB:
            theMesh->unGrab(touch.id);
            break;
        case globalUI.INSCRIBE_PATH:
            theMesh->disconnectDraw();
            break;
        case globalUI.FORCE_FIELD:
            globalForces.removeForceTouchPoint(touch.id);
            break;
        case globalUI.SPATIAL_HARMONIC:
            break;
        case globalUI.CONSTRAIN:
            
            break;
        case globalUI.UNCONSTRAIN:
            
            break;
        case globalUI.VIBRATE:
            break;
        default:
            
            break;            
    }

}

//--------------------------------------------------------------
void testApp::touchDoubleTap(ofTouchEventArgs &touch){
    // pretty much useless.
    /*
    ofxOscMessage m;
    m.setAddress( "/test" );
    m.addIntArg( 1 );
    m.addFloatArg( 3.5f );
    m.addStringArg( "hello" );
    m.addFloatArg( ofGetElapsedTimef() );
    sender.sendMessage( m );
     */
    
    //ofxiPhoneScreenGrab(NULL);  
}


//--------------------------------------------------------------
void testApp::addTouch(){
    numTouches++;
    cout << "numtouches " << numTouches << endl;
}
//--------------------------------------------------------------
void testApp::removeTouch(){
    numTouches--;
    
}

//--------------------------------------------------------------
//--------------------------------------------------------------
    // MOUSE  - NOT NEEDED FOR IPAD
//--------------------------------------------------------------
//--------------------------------------------------------------
void testApp::mouseMoved(int x, int y ){
	//cout << "mouse moved " << x << 'y' << y << '\n';
    /*
	double dax, day;
	
	// normalise
	dax = double(x)/ofGetWidth();
	day = double(y)/ofGetHeight();
    
	theMesh->checkHover(dax,day);
    
    */
}

//--------------------------------------------------------------
void testApp::mouseDragged(int x, int y, int button){
	//cout << "mouse DRAGGED " << x << 'y' << y << '\n';
    /*
	double dax, day;
	dax = double(x)/ofGetWidth();
	day = double(y)/ofGetHeight();
	
	if(button == 0){
		if(!paused){
			theMesh->drag(dax,day, 0);
		}else{
			// pause mode - draw a scan path!
			
			theMesh->drawScanPath(dax,day);
			audioOn = false;
		}
	}else if(button == 2){
		
	}
     */
    
}

//--------------------------------------------------------------
void testApp::mousePressed(int x, int y, int button){
    /*
	double dax, day;
	// normalise
	dax = double(x)/ofGetWidth();
	day = double(y)/ofGetHeight();
	
	if(button == 0){
		if(paused){
			// draw a path
			audioOn = false;
			theMesh->deleteScanPath();
		}else{
			theMesh->grab(dax,day, 0);
		}
	}else if(button == 2){
		theMesh->constrain(dax,day,Mesh::CONSTRAIN_GRAB_REGION);
	}else{
		cout << "OTHER BUTTON?\n";
	}
     */
    
    
}

//--------------------------------------------------------------
void testApp::mouseReleased(int x, int y, int button){
	/*
	if(button == 0){
		theMesh->unGrab(0);
	}else if(button == 2){
	}else{
		cout << "butt other";
	}
     */
	
}
//--------------------------------------------------------------
//--------------------------------------------------------------
//-------------------------AUDIO-----------------------------
//--------------------------------------------------------------
//--------------------------------------------------------------
void testApp::restartAudioStream(){
    ofSoundStreamStart();
    audioOn = true;
    
}
//--------------------------------------------------------------

void testApp::audioReceived(float * input, int bufferSize, int nChannels){
    
}
//--------------------------------------------------------------
void testApp::audioRequested 	(float * output, int bufferSize, int nChannels){
	float sample;
    static double phasor;

    if(meshConstructionFlag) return;
    // if no mesh , scanpath will still be there, but empty

    phasorIncr = pitch/SAMPLE_RATE;

	if(audioOn){
        audioAccessFlag = TRUE;
		for (int i = 0; i < bufferSize; i++){

			sample  = float(scanPath.getNextSample(phasor));
            
            // hipass to get rid of DC
            //sample = mydspTools.highpass1(sample);
            sample = mydspTools.butter(sample);
			output[i*nChannels    ] = sample*globalForces.volume;
			output[i*nChannels + 1] = sample*globalForces.volume;
            
            phasor += phasorIncr;
            
            if(phasor >= 1.0){
                phasor -= 1.0;
            }
		}
        audioAccessFlag = FALSE;
	}else{
		for (int i = 0; i < bufferSize; i++){
			output[i*nChannels    ] = 0.0;
			output[i*nChannels + 1] = 0.0;
		}
	}
    
    
	
}

//--------------------------------------------------------------
//--------------------------------------------------------------
//-------------------------OSC MSGS--------------------------------
//--------------------------------------------------------------
//--------------------------------------------------------------

void testApp::handleMessages(){
    
	// hide old messages
	for ( int i=0; i<NUM_MSG_STRINGS; i++ )
	{
		if ( timers[i] < ofGetElapsedTimef() )
			msg_strings[i] = "";
	}
    
	// check for waiting messages
	while( receiver.hasWaitingMessages() )
	{
		// get the next message
		ofxOscMessage m;
		receiver.getNextMessage( &m );
        //cout << "OSC message: " << m.getAddress() << endl;
        
        /****************/
        /* REGENERATION OF NEW MESH */
        /****************/
        
        // if the mesh is not there check for regenerate , otherwise ignore everything
        if(theMesh == NULL){
            if ( m.getAddress() == "regenerate" )
            {
                string meshType;
                int dim1, dim2;
                int numArgs = m.getNumArgs();
                cout << "REGEN num args: " << numArgs << endl;
                // we want to do some error checking here
                meshType = m.getArgAsString(0);
                cout << "REGEN meshType: " << meshType << endl;
                dim1 = m.getArgAsInt32(1);
                if(numArgs == 3){
                    dim2 = m.getArgAsInt32(2);
                }else{
                    dim2 = 3;
                }
                
                regenerateMesh(meshType,dim1,dim2);

                
            }else{
                return;
            }
        }
        else if ( m.getAddress() == "restartNewMesh" )
		{
            ofSoundStreamStart();
            drawingOn = true;
            paused = false;
            audioOn = true;
            
		}   
        /****************/
        /* NOTE HANDLER */
        /****************/
        
		else if ( m.getAddress() == "note" )
		{
            int note = m.getArgAsInt32(0);
        
            int velocity = m.getArgAsInt32(1);
            if(velocity > 0){
                cout << "note pitch = " << pitch << endl;
                pitch = 440.0/pow(2.0,((69.0 - double(note))/12.0));
                theMesh->hit(0.5,0.5,velocity,globalForces.excitationType,globalForces.excitationShape);
            }else{
                theMesh->damp();
            }
            
            
 		}
        else if ( m.getAddress() == "excitationStrength" )
		{
            globalForces.excitationStrength = m.getArgAsFloat(0);
            
            
 		}
        
        else if ( m.getAddress() == "excitationType" )
		{
            
            // set MIDI twanger mode
            if(m.getArgAsString(0) == "POSITION"){
                globalForces.excitationType = GlobalForces::POSITION;
                
            }else if (m.getArgAsString(0) == "VELOCITY"){
                cout << "excitationType = VELOCITY" << endl;
                globalForces.excitationType = GlobalForces::VELOCITY;
            }
		}
        else if ( m.getAddress() == "excitationShape" )
		{
            
            // set MIDI twanger twang shape
            if(m.getArgAsString(0) == "NOISE"){
                globalForces.excitationShape = GlobalForces::NOISE;
                
            }else if (m.getArgAsString(0) == "GAUSS"){
                cout << "excitationType = GAUSS" << endl;
                globalForces.excitationShape = GlobalForces::GAUSS;
            }else if (m.getArgAsString(0) == "SINE"){
                cout << "excitationType = SINE" << endl;
                globalForces.excitationShape = GlobalForces::SINE;
        
            }
            // set size
            globalForces.exciteShapeX = m.getArgAsInt32(1);
            globalForces.exciteShapeY = m.getArgAsInt32(2);
		}
          
        // touchModes: {GRAB,FORCE_FIELD,SPATIAL_HARMONIC,CONSTRAIN,VIBRATE,INSCRIBE_PATH};
		else if ( m.getAddress() == "touchMode" )
		{
            
            // set touch mode
            if(m.getArgAsString(0) == "GRAB"){
                globalUI.touchMode = globalUI.GRAB;
                
            }else if (m.getArgAsString(0) == "FORCE_FIELD"){
                cout << "touchMode = FORCE_FIELD" << endl;
                globalUI.touchMode = globalUI.FORCE_FIELD;
            }else if (m.getArgAsString(0) == "SPATIAL_HARMONIC"){
                //cout << "touchMode = SPATIAL_HARMONIC" << endl;
                globalUI.touchMode = globalUI.SPATIAL_HARMONIC;
            }else if (m.getArgAsString(0) == "CONSTRAIN"){
                //cout << "touchMode = CONSTRAIN" << endl;
                globalUI.touchMode = globalUI.CONSTRAIN;
            }else if (m.getArgAsString(0) == "VIBRATE"){
                //cout << "touchMode = VIBRATE" << endl;
                globalUI.touchMode = globalUI.VIBRATE;
            }else if (m.getArgAsString(0) == "UNCONSTRAIN"){
                //cout << "touchMode = UNCONSTRAIN" << endl;
                globalUI.touchMode = globalUI.UNCONSTRAIN;
            }else if (m.getArgAsString(0) == "INSCRIBE_PATH"){
                //cout << "touchMode = INSCRIBE_PATH" << endl;
                globalUI.touchMode = globalUI.INSCRIBE_PATH;
                theMesh->clearScanPath();              
            }
		}
       
        else if ( m.getAddress() == "pause" )
		{
            paused = !paused;
		}
        else if ( m.getAddress() == "touchStrength" )
		{
            for ( int i=0; i<m.getNumArgs(); i++ )
			{
                if( m.getArgType( i ) == OFXOSC_TYPE_FLOAT ){
                    cout << "touchStrength = " << m.getArgAsFloat(i) << endl;
                    
                    globalForces.touchStrength = m.getArgAsFloat(i);
                }
            }
		}
        else if ( m.getAddress() == "homingAmt" )
		{
            for ( int i=0; i<m.getNumArgs(); i++ )
			{
                if( m.getArgType( i ) == OFXOSC_TYPE_FLOAT ){
                    globalForces.homingAmt = m.getArgAsFloat(i);
                }
            }
		}
        else if ( m.getAddress() == "avFilterAmt" )
		{
            for ( int i=0; i<m.getNumArgs(); i++ )
			{
                if( m.getArgType( i ) == OFXOSC_TYPE_FLOAT ){
                    globalForces.avFilterAmt = m.getArgAsFloat(i);
                }
            }
		}
        else if ( m.getAddress() == "scanMode" )
		{
            // set scan mode
            if (m.getArgAsString(0) == "DISPLACEMENT"){
                cout << "touchMode = DISPLACEMENT" << endl;
                scanPath.scanMode = scanPath.DISPLACEMENT;
            }else if (m.getArgAsString(0) == "SPEED"){
                
                cout << "scanMode = SPEED" << endl;
                scanPath.scanMode = scanPath.SPEED;
            }else if (m.getArgAsString(0) == "SPRING_FORCE"){
                cout << "scanMode = SPRING_FORCE" << endl;
                scanPath.scanMode = scanPath.SPRING_FORCE;
            }else if (m.getArgAsString(0) == "YPOS"){
                cout << "scanMode = YPOS" << endl;
                scanPath.scanMode = scanPath.YPOS;
            }else if (m.getArgAsString(0) == "OTHER"){
                cout << "scanMode = OTHER" << endl;
                //scanMode = OTHER;
            }
		}
		else if ( m.getAddress() == "inscribeScanPath" )
		{
            if (globalUI.touchMode != globalUI.INSCRIBE_PATH){
                theMesh->resetPositions();
                theMesh->resetVelocities();
                paused = true;
                audioOn = false;
                globalUI.touchMode = globalUI.INSCRIBE_PATH;
                theMesh->clearScanPath();
                theMesh->update();
            }else{

                paused = false;
                audioOn = true;
                globalUI.touchMode = globalUI.GRAB;           
            }
            
		}
		else if ( m.getAddress() == "clearScanPath" )
		{
            theMesh->clearScanPath();
		}
        else if ( m.getAddress() == "toggleForce" )
		{
            theMesh->toggleSpringForces();
		}
        else if ( m.getAddress() == "toggleGravity" )
		{
            theMesh->toggleGravity();
		}
        else if ( m.getAddress() == "resetPositions" )
		{
            theMesh->resetPositions();
		}
        else if ( m.getAddress() == "resetVelocities" )
		{
            theMesh->resetVelocities();
		}

        
        else if ( m.getAddress() == "reset" )
		{
            theMesh->resetPositions();
            theMesh->resetVelocities();
            theMesh->update(); 
            
		}
        else if ( m.getAddress() == "deleteMesh" )
		{
            deleteMesh();
		}
        else if ( m.getAddress() == "regenerate" )
		{
            // ignore it - the mesh is not deleted
            
            
		}
        else if ( m.getAddress() == "zeroRestLength" )
		{
            theMesh->zeroRestLength();
		}
        else if ( m.getAddress() == "setThisRestLength" )
		{
            theMesh->setRestLength();
		}
        else if ( m.getAddress() == "setZeroAudioLine" )
		{
            theMesh->setZeroAudioLine();
		}
        else if ( m.getAddress() == "speedLimit" )
		{
            for ( int i=0; i<m.getNumArgs(); i++ )
			{
                if( m.getArgType( i ) == OFXOSC_TYPE_FLOAT ){
                    globalForces.speedLimit = m.getArgAsFloat(i);
                }
            }
		}
        else if ( m.getAddress() == "gravityAmt" )
		{
            for ( int i=0; i<m.getNumArgs(); i++ )
			{
                if( m.getArgType( i ) == OFXOSC_TYPE_FLOAT ){
                    globalForces.gravityAmt = m.getArgAsFloat(i);
                }
            }
		}
        else if ( m.getAddress() == "pressureAmt" )
		{
            for ( int i=0; i<m.getNumArgs(); i++ )
			{
                if( m.getArgType( i ) == OFXOSC_TYPE_FLOAT ){
                    globalForces.pressureAmt = m.getArgAsFloat(i);
                }
            }
		}    
        else if ( m.getAddress() == "wallBounce" )
		{
            for ( int i=0; i<m.getNumArgs(); i++ )
			{
                if( m.getArgType( i ) == OFXOSC_TYPE_FLOAT ){
                    //params::wallBounce = m.getArgAsFloat(i);
                }
            }
		}
        else if ( m.getAddress() == "toggleSyrup" )
		{
            theMesh->toggleSyrup();
        }
        else if ( m.getAddress() == "toggleAudio" )
		{
            if(audioOn){
                ofSoundStreamStop();
                audioOn = false;
            }else{
                restartAudioStream();
            }
        
		}       

        else if ( m.getAddress() == "constrainEdges" )
		{
            theMesh->constrain(.0,.0,Mesh::CONSTRAIN_EDGES);
		}
        else if ( m.getAddress() == "constrainCorners" )
		{
            theMesh->constrain(.0,.0,Mesh::CONSTRAIN_CORNERS    );
		}
        else if ( m.getAddress() == "unconstrain" )
        {
            theMesh->unconstrain();
        }
		else if ( m.getAddress() == "mass" )
		{
			for ( int i=0; i<m.getNumArgs(); i++ )
			{
                if( m.getArgType( i ) == OFXOSC_TYPE_INT32 ){
                    theMesh->setMass(m.getArgAsInt32( i ));
                }
                else if( m.getArgType( i ) == OFXOSC_TYPE_FLOAT )
                {
                    theMesh->setMass(m.getArgAsFloat( i )<0.01?0.01:m.getArgAsFloat( i ) ); 
                }
                
            }
		}
		else if ( m.getAddress() == "springk" )
		{
			for ( int i=0; i<m.getNumArgs(); i++ )
			{
                if( m.getArgType( i ) == OFXOSC_TYPE_INT32 ){
                    theMesh->setSpringConstant(m.getArgAsInt32( i ));
                }
                else if( m.getArgType( i ) == OFXOSC_TYPE_FLOAT )
                {
                    theMesh->setSpringConstant(m.getArgAsFloat( i )<0.01?0.01:m.getArgAsFloat( i ) ); 
                }
                
            }
		}
		else if ( m.getAddress() == "friction" )
		{
			for ( int i=0; i<m.getNumArgs(); i++ )
			{
                if( m.getArgType( i ) == OFXOSC_TYPE_INT32 ){
                    theMesh->setFriction(m.getArgAsInt32( i ));
                }
                else if( m.getArgType( i ) == OFXOSC_TYPE_FLOAT )
                {
                    theMesh->setFriction(m.getArgAsFloat( i )<0.01?0.01:m.getArgAsFloat( i ) ); 
                }
                
            }
		}
        //////////////// asym
        else if ( m.getAddress() == "massAsym" )
		{
			for ( int i=0; i<m.getNumArgs(); i++ )
			{
                if( m.getArgType( i ) == OFXOSC_TYPE_FLOAT )
                {
                    theMesh->setMassAsym(m.getArgAsFloat( i )<0.01?0.01:m.getArgAsFloat( i ) ); 
                }
                
            }
		}
		else if ( m.getAddress() == "springkAsym" )
		{
			for ( int i=0; i<m.getNumArgs(); i++ )
			{
                if( m.getArgType( i ) == OFXOSC_TYPE_FLOAT )
                {
                    theMesh->setSpringConstantAsym(m.getArgAsFloat( i )<0.00?0.00:m.getArgAsFloat( i ) ); 
                }
                
            }
		}
		else if ( m.getAddress() == "frictionAsym" )
		{
			for ( int i=0; i<m.getNumArgs(); i++ )
			{
                if( m.getArgType( i ) == OFXOSC_TYPE_FLOAT )
                {
                    theMesh->setFrictionAsym(m.getArgAsFloat( i )<0.01?0.01:m.getArgAsFloat( i ) ); 
                }
                
            }
		}
        else if ( m.getAddress() == "volume" )
		{
			for ( int i=0; i<m.getNumArgs(); i++ )
			{
                if( m.getArgType( i ) == OFXOSC_TYPE_FLOAT )
                {
                    cout << "change volume\n";
                    globalForces.volume = m.getArgAsFloat( i ); 
                }
                
            }
		}
        ///////////////////////
		else
		{
			// unrecognized message: display on the bottom of the screen
			string msg_string;
			msg_string = m.getAddress();
			msg_string += ": ";
			for ( int i=0; i<m.getNumArgs(); i++ )
			{
				// get the argument type
				msg_string += m.getArgTypeName( i );
				msg_string += ":";
				// display the argument - make sure we get the right type
				if( m.getArgType( i ) == OFXOSC_TYPE_INT32 )
                    pitch = m.getArgAsInt32( i );
				else if( m.getArgType( i ) == OFXOSC_TYPE_FLOAT )
					msg_string += ofToString( m.getArgAsFloat( i ) );
				else if( m.getArgType( i ) == OFXOSC_TYPE_STRING )
					msg_string += m.getArgAsString( i );
				else
					msg_string += "unknown";
			}
			// add to the list of strings to display
			msg_strings[current_msg_string] = msg_string;
			timers[current_msg_string] = ofGetElapsedTimef() + 5.0f;
			current_msg_string = ( current_msg_string + 1 ) % NUM_MSG_STRINGS;
			// clear the next line
			msg_strings[current_msg_string] = "";
		}
        
	}
}
