#include "grafRecorderApp.h"

#ifdef TARGET_OSX
	#include <sys/stat.h>
#endif


//--------------------------------------------------------------
grafRecorderApp::grafRecorderApp(){
	
	camW				= 320;
    camH				= 240;
    camId				= 1;
	bUseVideoFiles		= false;
    bRecording			= false;
	bZooming			= true;
	bUseMouseCapture	= false;
    bShowPanel			= true;
	bShiftOn			= false;
	bShowEditPanel		= false;
	recordMode			= REC_CAPTURE;
	zoom				= 0;
	tagMoveForce		= .1;
	tagPosVel.set(0,0,0);
	fps					= 1/30.f;
	fpsCounter			= 0;

}

grafRecorderApp::~grafRecorderApp(){

	delete videoSource;

}
//--------------------------------------------------------------
void grafRecorderApp::setup(){

	
	screenW = ofGetWidth();
    screenH = ofGetHeight();
    

	//----------- app config
	ofxXmlSettings  xml;
    xml.loadFile("settings/config.xml");
	
    bUseVideoFiles		= xml.getValue("useVideo",false);
    camW				= xml.getValue("camWidth",320);
    camH				= xml.getValue("camHeight",240);
	string videoFile	= xml.getValue("videoFile","finger.mov");
	directory			= xml.getValue("directory","tags");
	bUseFolders			= xml.getValue("useFolders", 1);
	
	//----------- setup tracking
	grafTracker.setup(camW,camH);
	
	//----------- setup editing and tag
	tag.minPtDist = 0.0001;
	tag.drawScale = 1024.f;
	
	gEditor.setup(20,screenH-110,200,18);
	tag.tagname = gEditor.getName();
	
	//----------- setup controls
	setupContolPanel();
	updateControlPanel();
	
	
	//----------- video settings
	if (bUseVideoFiles == true){
		
		videoSource			= new ofVideoPlayer();
		((ofVideoPlayer *)videoSource)->loadMovie(videoFile);
		((ofVideoPlayer *)videoSource)->play();
		((ofVideoPlayer *)videoSource)->setLoopState(OF_LOOP_NORMAL);
		((ofVideoPlayer *)videoSource)->setSpeed(1);
		
	} else {
		
		videoSource			= new ofVideoGrabber();
		((ofVideoGrabber *)videoSource)->listDevices();
		((ofVideoGrabber *)videoSource)->setDeviceID(camId);
		((ofVideoGrabber *)videoSource)->initGrabber(camW, camH);
	}
	
	
	fontL.loadFont("fonts/frabk.ttf",22);

}

//--------------------------------------------------------------
void grafRecorderApp::reset()
{
	recordMode = REC_CAPTURE;
	bRecording = false;
	bShowPanel = true;
	tag.clear(true);
	fpsCounter		= 0;
	zoom			= 0;
	tagMoveForce	= .1;
	tagPosVel.set(0,0,0);
	tag.drawScale = 1024.f;
}

static float lastTime = 0.f;
//--------------------------------------------------------------
void grafRecorderApp::update(){

	dt  = ofGetElapsedTimef()-lastTime;
	lastTime  = ofGetElapsedTimef();
	
	
	//----------- get video capture
    videoSource->update();
	
	
    //----------- update tracker with incoming video
    if(!bUseMouseCapture && recordMode == REC_CAPTURE) grafTracker.update( videoSource->getPixels() );
	
	if( recordMode == REC_CAPTURE )
	{
		
		// set the tracked point (note mouse capture is not rendering in correct place )
		ofPoint addPoint;
		if(bUseMouseCapture)    addPoint.set(lastX/1024.f,lastY/1024.f,1 );
		else                    addPoint.set(grafTracker.getX()/camW, grafTracker.getY()/camW,1);
		
		// only update if fps matches our cam (assuming 30 fps )
		if(bRecording && (ofGetElapsedTimef()-fpsCounter) >= fps )
		{
		tag.addNewPoint(addPoint);
		fpsCounter += fps;
		
		}
		
		// calc tag stats to render timeline correctly
		tag.calcMinMax();
		
		// note: would need to change this for multi-stroke
		if(bRecording) gEditor.updateMax( tag.myStrokes[0].pts.size() );
		drawer.setup(&tag,0,0,1, tag.myStrokes[0].pts.size()-1);
	}
	
	
	// update pos / vel to move tag around screen nicely
	tag.position.x += tagPosVel.x;
	tag.position.y += tagPosVel.y;
		
	tagPosVel.x -= .1*tagPosVel.x;
	tagPosVel.y -= .1*tagPosVel.y;
	
	// set zoom so we can see the whole tag while recording / editing	
	int tPts = tag.myStrokes[0].pts.size();
	
	if(bRecording && bZooming && tPts > 0 ) zoom = -( tag.myStrokes[0].pts[tPts-1].time*1000) / tag.z_const;
	else if( recordMode == REC_EDIT )
	{
		int halfPt = gEditor.getStart() + ( gEditor.getEnd() -  gEditor.getStart() )/2.f;
		zoom = -(tag.myStrokes[0].pts[halfPt].time*1000) / tag.z_const;
	}
	
	// update control panel
	if( bShowPanel ) updateControlPanel();
	
}


//--------------------------------------------------------------
void grafRecorderApp::draw(){

	ofEnableAlphaBlending();
	ofSetColor(255,255,255,255);
	ofNoFill();
	glEnable(GL_DEPTH_TEST);
		
	//-------- draw while capturing
	if( recordMode == REC_CAPTURE )
	{
		glPushMatrix();
		
		glTranslatef(0,0, zoom);
		glScalef(tag.drawScale,tag.drawScale,1);
		
		ofSetColor(255,255,255);
		
		drawer.drawWireFrame( &tag,0,gEditor.getStart(),0,gEditor.getEnd() );
		drawer.draw(0,1,0,tag.myStrokes[0].pts.size());
		
		int tPts = tag.myStrokes[0].pts.size()-1;
		drawer.drawTimeLine(ofPoint(tag.min.x+tag.center.x,tag.max.y+.05,0), tag.myStrokes[0].pts[tPts].time, tag.myStrokes[0].pts[0].time, tag.z_const, &fontL, tag.drawScale);
		
		glPopMatrix();
		
	}

	else if( recordMode == REC_EDIT )
	{
		glPushMatrix();
	
			glTranslatef(screenW/2, screenH/2, 0);
		
			glScalef(tag.position.z,tag.position.z,tag.position.z);
		
			glRotatef(tag.rotation.x,0,1,0);
			glRotatef(tag.rotation.y,1,0,0);
			glRotatef(tag.rotation.z,0,0,1);
		
			glTranslatef(tag.position.x,tag.position.y,zoom);//PR->position.z);
		
			glPushMatrix();
						
				
				glScalef(tag.drawScale,tag.drawScale,1);
		
				glTranslatef(-tag.min.x,-tag.min.y,0);//-tag.min.z);
				glTranslatef(-tag.center.x,-tag.center.y,0);//-tag.center.z);
				
				ofSetColor(100,100,100);
				drawer.drawBoundingBox(tag.min,tag.max,tag.center);
				
				ofSetColor(255,255,255);
				drawer.drawWireFrame( &tag,0,gEditor.getStart(),0,gEditor.getEnd() );
				drawer.draw(0,gEditor.getStart()+1,0,gEditor.getEnd());//tag.myStrokes[0].pts.size());
			
				int tPts = tag.myStrokes[0].pts.size()-1;
				drawer.drawTimeLine(ofPoint(tag.min.x+tag.center.x,tag.max.y+.05,0), tag.myStrokes[0].pts[tPts].time, tag.myStrokes[0].pts[0].time, tag.z_const, &fontL, tag.drawScale);

			glPopMatrix();
			
		glPopMatrix();
	}
	
	
	//-------- tracking and controls
	if(bShowPanel)	drawTracking();
	if( recordMode == REC_EDIT ) gEditor.draw();
	
	//-------- draw tracked position as red dot
	if( recordMode == REC_CAPTURE )
	{
		
		ofSetColor(255,0,0);
		ofCircle( (grafTracker.getX()/camW) * screenW, (grafTracker.getY()/camW)*screenW, 4 );
	}	

	
}

void grafRecorderApp::drawTracking()
{

	glDisable(GL_DEPTH_TEST);
	
	// tracking panels
	grafTracker.draw();
	
	// control panel
	panel.draw();
	
	ofSetColor(0xffffff);
	ofDrawBitmapString( ofToString( ofGetFrameRate(), 2 ),20,500);
	ofDrawBitmapString( "Name: " + tag.tagname,20,520);
	ofDrawBitmapString( "Total Points: " + ofToString((int)tag.myStrokes[0].pts.size()),20,540);
	ofDrawBitmapString( "' ': toggle recording ",20,570);
	ofDrawBitmapString( "c: toggle camera/cv panel ",20,590);
	ofDrawBitmapString( "RETURN: advance app",20,610);
	ofDrawBitmapString( "x: toggle controls (in playback mode)",20,630);
}

void grafRecorderApp::drawControls()
{
}

//--------------------------------------------------------------
void grafRecorderApp::keyPressed(int key){


	if( recordMode == REC_EDIT && gEditor.isEnteringName() )
    {
        gEditor.editName( key );
        return;
    }
	
	
    switch(key){
			
  		case 'b':
            grafTracker.bCaptureBg = true;
            break;
        case 'c':
            bShowPanel = !bShowPanel;
            break;
        case 'e':
            bShowEditPanel = !bShowEditPanel;
            break;
        case ' ':
  			toggleRecording();
			break;
  		case 'm':
            bUseMouseCapture = !bUseMouseCapture;
		default:
  			break;
			
	}
	

}
//--------------------------------------------------------------
void grafRecorderApp::toggleRecording(){
	if( bRecording && tag.myStrokes[0].pts.size() > 0 )
	{
		bRecording = false;
		recordMode = REC_EDIT;
		drawer.setup(&tag,0,gEditor.getStart(),1,gEditor.getEnd());
		bShowPanel = false;
		tag.drawScale = 768.f;
	}else{
		bRecording = true;
	}
}

//--------------------------------------------------------------
void grafRecorderApp::keyReleased(int key){

}

//--------------------------------------------------------------
void grafRecorderApp::mouseMoved(int x, int y ){

	lastX   = x;
	lastY   = y;
}

//--------------------------------------------------------------
void grafRecorderApp::mouseDragged(int x, int y, int button){

	
	if(bShowPanel)	grafTracker.mouseDragged(x,y,button);
    
	
    bool bEdit = false;
    if( recordMode == REC_EDIT )	bEdit = gEditor.update(x,y) ;
        
		
    if(!bShowPanel && recordMode == REC_EDIT && !bEdit)
	{
	    if( button == 0 )
		{
			if(bShiftOn)
			{
				tagPosVel.x +=  tagMoveForce * (x-lastX);
				tagPosVel.y +=  tagMoveForce * (y-lastY);
			}else{
				tag.position.z += .01 * (y-lastY);
				tag.position.z = MAX(tag.position.z,.01);
			}
			
			
		}else{
			if( bShiftOn) tag.rotation.y += (y-lastY);
			else if( !bShiftOn) tag.rotation.x += (x-lastX);
		}
		
	}
	
	
	
	
	if( bShowPanel && !panel.minimize) 
	{
		panel.mouseDragged(x,y,button);
	}
		
	
	
	lastX   = x;
	lastY   = y;

}

//--------------------------------------------------------------
void grafRecorderApp::mousePressed(int x, int y, int button){

    if( bShowPanel ) panel.mousePressed(x,y,button);
    	
	if(glutGetModifiers() == GLUT_ACTIVE_SHIFT) bShiftOn = true;
	
    if(bShowPanel) grafTracker.mousePressed(x,y,button);
    if(recordMode == REC_EDIT) gEditor.update(x,y) ;
	
}

//--------------------------------------------------------------
void grafRecorderApp::mouseReleased(int x, int y, int button){

	if( bShowPanel && !panel.minimize) 
	{
		panel.mouseReleased();
	}
	 
    bShiftOn = false;


}

//--------------------------------------------------------------
void grafRecorderApp::setupContolPanel()
{
	
	panel.setup("cv", 330, 0, 300, 220);
	panel.addPanel("cv settings", 1, false);
	panel.addPanel("camera settings", 1, false);
	
	panel.setWhichPanel("cv settings");
	panel.addSlider("Threshold","THRESHOLD",180,0,255,true);
	
	panel.setWhichPanel("camera settings");
	panel.addSlider("Cam Id (restart)","CAM_ID",0,0,10,true);
	panel.addToggle("Flip X", "FLIP_X", false);
	panel.addToggle("Flip Y", "FLIP_Y", false);
	
	panel.loadSettings("settings/cv-settings.xml");
}

//--------------------------------------------------------------
void grafRecorderApp::updateControlPanel()
{
	panel.update();
	
	camId = panel.getValueI("CAM_ID");
	grafTracker.threshold = panel.getValueI("THRESHOLD");
	grafTracker.bFlipV = panel.getValueB("FLIP_Y");
	grafTracker.bFlipH = panel.getValueB("FLIP_X");
}

//--------------------------------------------------------------
void grafRecorderApp::saveTag()
{
	string myDir = directory;
	
	gIO.setup("","Graffiti Analysis 2.0","1.0");
	
	tag.tagname.clear();
	tag.tagname = gEditor.getName();

	cout << "directory " << myDir << endl;
	cout << "gEditor " << gEditor.grafName << endl;
	gEditor.checkNameForSaving(directory);
	
	tag.tagname = gEditor.getName();
	
	// crop to start and end pts
	cropTagToEdits();
	
	// create gml
	gIO.constructGML(&tag);
	
	// create directory
	if( bUseFolders )
	{
		myDir = directory + "/" + tag.tagname;
		mkdir(ofToDataPath(myDir).c_str(),0777);
	}
	
	// save file
	gIO.xml.saveFile( myDir + "/" + tag.tagname + ".gml" );
}
//--------------------------------------------------------------
void grafRecorderApp::cropTagToEdits()
{
	// remove start and end points
	// remove end first or it is the wrong size...
	int startPoint	= gEditor.getStart();
	int endPoint	= gEditor.getEnd();
	int total		= tag.myStrokes[0].pts.size()-1;
	
	if( endPoint < total)  tag.myStrokes[0].pts.erase(tag.myStrokes[0].pts.begin()+endPoint,tag.myStrokes[0].pts.begin()+total );
	if( startPoint > 0 )   tag.myStrokes[0].pts.erase(tag.myStrokes[0].pts.begin(),tag.myStrokes[0].pts.begin()+startPoint);
	
	float startTime = tag.myStrokes[0].pts[0].time;
	
	// change time so that start time is 0
	for( int i = 0; i < tag.myStrokes[0].pts.size(); i++)
	{
		tag.myStrokes[0].pts[i].time -= startTime;
	}
	
}

