#include "testApp.h"
#include "grid.h"
#include "presetManager.h"
#include "eventLogger.h"

extern Grid theGridView;
extern PresetManager presetManager;
extern EventLogger eventLogger;
//--------------------------------------------------------------
void testApp::setup(){
	ofSetOrientation(OF_ORIENTATION_90_LEFT);

	ofBackground( 0, 0, 0 );
    ofEnableAlphaBlending();

	// open an outgoing connection to HOST:PORT
	sender.setup( HOST, PORT );
    ofSetFrameRate(40);
    // reciever
    
    
    prevTouchX = 0;
    prevTouchY = 0;
    
    xLocked = false;
    yLocked = false;
    
    numActiveTouches = 0;
    touch0.setCoord(1,2);
    touch1.setCoord(10,20);
    prevTouch0.setCoord(1,2);
    prevTouch1.setCoord(10,20);
    prevDist = 10;
    
    theGridView.init();

    slowFactor = 0.97;
    
    setupStandardGui();
    standardGUIShowing = false;
    standardGUI->setVisible(standardGUIShowing);
    
    setupZoomGui();
    zoomGUI->setVisible(!standardGUIShowing);
    
    // initial slider vals
    for(int i=0; i<10;i++){
        sliderVals.push_back(64);
    }
    
    keyboard = new ofxiPhoneKeyboard(500,380,320,32);
	keyboard->setVisible(false);
	keyboard->setBgColor(255, 255, 255, 255);
	keyboard->setFontColor(0,0,0, 255);
	keyboard->setFontSize(26);
    
    ofxiPhoneSetOrientation( OF_ORIENTATION_90_RIGHT );
    

    
}
//--------------------------------------------------------------
void testApp::setupZoomGui(){
    zoomGUI = new ofxUICanvas(ofGetWidth()-200,0,190,125);
    zoomGUI->setTheme(OFX_UI_THEME_HACKER );
    
    ofxUIWidget *bwidge = zoomGUI->addLabelToggle("SWITCH VIEW", false);
    
    zoomGUI->addLabelButton("SAVE PRESET", false);
    
    zoomGUI->addLabelToggle("LOCK SEQUENCE (X)", false);
    zoomGUI->addLabelToggle("LOCK SYNTH (Y)", false);
    
    ofAddListener(zoomGUI->newGUIEvent, this, &testApp::zoomGUIEvent);
}
//--------------------------------------------------------------
void testApp::zoomGUIEvent(ofxUIEventArgs &e){

    if(e.widget->getName() == "SWITCH VIEW")
    {
        cout << "change VIEW\n";

        standardGUI->setVisible(!standardGUIShowing);
        standardGUIShowing = !standardGUIShowing;
        if(standardGUIShowing){
            // set the slider values to stuff got from zoomer
            sliderVals = theGridView.getParams();
            setGUISliders(sliderVals);
        }

    }else if(e.widget->getName() == "SAVE PRESET")
    {
        cout << "SAVE PRESET\n";
        // uh...
        // TwoVector preset = theGridView.getCoord();
        // presetManager.savePreset(preset);
        // just coordinate or all the dims?
        TwoVector preset = theGridView.getCoord();
        
        if(!keyboard->isKeyboardShowing()){
			keyboard->openKeyboard();
			keyboard->setVisible(true);
		} else{
			keyboard->setVisible(false);
		}
        // set some kind of modal dialog thing to stop other stuff going on...
        // this wont work
        presetManager.addPreset(preset,keyboard->getText());
    }else if(e.widget->getName() == "LOCK SEQUENCE (X)")
    {
        cout << "LOCK SEQUENCE (X)\n";
        // lock
        xLocked = !xLocked;
    }else if(e.widget->getName() == "LOCK SYNTH (Y)")
    {
        cout << "LOCK SYNTH (Y)\n";
        // lock
        
        yLocked = !yLocked;
    }else{
        cout << "GUI error :  unknown event recieved\n";
    }
    
}
//--------------------------------------------------------------
void testApp::setupStandardGui(){
    float xInit = OFX_UI_GLOBAL_WIDGET_SPACING;
    float length = 256-xInit*2;
    
    
    float dim = 42;
    
    // LEFT GUI
    standardGUI = new ofxUICanvas(0,0,length+90,ofGetHeight()-90);
    
    
    
    // Uh.. loop this
    for(int i = 1; i<=10;i++){
        stringstream ss;
        ss << "P" << i;
        ofxUISlider *slider;
        slider = (ofxUISlider *)standardGUI->addWidgetDown(new ofxUISlider(length,dim,0.0,127,64,ss.str()));
        slider->setDrawPadding(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(standardGUI->newGUIEvent, this, &testApp::standardGUIEvent);
    standardGUI->loadSettings(ofxiPhoneGetDocumentsDirectory() + "guiSettings.xml");
    
}
//--------------------------------------------------------------
void testApp::standardGUIEvent(ofxUIEventArgs &e){
    if(!standardGUIShowing){
        cout << "GUI ERROR";
        return;
    }
    // "normal" parameter changes
    for(int i = 1; i<=10;i++){
        stringstream ss;
        ss << "P" << i;
        string p = ss.str();
        
        if(e.widget->getName() == p)
        {
            //cout << "param change: " << p;
            ofxUISlider *slider = (ofxUISlider *) e.widget;
            updateSliderValue(i-1,slider->getScaledValue()); // internal array 0 indexed
            
        }
    }
    
    // TODO reflect these changes in zoomer view? Or only when switching?
    
}
//--------------------------------------------------------------
void testApp::updateSliderValue(int which, float value){
    sliderVals[which] = (int)value;
    theGridView.setParams(sliderVals);
}
//--------------------------------------------------------------
void testApp::setGUISliders(vector<int> vals){
    for(int i = 0; i<10;i++){
        sliders[i]->setValue(vals[i]);
        sliderVals[i] = vals[i];
    }
}
//--------------------------------------------------------------
void testApp::update(){
	//we do a heartbeat on iOS as the phone will shut down the network connection to save power
	//this keeps the network alive as it thinks it is being used.

    
	if( ofGetFrameNum() % 120 == 0 ){
		ofxOscMessage m;
		m.setAddress( "/misc/heartbeat" );
		m.addIntArg( ofGetFrameNum() );
		sender.sendMessage( m );
	}
    
    // continiue to move at velocity
    if (numActiveTouches == 0 && moveVel.norm() > 0.01){
        theGridView.move(moveVel.x,moveVel.y);
        moveVel = moveVel*slowFactor;
    }
    // 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;
    }
    
    // we need this?
    theGridView.update();
    vector<int> params = theGridView.getParams(); // FILTER HERE? NEED FLOATS...
    setGUISliders(params);
    
    // sendOSCParams();
    // sendMIDIParams();
}
//--------------------------------------------------------------
void testApp::sendOSCParams(){
    
    vector<int> params = theGridView.getParams(); // FILTER HERE? NEED FLOATS...
    vector<int>::iterator iter = params.begin();
    
    ofxOscMessage m;
    m.setAddress( "p" );
    
    for(;iter < params.end();iter++){
        
        m.addFloatArg( *iter );
        
    }
    sender.sendMessage( m );
}
//--------------------------------------------------------------
void testApp::draw(){

    if (standardGUIShowing){
        ofSetColor(57, 57, 57,200);
        ofRect(0,0,ofGetWidth(),ofGetHeight());
    }
    theGridView.draw();
    
    //ofSetColor(20, 160, 240, 255);
	//ofDrawBitmapString("text entered = "+  keyboard->getText() , 2, 70);
}

//--------------------------------------------------------------
void testApp::exit(){
    delete standardGUI;
    delete zoomGUI;
}

//--------------------------------------------------------------
void testApp::touchDown(ofTouchEventArgs &touch){
    if(standardGUIShowing){
        // check if in GUI area
        if(touch.x < 256) return;
    }
    
    numActiveTouches++;
    // absolute position doesn't matter
    // which one?
    if(touch.id == 0){
        touch0.setCoord(touch.x,touch.y);
        prevTouch0 = touch0;
    }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){
    
    //cout << "touch id  " << touch.id << "\n";
    //cout << "active touches  " << numActiveTouches << "\n";

    
    if(standardGUIShowing){
        // check if in GUI area
        if(touch.x < 256) return;
    }

    // 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){

        handleScroll();
    }else if(numActiveTouches == 2){
        handleZoom();
        
    }
    prevTouch0 = touch0;
   
    
}
//--------------------------------------------------------------
void testApp::handleScroll(){
    
    TwoVector move = touch0 - prevTouch0;
    moveVel = move*0.3 + prevMove*0.34 + prevMove2*0.38; // broke?
    prevMove2 = prevMove;
    prevMove = move;
    
    theGridView.move(move.x,move.y);
}
//--------------------------------------------------------------
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;
    // TODO: when num touches goes down to 1 after zoom prevTouch can be hugely different!
    // also if try to move with other finger after zoom , this is touch1 :(
    // prevTouch0 = ???
}
//--------------------------------------------------------------
void testApp::touchUp(ofTouchEventArgs &touch){
    if(numActiveTouches > 0) numActiveTouches--; // dirty
    
    if(standardGUIShowing){
        // check if in GUI area
        if(touch.x < 256)
        
        return;
    }

    // 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);
    }

}

//--------------------------------------------------------------
void testApp::touchDoubleTap(ofTouchEventArgs &touch){
    // preset?

}

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

}

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

}

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

}

//--------------------------------------------------------------
void testApp::deviceOrientationChanged(int newOrientation){
    cout << "orientation: " << newOrientation;
    keyboard->updateOrientation(); // takes ages , only applies to text box
    if(newOrientation == 4){
        ofxiPhoneSetOrientation( OF_ORIENTATION_90_RIGHT );
    }else if(newOrientation == 3){
        ofxiPhoneSetOrientation( OF_ORIENTATION_90_LEFT );
    }
    
}


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

}

// 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;
}