#include "testApp.h"


extern Grid theGridView;
extern PresetManager presetManager;
extern EventLogger eventLogger;
extern Frequencer frequencer;
extern Hilbert hilbert;

const vector<string> parameterNames;

const string sliderParamNames[10] = {"Transpose", "1/4 note","1/6 note","1/7 note","1/8 note","Waveform", "Filter Type","Filter Freq", "Envelope","FM amt"};
//const vector<const string> v(ra[0],ra[1]);



//DeviceID3523537000
void testApp::initialiseVariables(){
    paused = true;
    sendMIDIAndOSC = false;
    
    ofBackground( 0, 0, 0 );
    ofEnableAlphaBlending();
    currentSequence = 0;
    //ofEnableSmoothing();
    
	// open an outgoing connection to HOST:PORT for OSC
    
	sender.setup( OSC_HOST, OSC_PORT );
    ofSetFrameRate(50);
    // reciever
    lastMoveTime = ofGetSystemTimeMicros();
    prevTouchX = 0;
    prevTouchY = 0;
    
    xLocked = false;
    yLocked = false;
    
    numActiveTouches = 0;
    touch0.setCoord(17./7., 2./3.);
    touch1.setCoord(10,20);
    
    prevTouch0.setCoord(1,2);
    prevTouch1.setCoord(10,20);
    prevDist = 10;
    slowFactor = 0.98;
    preventingMovePostScroll = false;
    
    // the 5 harmonics for the frequencer
    freqIndexes.push_back(0);
    freqIndexes.push_back(4);
    freqIndexes.push_back(6);
    freqIndexes.push_back(7);
    freqIndexes.push_back(8);
    
    
    ofxiPhoneSetOrientation( OFXIPHONE_ORIENTATION_PORTRAIT );
    ofxiPhoneExternalDisplay::mirrorOn();
}
void testApp::initialiseGUIs(){
    theGridView.init();
    
    setupSliderGui();
    sliderGUI->setVisible(true);
    
    // initial slider vals
    for(int i=0; i<10;i++){
        sliderVals.push_back(64);
    }
    
    
    // set up iOS gui stuff
    bottomTabViewController	= [[BottomTabViewController alloc] initWithNibName:@"BottomTabViewController" bundle:nil];
    [ofxiPhoneGetGLParentView() addSubview:bottomTabViewController.view];
	
    [bottomTabViewController setAppRef:(id)this];
    bottomTabViewController.view.frame = CGRectMake(0,getHeight()-44,getWidth(),44);
    bottomTabViewController.view.hidden = YES;
    
    /////
    
    topButtonViewController	= [[TopButtonViewController alloc] initWithNibName:@"TopButtonViewController" bundle:nil];
    [ofxiPhoneGetGLParentView() addSubview:topButtonViewController.view];
    [topButtonViewController setAppRef:(id)this];
    [topButtonViewController show:(id)this];
    topButtonViewController.view.frame = CGRectMake(0,0,getWidth(),44);
    
    usernameAlertViewController = [[UsernameAlertViewController alloc] init];
    [usernameAlertViewController setAppRef:(id)this];
    
    helpViewController	= [[HelpViewController alloc] initWithNibName:@"HelpViewController" bundle:nil];
    [ofxiPhoneGetGLParentView() addSubview:helpViewController.view];
    [helpViewController setAppRef:(id)this];
    helpViewController.view.hidden = YES;
    
    /*
     sliderViewController	= [[SliderViewController alloc] initWithNibName:@"SliderViewController" bundle:nil];
     [ofxiPhoneGetGLParentView() addSubview:sliderViewController.view];
     [sliderViewController setAppRef:(id)this];
     sliderViewController.view.frame = CGRectMake(0,getHeight()-43 - 363,getWidth(),363);
     //[sliderViewController show:(id)this];
     */
    
    setAllGUISliders(theGridView.getParams());
    
}

void testApp::initialiseMIDI(){
    
    /////////////////////////
    // MIDI
    
    midiChannel = 7;
    midiOffset = 0;
	
	// enables the network midi session between iOS and Mac OSX on a
	// local wifi network
	//
	// in ofxMidi: open the input/outport network ports named "Session 1"
	//
	// on OSX: use the Audio MIDI Setup Utility to connect to the iOS device
	//
	ofxMidi::enableNetworking();
	
	// list the number of available input & output ports
	ofxMidiIn::listPorts();
	ofxMidiOut::listPorts();
	
	// create and open input ports
	for(int i = 0; i < ofxMidiIn::getNumPorts(); ++i) {
		
		// new object
		inputs.push_back(new ofxMidiIn);
		
		// set this class to receive incoming midi events
		inputs[i]->addListener(this);
		
		// open input port via port number
		inputs[i]->openPort(i);
	}
	
	// create and open output ports
	for(int i = 0; i < ofxMidiOut::getNumPorts(); ++i) {
		
		// new object
		outputs.push_back(new ofxMidiOut);
		
		// open input port via port number
		outputs[i]->openPort(i);
	}
	
	// set this class to receieve midi device (dis)connection events
	ofxMidi::setConnectionListener(this);
    
    // END MIDI

}
//--------------------------------------------------------------
void testApp::setup(){
    
    // initilaise
    
    initialiseVariables();
    initialiseGUIs();
       
    // initialise PD
    
	int ticksPerBuffer = 8;	// 8 * 64 = buffer len of 512
	core.setup(2, 1, 44100, ticksPerBuffer);
    
	// setup OF sound stream
	ofSoundStreamSetup(2, 1, this, 44100, ofxPd::blockSize()*ticksPerBuffer, 3);

    tsc = [[TimedSessionController alloc] init];
    [tsc setAppRef:(id)this];
    
    //--------------------------------------
    // load stuff
    loadSequences();
    
    // load presets
    presetManager.startLoadAll();

    eventLogger.startLoadAll();
    //--------------------------------------

   // now do things that will affect the start up state of the app
    
    if(eventLogger.questionnaireCompleted){ // then we go into do-what-you-like mode

        freeUseMode();
    }else{
        // then we're in timed session mode
        showIntro();
    }
    
    // GO
    paused = false;
    eventLogger.logEvent(APP_STARTED);
}


//--------------------------------------------------------------
void testApp::exit(){
    
    presetManager.exitAndSaveAll();
    eventLogger.exitAndSave();
    
    core.exit();
    
    // are these handled automatically?
    //[introViewController release];
    //[topButtonViewController release];
    //[bottomTabViewController release];
    
    // clean up MIDI
	for(int i = 0; i < inputs.size(); ++i) {
		inputs[i]->closePort();
		inputs[i]->removeListener(this);
		delete inputs[i];
	}
    
	for(int i = 0; i < outputs.size(); ++i) {
		outputs[i]->closePort();
		delete outputs[i];
	}
    
    delete sliderGUI;
    
    cout << "exit done \n";
}

//--------------------------------------------------------------
#pragma mark GUI
//--
float testApp::getWidth(){
    // depends on orientation
    return ofGetWidth();
    
}
float testApp::getHeight(){
    // depends on orientation
    return ofGetHeight();
    
}
#pragma mark GUI
////////////////////////////
// These functions called from iOS toolbars
//--------------------------------------------------------------
void testApp::lockSequencerPressed(bool locked){
    theGridView.shiftCentreToSnapped();
    xLocked = locked;
    eventLogger.logEvent(SEQ_LOCKED); // TODO whatabout unlock?
    
    
}
//--------------------------------------------------------------
void testApp::lockSynthPressed(bool locked){
    theGridView.shiftCentreToSnapped();
    yLocked = locked;
    eventLogger.logEvent(SYNTH_LOCKED);
    
}
//--------------------------------------------------------------
void testApp::seqStartStop(bool go){
    if(!go){ //stop
        core.pd.startMessage();
        core.pd.addFloat(0);
        core.pd.finishMessage("fromOF", "seqStartStop");
        eventLogger.logEvent(PAUSE_PRESSED);
    }else { // play
        //stopSequencer();
        core.pd.startMessage();
        core.pd.addFloat(1);
        core.pd.finishMessage("fromOF", "seqStartStop");
        eventLogger.logEvent(PLAY_PRESSED);
    }
}
//--------------------------------------------------------------
void testApp::showQuestionnaire(){
    // stop updating / drawing
    
    // if(eventLogger.questionnaireCompleted) return;
    
    [topButtonViewController pausePressed:(id)this];
    
    //stopSequencer
    seqStartStop(false);
    
    questionnaireViewController	= [[QuestionnaireViewController alloc] initWithNibName:@"QuestionnaireViewController" bundle:nil];
    [ofxiPhoneGetGLParentView() addSubview:questionnaireViewController.view];
	
    [questionnaireViewController setAppRef:(id)this];
    [questionnaireViewController show:(id)this];
    
    whichInterfaceShowing = QUESTIONNAIRE;
    
    
}
//--------------------------------------------------------------
void testApp::questionnaireHidden(vector<int> answers, const char* userComments){
    // send answers to server as json
    eventLogger.questionnaireAnswersObtained(answers, userComments);

    freeUseMode();
    
}
//--------------------------------------------------------------
// shortcut function for testing
void testApp::justStart(){
    freeUseMode();
}
//--------------------------------------------------------------
void testApp::freeUseMode(){
    interfaceSelected(1);
    [bottomTabViewController show:(id)this withSelection:1];
    
    [topButtonViewController enableSmoothSwitch:(id)this];
    
    sendMIDIAndOSC = true;
}
//--------------------------------------------------------------
void testApp::setInterp(int state){
    if(state == 0){
        theGridView.setInterpolation(Grid::NO_INTERPOLATION);
        eventLogger.logEvent(SMOOTHING_OFF);
    }else if(state == 1){
        theGridView.setInterpolation(Grid::INTERPOLATE_GRID);
        eventLogger.logEvent(SMOOTHING_ON);
    }
    // tell sliders and PD
    setAllGUISliders(theGridView.getParams());
    
}
//--------------------------------------------------------------
void testApp::showIntro(){
    
    cout << "SHOW INTRO\n";
    
    [tsc cancelTimers];
    bottomTabViewController.view.hidden = YES;
    introViewController	= [[IntroViewController alloc] initWithNibName:@"IntroViewController" bundle:nil];
    [ofxiPhoneGetGLParentView() addSubview:introViewController.view];
	
    [introViewController setAppRef:(id)this];
    [introViewController show:(id)this];
    
    
    whichInterfaceShowing = INTRO;
    
}
//--------------------------------------------------------------
void testApp::introHidden(bool OK){
    if(OK){
        eventLogger.consentGiven = true;
        //
        // clear presets?
        presetManager.clearAll();
        // show username prompt
        [usernameAlertViewController showUserNamePrompt];
        // reset top buttons
        [topButtonViewController unlockAll];
        randomise();
    }
    // no unOK
}
//--------------------------------------------------------------
void testApp::startTimedSession(){
    [tsc startTimer];
    // timer will show alert and set interface
}
//--------------------------------------------------------------
// called from BottomTabViewController iOS segmented thing, also timed session controller
void testApp::interfaceSelected(int which){
    switch (which){
        case 0: // slider
            
            whichInterfaceShowing = SLIDERS;
            sliderGUI->setVisible(true);
            // set the slider values to stuff got from zoomer
            sliderVals = theGridView.getParams();
            setAllGUISliders(sliderVals);
            
            break;
        case 1: // both
            
            whichInterfaceShowing = BOTH;
            sliderGUI->setVisible(true);
            // set the slider values to stuff got from zoomer
            sliderVals = theGridView.getParams();
            setAllGUISliders(sliderVals);
            
            break;
        case 2: // zoomer
            
            sliderGUI->setVisible(false);
            whichInterfaceShowing = ZOOMER;
            break;
    }
    eventLogger.logEvent(SWAP_VIEW,theGridView.getCoord(),theGridView.getScale(), which);
}
//--------------------------------------------------------------
//--------------------------------------------------------------
void testApp::setupSliderGui(){

    float length = SLIDER_GUI_WIDTH - (OFX_UI_GLOBAL_WIDGET_SPACING*2);
    
    
    //float dim = 42;
    
    // make this iphone size...?
    int height = 480;

    float dim = (height-10.0*OFX_UI_GLOBAL_WIDGET_SPACING)/10.0;
    // LEFT GUI
    sliderGUI = new ofxUICanvas(10,160,SLIDER_GUI_WIDTH,getHeight());
    
    // Uh.. loop this
    for(int i = 1; i<=10;i++){
        
        ofxUISlider *slider;
        slider = (ofxUISlider *)sliderGUI->addWidgetDown(new ofxUISlider(length,dim,0.0,127,64,sliderParamNames[i-1]));
        slider->setDrawPadding(true);
        slider->setDrawPaddingOutline(true);
        if(i <= 5){
            slider->setColorFill(ofColor(0,0,255));
            slider->setColorFillHighlight(ofColor(0,0,255));
        }else{
            slider->setColorFill(ofColor(255,0,0));
            slider->setColorFillHighlight(ofColor(255,0,0));
        }
        
        sliders.push_back(slider);
    }
    
    
    
    ofAddListener(sliderGUI->newGUIEvent, this, &testApp::sliderGUIEvent);
    
}
//--------------------------------------------------------------
void testApp::sliderGUIEvent(ofxUIEventArgs &e){
    if(whichInterfaceShowing == ZOOMER){
        cout << "GUI ERROR";
        return;
    }
    
    // "normal" parameter changes
    for(int i = 1; i<=10;i++){
        
        if(e.widget->getName() == sliderParamNames[i-1])
        {
            //cout << "param change: " << p;
            ofxUISlider *slider = (ofxUISlider *) e.widget;
            sliderMoved(i-1,slider->getScaledValue()); // internal array 0 indexed
        }
    }
    
}
//--------------------------------------------------------------
void testApp::sliderMoved(int which, float value){
    // an update caused by slider view being touched
    sliderVals[which] = (int)value;
    theGridView.setParams(sliderVals);
    
    sendParametersToPD();
    if(sendMIDIAndOSC){
        
        sendMidiParam(which);
        sendOSCParams();
    }
    
    eventLogger.logEvent(CHANGE_SLIDER, TwoVector(),0.0,which , value);
    
}
//--------------------------------------------------------------
void testApp::setAllGUISliders(vector<int> vals){
    // an update caused by zoomer view being moved
    for(int i = 0; i<NUM_PARAMS;i++){
        sliders[i]->setValue(vals[i]);
        sliderVals[i] = vals[i];
    }
    
    sendParametersToPD();
    if(sendMIDIAndOSC){
        
        sendMidiParams();
        sendOSCParams();
    }
    
}
//--------------------------------------------------------------
void testApp::randomise(){
    // pick random settings for all params
    
    // random zoom? no.
    for(int i=0; i < NUM_PARAMS ; i++){
        sliderVals[i] = ofRandom(0, 127);
        
    }
    currentSequence = ofRandom(0, sequences.size()-1);
    // send to grid, sliders and PD
    theGridView.setParams(sliderVals);
    setAllGUISliders(sliderVals);


    eventLogger.logEvent(RANDOMISE, theGridView.getCoord() ,0.0);
    
}
//--------------------------------------------------------------
void testApp::showHelp(){
    whichInterfaceShowing = HELP;
    seqStartStop(false);
    [topButtonViewController pausePressed:(id)this];
    helpViewController.view.hidden = NO;
    eventLogger.logEvent(HELP_PRESSED);
}
void testApp::helpHidden(){
    whichInterfaceShowing = BOTH;
    // start seq?
    
}
//--------------------------------------------------------------
void testApp::nextSequence(){
    currentSequence++;
    if(currentSequence >= sequences.size()){
        currentSequence = 0;
    }
    sendParametersToPD();
}
//--------------------------------------------------------------
//--------------------------------------------------------------
#pragma mark sending to pd and midi
void testApp::sendParametersToPD(){
    static int previousSequence;
    // frequencer stuff to get 16 steps
    
     vector<double> vals;
     
     
     vals.push_back((sliderVals[0]+32)*8.); // DC offset
     for(int i=1; i<5;i++){
     vals.push_back((sliderVals[i] - 64)*2.);
     }
    
    vector<double> steps = frequencer.freqMagEdit(freqIndexes, vals);
    List seqSteps;
    seqSteps.addSymbol("seqSteps");
    for(int i=0; i < 16; i++){
        seqSteps.addFloat(round(steps[i]));
    }
    core.pd.sendList("fromOF", seqSteps);

    /*
    if(currentSequence != previousSequence){
        List seqSteps;
        
        seqSteps.addSymbol("seqSteps");
        if(currentSequence >= sequences.size() || currentSequence < 0){
            cout << "ERROR: not a valid sequence index\n";
            for(int i=0; i < 16; i++){
                seqSteps.addFloat(50);
            }
        }else{
            for(int i=0; i < 16; i++){
                seqSteps.addFloat(round(sequences[currentSequence][i]));
            }
        }
        
        core.pd.sendList("fromOF", seqSteps);
        previousSequence = currentSequence;
    }
     */
    
    // send synth params
    sendOscShape(sliderVals[5]);
    sendFiltType(sliderVals[6]);
    sendFiltFreq(sliderVals[7]);
    sendEnvShape(sliderVals[8]);
    sendModFreq(sliderVals[9]);

    
}
//--------------------------------------------------------------
void testApp::sendMidiParam(int which){
    int midiChannel = 7;
    int offset = 0;
    
    for(int i = 0; i < outputs.size(); ++i) {
        outputs[i]->sendControlChange(midiChannel, offset+which, sliderVals[which]);
    }
    
    
}
//--------------------------------------------------------------
void testApp::sendMidiParams(){

    for(int i = 0; i< sliderVals.size(); i++){
       	for(int j = 0; j < outputs.size(); ++j) {
            outputs[j]->sendControlChange(midiChannel, midiOffset+i, sliderVals[i]);
        }
    }

}
//--------------------------------------------------------------
void testApp::sendOSCParams(){
       
    ofxOscMessage m;
    m.setAddress( "sonicZoom" );
    
    for(int i = 0; i< sliderVals.size(); i++){
        
        m.addFloatArg(sliderVals[i]);
        
    }
    sender.sendMessage( m );
}
//--------------------------------------------------------------

void testApp::setupNewUser(){
    // this function is for supervised trials with my ipad
    eventLogger.newUser();
}
//--------------------------------------------------------------
#pragma mark STANDARD OF FUNCTIONS
//--------------------------------------------------------------
void testApp::update(){
    
    if(paused) return;
    
    if(ofxiPhoneExternalDisplay::isExternalScreenConnected()){
        if(!ofxiPhoneExternalDisplay::isMirroring()){
            ofxiPhoneExternalDisplay::mirrorOn();
            printf("turned on Mirroring!\n");
        }
    }
    
    // continiue to move or zoom at velocity, unless snapped
    
    if (numActiveTouches == 0){ // no touches, use momentum
        // ZOOM MOMENTUM
        // continiue to zoom at velocity
        if (numActiveTouches < 2 && abs(zoomVel)>0.001){
            theGridView.zoom(zoomVel + 1.0); // +1 because zoomVel factor is + or - , wheras zoom is a multiplier near 1
            zoomVel = zoomVel*slowFactor;
            moveVel.setCoord(0.0,0.0);; // don't move if zooming! Too many events!
            
            setAllGUISliders(theGridView.getParams());

        }
        
        if(moveVel.norm() > 0.3){
            if(theGridView.snapped){
                // stop it (snap check sends snap event)
                moveVel.setCoord(0.0,0.0);
            }else{
                theGridView.move(moveVel);
                moveVel = moveVel*slowFactor;
            }
            // and get new parameter values
            setAllGUISliders(theGridView.getParams());

        }else if(moveVel.norm() > 0.01){ // and less than 0.3
            // stop it
            moveVel.setCoord(0.0,0.0);
            setAllGUISliders(theGridView.getParams());

            eventLogger.logEvent(SCROLL_STOPPED, theGridView.getCoord() );
            
        }else{
            // stopped - do nothing
        }
        
    }
    
}

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

void testApp::draw(){
    
    switch (whichInterfaceShowing){
        case SLIDERS:
            break;
        case ZOOMER:
            theGridView.draw();
            
            break;
        case BOTH:
            
            theGridView.draw();
            
            break;
        case INTRO:
            break;
            
        case QUESTIONNAIRE:
            break;
            
    }
    
}

//--------------------------------------------------------------
void testApp::touchDown(ofTouchEventArgs &touch){
    // in slider gui area?
    if(whichInterfaceShowing == SLIDERS){
        return;
    }else if (whichInterfaceShowing == BOTH && touch.x < (SLIDER_GUI_WIDTH+10)){
        return;
        
    }// otherwise we're good to let the zoomer handle touch
    
    numActiveTouches++;
    preventingMovePostScroll = false;
    
    if(touch.id == 0){
        touch0.setCoord(touch.x,touch.y);
        prevTouch0 = touch0;
        // stop zoom
        zoomVel = 0.0;
    }else if(touch.id == 1){
        
        touch1.setCoord(touch.x,touch.y);
        prevTouch1 = touch1;
        
    }
    if(numActiveTouches == 1){
        moveVel.setCoord(0.0, 0.0);
        prevMove.setCoord(0.0, 0.0);
        prevMove2.setCoord(0.0, 0.0);
        
    }else if(numActiveTouches == 2){
        zoomVel = 0.0;
        prevZoom = 0.0;
        prevZoom2 = 0.0;
        double dist = touch1.distanceTo(touch0);
        prevDist = dist;
    }
    
}

//--------------------------------------------------------------
void testApp::touchMoved(ofTouchEventArgs &touch){
    
    // TODO check if in gui area!!!
    if(whichInterfaceShowing == SLIDERS){
        return;
    }else if (whichInterfaceShowing == BOTH && touch.x < (SLIDER_GUI_WIDTH+10)){
        return;
        
    }
    
    // otherwise we're good to let the zoomer handle touch
    
    // which one? keep track of each touch point
    if(touch.id == 0){
        touch0.setCoord(touch.x,touch.y);
        
    }else if(touch.id == 1){
        
        touch1.setCoord(touch.x,touch.y);
    }
    
    if(numActiveTouches == 1){
        if(preventingMovePostScroll) return;
        handleScroll();
    }else if(numActiveTouches == 2){
        handleZoom();
        
    }
    prevTouch0 = touch0;
    
    
}

//--------------------------------------------------------------
// handle a finger being dragged
void testApp::handleScroll(){
    
    TwoVector move = touch0 - prevTouch0;
    if(yLocked){
        move.y = 0.0;
    }
    if(xLocked){
        move.x = 0.0;
    }
    
    // check time since last move - if
    unsigned int moveTime = ofGetSystemTimeMicros();
    if(moveTime - lastMoveTime > 100000){
        moveVel = TwoVector(); // zero
    }else{
        moveVel = (move*0.3 + prevMove*0.34 + prevMove2*0.38); // use the time
        
    }
    lastMoveTime = moveTime;
    
    
    prevMove2 = prevMove;
    prevMove = move;
    
    
    theGridView.move(move);
    
    // and get new parameter values
    setAllGUISliders(theGridView.getParams());

}
//--------------------------------------------------------------
// handle pinch movememnt
void testApp::handleZoom(){
    // work out change in difference
    double dist = touch1.distanceTo(touch0);
    double zoomFactor = prevDist/dist;
    
    //TODO check for sensible maximums, e.g. spurious touch data
    if(zoomFactor > 2.0 || zoomFactor < 0.5){
        cout << "Zoom too much!!!!" << zoomFactor;
        zoomFactor = 1.0;
    }
    
    zoomVel = (zoomFactor-1)*0.3 + prevZoom*0.34 + prevZoom2*0.38;
    prevZoom2 = prevZoom;
    prevZoom = (zoomFactor-1);
    
    theGridView.zoom(zoomFactor);
    
    prevDist = dist;
    

    setAllGUISliders(theGridView.getParams());
    

    
}
//--------------------------------------------------------------
void testApp::touchUp(ofTouchEventArgs &touch){
    if(numActiveTouches > 0) numActiveTouches--; // dirty
    preventingMovePostScroll = false;
    // TODO check if in gui area!!!
    if(whichInterfaceShowing == SLIDERS){
        return;
    }else if (whichInterfaceShowing == BOTH && touch.x < 256){
        return;
        
    }// otherwise we're good to let the zoomer handle touch
    
    
    // which one?
    if(touch.id == 0){
        // tricky situation - we tried to zoom but may have left non-move finger on
        prevTouch0.setCoord(touch.x,touch.y);
        
    }else if(touch.id == 1){
        
        
        prevTouch1.setCoord(0,0);
        
    }
    if(numActiveTouches == 0){
        // check time since last move
        // check time since last move - if
        unsigned int moveTime = ofGetSystemTimeMicros();
        if(moveTime - lastMoveTime > 100000){
            moveVel = TwoVector(); // zero
        }else{
            moveVel = (move*0.3 + prevMove*0.34 + prevMove2*0.38); // use the time
            
        }
        lastMoveTime = moveTime;
    }else if (numActiveTouches == 1){
        // just zoomed , but now lifted one of the fingers
        // can be bad if moved so create special mode to stop scroll  (special modes bad!)
        preventingMovePostScroll = true;
    }
    
}

//--------------------------------------------------------------
void testApp::touchDoubleTap(ofTouchEventArgs &touch){
    // preset?
    
    /* ballses everything for some reason
     TwoVector centre = TwoVector(getWidth()*0.5,getHeight()*0.5);
     // if near centre
     if((touch.x < centre.x+10) && (touch.x > centre.x-10) && (touch.y < centre.y+10) && (touch.y > centre.y-10)){
     numActiveTouches = 0; // dirty
     presetManager.showNameDialog();
     
     }
     */
    
}

//--------------------------------------------------------------
void testApp::lostFocus(){
    
}

//--------------------------------------------------------------
void testApp::gotFocus(){
    
}

//--------------------------------------------------------------
void testApp::gotMemoryWarning(){
    
}

//--------------------------------------------------------------
void testApp::deviceOrientationChanged(int newOrientation){
    /*
     cout << "orientation: " << newOrientation;
     
     if(newOrientation == 4){
     ofxiPhoneSetOrientation( OF_ORIENTATION_DEFAULT );
     
     }else if(newOrientation == 3){
     ofxiPhoneSetOrientation( OF_ORIENTATION_90_LEFT );
     }else if(newOrientation == 3){
     ofxiPhoneSetOrientation( OF_ORIENTATION_90_LEFT );
     }
     
     
     [ofxiPhoneGetGLView() updateDimensions];
     */
}


//--------------------------------------------------------------
void testApp::touchCancelled(ofTouchEventArgs& args){
    
}
//---------------------------------------------------------------
// AUDIO STUFF
//---------------------------------------------------------------

#pragma mark AUDIO STREAMS
//--------------------------------------------------------------
void testApp::audioReceived(float * input, int bufferSize, int nChannels) {
	core.audioReceived(input, bufferSize, nChannels);
}

void testApp::audioRequested(float * output, int bufferSize, int nChannels) {
	core.audioRequested(output, bufferSize, nChannels);
}
//---------------------------------------------------------------
#pragma mark UTILITIES

// 5hz cut off
const double fB[3] = {0.049489956268677,   0.098979912537354,   0.049489956268677};

const double fA[3] = {1.000000000000000,  -1.279632424997809,   0.477592250072517};

// 1hz cut off
//const double fB[3] = {0.002550535158536,   0.005101070317073,   0.002550535158536};

//const double fA[3] = {1.000000000000000,  -1.852146485395936,   0.862348626030081};


//a(1)*y(n) = b(1)*x(n) + b(2)*x(n-1) + ... + b(nb+1)*x(n-nb)- a(2)*y(n-1) - ... - a(na+1)*y(n-na)
//---------------------------------------------------------------
vector<float> testApp::vectorFilter(vector<float> newVec){
    static vector<float> x0(10,0);
    static vector<float> x1(10,0);
    static vector<float> x2(10,0);
    static vector<float> y0(10,0);
    static vector<float> y1(10,0);
    static vector<float> y2(10,0);
    
    x0 = newVec;
    
    // this low passes a bunch of params values all at once
    int sz =  newVec.size();
    for(int i=0; i<sz; i++){
        y0[i] = fB[0]*x0[i] + fB[1]*x1[i] + fB[2]*x2[i] - fA[1]*y1[i] - fA[2]*y2[i];
    }
    // shift
    x2 = x1;
    x1 = x0;
    y2 = y1;
    y1 = y0;
    
    return y0;
}
//---------------------------------------------------------------
float ctrlSmoother(float newsamp){
    static float x1,x2,y1,y2;
    float x0, y0;
    
    x0 = newsamp;
    
    y0 = fB[0]*x0 + fB[1]*x1 + fB[2]*x2 - fA[1]*y1 - fA[2]*y2;
    
    // shift
    x2 = x1;
    x1 = x0;
    y2 = y1;
    y1 = y0;
    
    return y0;
}
//---------------------------------------------------------------
void testApp::sendOscShape(int ctrlin){
    if(ctrlin < 0 || ctrlin > 127){
        cout << "ERROR: bad slider value!";
        return;
    }
    
    static int numpoints = 5;
    static int numcontrols = 5;
    //float values[points][controls] =
    float ctrlout[numcontrols];
    string ctrlName[5] = {"pWidth" , "sqVol", "sawVol", "sineVol", "FMAmt"};
    float values[5][5] =
    {{0.5, 0., 0., 1., 1.}, // 0
        {0.5, 0., 0., 1., 0.},  // 32
        {0.5, 0., 1., 0., 0.},  // 64
        {0.5, 1., 1., 0., 0.},  // 96
        {0.01,1., 1., 0., 0.}}; // 127
    
    float fidx = (numpoints-1)*ctrlin/128.;
    int idx = floor(fidx);
    float frac = fidx - idx;
    for(int i=0; i < numcontrols; i++){
        ctrlout[i] = (1 - frac)*values[idx][i] + (frac)*values[idx+1][i];
        // send to PD
        List toPD;
        
        toPD.addSymbol(ctrlName[i]);
        toPD.addFloat(ctrlout[i]); // rounding here??
        
        core.pd.sendList("fromOF", toPD);
        //cout << ctrlName[i] << "sending" << ctrlout[i] << "\n";
    }
    
}
//---------------------------------------------------------------
void testApp::sendFiltType(int ctrlin){
    if(ctrlin < 0 || ctrlin > 127){
        cout << "ERROR: bad slider value!";
        return;
    }
    static int numpoints = 3;
    static int numcontrols = 4;
    //float values[points][controls] =
    float ctrlout[numcontrols];
    string ctrlName[4] = {"lpLev" , "bpLev", "hpLev", "reson"};
    float values[3][4] =
    {{2., 0., 0., 1.}, // 0
        {0., 10., 0., 10.},  // 64
        {0., 0., 1., 1.}}; // 127
    
    float fidx = (numpoints-1)*ctrlin/128.;
    int idx = floor(fidx);
    float frac = fidx - idx;
    for(int i=0; i < numcontrols; i++){
        ctrlout[i] = (1 - frac)*values[idx][i] + (frac)*values[idx+1][i];
        // send to PD
        List toPD;
        
        toPD.addSymbol(ctrlName[i]);
        toPD.addFloat(ctrlout[i]); // rounding here??
        
        core.pd.sendList("fromOF", toPD);
        //cout << ctrlName[i] << "sending" << ctrlout[i] << "\n";
    }
}
//---------------------------------------------------------------
void testApp::sendFiltFreq(int ctrlin){
    if(ctrlin < 0 || ctrlin > 127){
        cout << "ERROR: bad slider value!";
        return;
    }
    // smooth this
    float fin = ctrlin;
    float fout;
    fout = (int)ctrlSmoother(fin);
    List toPD;
    
    toPD.addSymbol("filtFreq");
    toPD.addFloat(fout);
    
    core.pd.sendList("fromOF", toPD);
}
//---------------------------------------------------------------
void testApp::sendEnvShape(int ctrlin){
    if(ctrlin < 0 || ctrlin > 127){
        cout << "ERROR: bad slider value!";
        return;
    }
    static int numpoints = 5;
    static int numcontrols = 3;
    //float values[points][controls] =
    float ctrlout[numcontrols];
    string ctrlName[3] = {"attack" , "decay", "sustain"};
    float values[5][3] =
    {{0., 0., 0.}, // 0
        {0., 0.5, 0.},  // 32
        {0.0, 1., 0.8},  // 64
        {0.99, 0.3, 0.},  // 96
        {0.3, 0.1, 0.}}; // 127
    
    float fidx = (numpoints-1)*ctrlin/128.;
    int idx = floor(fidx);
    float frac = fidx - idx;
    for(int i=0; i < numcontrols; i++){
        ctrlout[i] = (1 - frac)*values[idx][i] + (frac)*values[idx+1][i];
        // send to PD
        List toPD;
        
        toPD.addSymbol(ctrlName[i]);
        toPD.addFloat(ctrlout[i]); // rounding here??
        
        core.pd.sendList("fromOF", toPD);
        //cout << ctrlName[i] << "sending" << ctrlout[i] << "\n";
    }
}
//---------------------------------------------------------------
void testApp::sendModFreq(int ctrlin){
    if(ctrlin < 0 || ctrlin > 127){
        cout << "ERROR: bad slider value!";
        return;
    }
    float fm = ctrlin/127.;
    List toPD;
    
    toPD.addSymbol("FMFreq");
    toPD.addFloat(fm); // rounding here??
    
    core.pd.sendList("fromOF", toPD);
}
//---------------------------------------------------------------



//---------------------------------------------------------------------------
void saveSequences(){
    ofFile sequenceFile(ofxiPhoneGetDocumentsDirectory() + "pilot_sequences.json" ,ofFile::WriteOnly);
    
    // the 5 harmonics for the frequencer
    vector<int> freqIndexes;
    freqIndexes.push_back(0);
    freqIndexes.push_back(4);
    freqIndexes.push_back(6);
    freqIndexes.push_back(7);
    freqIndexes.push_back(8);
    
    // loop thru all presets
    int N = presetManager.thePresets.size();
    
    vector<int> sliderVals;
    
    Json::Value root;
    
    vector<double> vals;
    
    for(int i = 0; i< N ; i++){
        sliderVals = theGridView.calculateParamsFromCoord(presetManager.thePresets[i]->coordinates);
        
        vals.push_back((sliderVals[0]+32)*8.); // DC offset
        for(int i=1; i<5;i++){
            vals.push_back((sliderVals[i] - 64)*2.);
        }
        
        
        vector<double> steps = frequencer.freqMagEdit(freqIndexes, vals);
        
        for(int j = 0; j<16;j++){
            root["sequences"][i][j] = int(steps[j]);
        }
        vals.clear();
    }
    
    //cout << root;
    sequenceFile << root;
}
//=--------------------------------------------------------------------
void testApp::loadSequences(){
    
    // read in sequence preset file
    string jsonFile = ofxiPhoneGetDocumentsDirectory() + "pilot_sequences.json";
    
    Json::Value root;
    Json::Reader reader;
    
    ifstream theFile(jsonFile.c_str());
    stringstream fileText;
    string line;
    if(!theFile){
        cout<<"can't find sequence file: " << jsonFile.c_str() << "\n";
        return;
    }else{
        
        while(theFile){
            theFile >> line;
            // cout << line << "\n"; // lots?
            fileText << line;
        }
        
        theFile.close();
    }
    
    bool parsingSuccessful = reader.parse( fileText.str(), root );
    
    if ( !parsingSuccessful )
    {
        // report to the user the failure and their locations in the document.
        std::cout  << "Failed to parse sequence JSON: \n"
        << reader.getFormattedErrorMessages();
        return;
    }
    
    // now put into variables
    const Json::Value jseqs = root["sequences"];
    int N = jseqs.size();
    for(int i=0; i<N; i++){
        sequences.push_back(vector<int>());
        for(int j=0; j<jseqs[i].size(); j++){
            sequences.back().push_back(jseqs[i][j].asInt());
        }
    }
    // currentSequence
    
    currentSequence = ofRandom(0,N-1);
    
}
//-------------------------------------------------------------------------
//--------------------------------------------------------------

#pragma mark MIDI
void testApp::addMessage(string msg) {
	cout << msg << endl;
	messages.push_back(msg);
	while(messages.size() > maxMessages)
		messages.pop_front();
}

//--------------------------------------------------------------
void testApp::newMidiMessage(ofxMidiMessage& msg) {
	//addMessage(msg.toString());
    
    // look at what it is

    if(msg.channel == midiChannel && msg.status == MIDI_CONTROL_CHANGE){
        int ctl_num = msg.control;
        int ctl_val = msg.value;
        if(ctl_num - midiOffset >= 0 && ctl_num - midiOffset < sliderVals.size()){
            sliderVals[ctl_num] = (int)ctl_val;
            sliders[ctl_num]->setValue(ctl_val);
            theGridView.setParams(sliderVals);
        }
    }
 
}

//--------------------------------------------------------------
void testApp::midiInputAdded(string name, bool isNetwork) {
	stringstream msg;
	msg << "ofxMidi: input added: " << name << " network: " << isNetwork;
    cout << msg.str();
	addMessage(msg.str());
	
	// create and open a new input port
	ofxMidiIn * newInput = new ofxMidiIn;
	newInput->openPort(name);
	newInput->addListener(this);
	inputs.push_back(newInput);
}

//--------------------------------------------------------------
void testApp::midiInputRemoved(string name, bool isNetwork) {
	stringstream msg;
	msg << "ofxMidi: input removed: " << name << " network: " << isNetwork << endl;
    cout << msg.str();
	addMessage(msg.str());
	
	// close and remove input port
	vector<ofxMidiIn*>::iterator iter;
	for(iter = inputs.begin(); iter != inputs.end(); ++iter) {
		ofxMidiIn * input = (*iter);
		if(input->getName() == name) {
			input->closePort();
			input->removeListener(this);
			delete input;
			inputs.erase(iter);
			break;
		}
	}
}

//--------------------------------------------------------------
void testApp::midiOutputAdded(string name, bool isNetwork) {
	stringstream msg;
	msg << "ofxMidi: output added: " << name << " network: " << isNetwork << endl;
    cout << msg.str();
	addMessage(msg.str());
	
	// create and open new output port
	ofxMidiOut * newOutput = new ofxMidiOut;
	newOutput->openPort(name);
	outputs.push_back(newOutput);
}

//--------------------------------------------------------------
void testApp::midiOutputRemoved(string name, bool isNetwork) {
	stringstream msg;
	msg << "ofxMidi: output removed: " << name << " network: " << isNetwork << endl;
    cout << msg.str();
	addMessage(msg.str());
	
	// close and remove output port
	vector<ofxMidiOut*>::iterator iter;
	for(iter = outputs.begin(); iter != outputs.end(); ++iter) {
		ofxMidiOut * output = (*iter);
		if(output->getName() == name) {
			output->closePort();
			delete output;
			outputs.erase(iter);
			break;
		}
	}
}
//--------------------------------------------------------------