//|||||||||||||||||||||||||||||||||||||||||||||||

#include "ViewGestureState.hpp"

//|||||||||||||||||||||||||||||||||||||||||||||||

using namespace Ogre;
using namespace GestureDef;

#define SCALE 14.0

//|||||||||||||||||||||||||||||||||||||||||||||||

ViewGestureState::ViewGestureState()
{
	m_pLogTimer		= 0;
	m_pDatabase		= 0;

    m_bQuit         = false; 
	m_bDrawScalar   = false;
    m_FrameEvent    = Ogre::FrameEvent();		
}

//|||||||||||||||||||||||||||||||||||||||||||||||

void ViewGestureState::enter()
{
	// TODO: check the connection of wiimote & PPT

	OgreFramework::getSingletonPtr()->m_pLog->logMessage("Entering ViewGestureState...");	
    m_pSceneMgr = OgreFramework::getSingletonPtr()->m_pRoot->createSceneManager(ST_GENERIC, "ViewSceneMgr");
	m_pTrayMgr  = OgreFramework::getSingletonPtr()->m_pTrayMgr;	

	// Set the background to black
	OgreFramework::getSingletonPtr()->m_pViewport->setBackgroundColour(Ogre::ColourValue::Black);

	// Setup lights
	m_pSceneMgr->setAmbientLight(Ogre::ColourValue(0.25f, 0.25f, 0.25f));
	m_pSceneMgr->setShadowTechnique(SHADOWTYPE_STENCIL_ADDITIVE);
	Light* l = m_pSceneMgr->createLight("MainLight");
	l->setType(Light::LT_POINT);
	l->setPosition(250,150,250);
	l->setDiffuseColour(ColourValue::White);
	l->setSpecularColour(ColourValue::White);

	// Setup the camera
	m_pCamera = m_pSceneMgr->createCamera("LogCam");
	m_pCamera->setFOVy(Radian(Degree(40)));
	m_pCamera->setNearClipDistance(1);	
	m_pCamera->setAspectRatio(Real(OgreFramework::getSingletonPtr()->m_pViewport->getActualWidth()) /
        Real(OgreFramework::getSingletonPtr()->m_pViewport->getActualHeight()));	
	OgreFramework::getSingletonPtr()->m_pViewport->setCamera(m_pCamera);
	mCamNode = m_pSceneMgr->getRootSceneNode()->createChildSceneNode("CamNode", Vector3(0,0,25));
	mCamNode->pitch(Degree(0));
	mCamNode->attachObject(m_pCamera);

	// Setup the log timer
	m_pLogTimer = new Ogre::Timer();

	// Try to fetch information of gesture name, tester, & trial
	m_pDatabase = OgreFramework::getSingletonPtr()->m_pDatabase;
	mCurrGestureIdx  = 0;
	mCurrGestureName = getGestureName(mCurrGestureIdx);	
	mCurrTesterIdx   = 0;	
	mCurrTrial   = 1;
	mTestersList = m_pDatabase->LoadTesters();
	mGestureToPlay.length = 0;
	if (mTestersList.empty()){
		mCurrTesterInfo.name = "unknown";
		mCurrTesterInfo.rightHand = 1;
	}
	else{
		mCurrTesterInfo = mTestersList.at(mCurrTesterIdx);	
	}

    buildGUI();
    createScene();
	
	OgreFramework::getSingletonPtr()->setFixedFrameRate(true);
}

//|||||||||||||||||||||||||||||||||||||||||||||||

void ViewGestureState::createScene()
{
	createMaterialRGB();
	m_pController = createController();
}

//|||||||||||||||||||||||||||||||||||||||||||||||

bool ViewGestureState::pause()
{
    OgreFramework::getSingletonPtr()->m_pLog->logMessage("Pausing ViewGestureState...");

    return true;
}

//|||||||||||||||||||||||||||||||||||||||||||||||

void ViewGestureState::resume()
{
    OgreFramework::getSingletonPtr()->m_pLog->logMessage("Resuming ViewGestureState...");

    buildGUI();

    OgreFramework::getSingletonPtr()->m_pViewport->setCamera(m_pCamera);
    m_bQuit = false;
}

//|||||||||||||||||||||||||||||||||||||||||||||||

void ViewGestureState::exit()
{
    OgreFramework::getSingletonPtr()->m_pLog->logMessage("Leaving ViewGestureState...");

    m_pSceneMgr->destroyCamera(m_pCamera);
    if(m_pSceneMgr)
        OgreFramework::getSingletonPtr()->m_pRoot->destroySceneManager(m_pSceneMgr);

	if(m_pLogTimer)	delete m_pLogTimer;

    OgreFramework::getSingletonPtr()->m_pTrayMgr->clearAllTrays();
    OgreFramework::getSingletonPtr()->m_pTrayMgr->destroyAllWidgets();
    OgreFramework::getSingletonPtr()->m_pTrayMgr->setListener(0);
}

//|||||||||||||||||||||||||||||||||||||||||||||||

bool ViewGestureState::keyPressed(const OIS::KeyEvent &keyEventRef)
{
	Gesture localGesture;
	switch (keyEventRef.key)
	{
	case OIS::KC_ESCAPE:
		pushAppStateFrom(findByName("PauseState"), this); break; // TODO: add another "pause state" for more option for data logging
	case OIS::KC_NUMPAD9:
		gotoNextGesture(); 
		if (m_pDatabase->LoadOneGesture(mCurrGestureName, mCurrTesterInfo.name, mCurrTrial, localGesture))
			playGesture(localGesture);
		break;
	case OIS::KC_NUMPAD7:
		gotoPrevGesture(); 
		if (m_pDatabase->LoadOneGesture(mCurrGestureName, mCurrTesterInfo.name, mCurrTrial, localGesture))
			playGesture(localGesture);
		break;
	case OIS::KC_NUMPAD6:		
		gotoNextTester(); 
		if (m_pDatabase->LoadOneGesture(mCurrGestureName, mCurrTesterInfo.name, mCurrTrial, localGesture))
			playGesture(localGesture);
		break;
	case OIS::KC_NUMPAD4:	
		gotoPrevTester(); 
		if (m_pDatabase->LoadOneGesture(mCurrGestureName, mCurrTesterInfo.name, mCurrTrial, localGesture))
			playGesture(localGesture);
		break;
	case OIS::KC_NUMPAD3:
		gotoNextTrial(); 
		if (m_pDatabase->LoadOneGesture(mCurrGestureName, mCurrTesterInfo.name, mCurrTrial, localGesture))
			playGesture(localGesture);
		break;
	case OIS::KC_NUMPAD1:
		gotoPrevTrial(); 
		if (m_pDatabase->LoadOneGesture(mCurrGestureName, mCurrTesterInfo.name, mCurrTrial, localGesture))
			playGesture(localGesture);
		break;
		/*
	case OIS::KC_DELETE:
		if (this->mCurrTrial>1){
			if (m_pDatabase->DeleteOneGesture(mCurrGestureName, mCurrTesterInfo.name, mCurrTrial-1))
				mCurrTrial = m_pDatabase->GetCurrentTrial(mCurrGestureName, mCurrTesterInfo.name);
		}
		break;	*/	
	case OIS::KC_SPACE:				
		if (m_pDatabase->LoadOneGesture(mCurrGestureName, mCurrTesterInfo.name, mCurrTrial, localGesture))
			playGesture(localGesture);
		break;
	default:
		OgreFramework::getSingletonPtr()->keyPressed(keyEventRef); break;
	}

    return true;
}

//|||||||||||||||||||||||||||||||||||||||||||||||

bool ViewGestureState::keyReleased(const OIS::KeyEvent &keyEventRef)
{
	Gesture localGesture;
	switch (keyEventRef.key)
	{
	// show/hide orientation helper
	case OIS::KC_O:
		if (m_bDrawScalar){			
			destroyScalarAtNode(m_pController);
			m_bDrawScalar = false;
		}
		else{			
			drawScalarAtNode(m_pController);
			m_bDrawScalar = true;
		}
		break;
	// show/hide extra information
	case OIS::KC_I:
		if (m_pDetailsPanel->isVisible())
			m_pDetailsPanel->hide();
		else
			m_pDetailsPanel->show();
		break;	
	default:
		OgreFramework::getSingletonPtr()->keyReleased(keyEventRef);
	}
    
    return true;
}

//|||||||||||||||||||||||||||||||||||||||||||||||

bool ViewGestureState::mouseMoved(const OIS::MouseEvent &evt)
{
    if(OgreFramework::getSingletonPtr()->m_pTrayMgr->injectMouseMove(evt)) return true;

	/* // Disable the rotation of the camera
	if (evt.state.buttonDown(OIS::MB_Right))
    {		
		mCamNode->yaw  (Degree(evt.state.X.rel * -0.1f), Node::TS_WORLD);
		mCamNode->pitch(Degree(evt.state.Y.rel * -0.1f), Node::TS_LOCAL);        
    }*/
    return true;
}

//|||||||||||||||||||||||||||||||||||||||||||||||

bool ViewGestureState::mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
{
    if(OgreFramework::getSingletonPtr()->m_pTrayMgr->injectMouseDown(evt, id)) return true;
    return true;
}

//|||||||||||||||||||||||||||||||||||||||||||||||

bool ViewGestureState::mouseReleased(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
{
    if(OgreFramework::getSingletonPtr()->m_pTrayMgr->injectMouseUp(evt, id)) return true;
    return true;
}

//|||||||||||||||||||||||||||||||||||||||||||||||

void ViewGestureState::update(double timeSinceLastFrame)
{
    m_FrameEvent.timeSinceLastFrame = timeSinceLastFrame;
    m_pTrayMgr->frameRenderingQueued(m_FrameEvent);
	
	if(m_bQuit == true)
    {
        popAppState();
        return;
    }
	
	updateDetailsPanel();
		
	Sample newSample;	
	// show one "Sample" from mGestureToPlay
	if (mFrameIdx < mGestureToPlay.length){
		newSample = mGestureToPlay.data.at(mFrameIdx);
		m_pController->setPosition( SCALE*getSamplePos(newSample.pos) );
		m_pController->setOrientation( getSampleOri(newSample.ori) );
		mFrameIdx++;
	}
}

//|||||||||||||||||||||||||||||||||||||||||||||||

void ViewGestureState::updateDetailsPanel()
{
	if(!m_pTrayMgr->isDialogVisible())
    {
        if(m_pDetailsPanel->isVisible())
        {			
			m_pDetailsPanel->setParamValue(0, mCurrGestureName);
			m_pDetailsPanel->setParamValue(1, mCurrTesterInfo.name+((mCurrTesterInfo.rightHand)?" (R)":" (L)"));
			m_pDetailsPanel->setParamValue(2, StringConverter::toString(mCurrTrial));						
        }
    }
}

//|||||||||||||||||||||||||||||||||||||||||||||||

void ViewGestureState::buttonHit(OgreBites::Button *button)
{	
}

//|||||||||||||||||||||||||||||||||||||||||||||||

void ViewGestureState::buildGUI()
{	
	m_pTrayMgr->destroyAllWidgets();
	m_pTrayMgr->showFrameStats(OgreBites::TL_BOTTOMLEFT);
	m_pTrayMgr->showLogo(OgreBites::TL_BOTTOMRIGHT);
	m_pTrayMgr->hideCursor();
	m_pTrayMgr->createLabel(OgreBites::TL_TOP, "GameLb1", "View Gesture mode", 280);    		

    Ogre::String infoText = "Use Numpad to control\n\n[7] & [9] - switch gesture\n[4] & [6] - switch tester \n[1] & [3]- switch trial \n";
    infoText.append("[SPACE] - replay\n[ESC] - Exit");
	m_pTrayMgr->createTextBox(OgreBites::TL_TOPRIGHT, "InfoPanel", infoText, 250, 130);

    Ogre::StringVector items;
	items.push_back("Gesture");
	items.push_back("Tester");
	items.push_back("CurrTrial");
	
    m_pDetailsPanel = m_pTrayMgr->createParamsPanel(OgreBites::TL_TOPLEFT, "DetailsPanel", 300, items);
	m_pDetailsPanel->setTextAreaCharHeight(25);
    m_pDetailsPanel->show();
	m_pTrayMgr->adjustTrays();
}

//|||||||||||||||||||||||||||||||||||||||||||||||

SceneNode* ViewGestureState::createController(){
	
	// ********* Ogre parts *********
	String name = "controller";	
	Entity* ent = m_pSceneMgr->createEntity(name, "wii.mesh");	
	ent->setCastShadows(false);
	
	// TODO: add another entity for overlayNode
	SceneNode *node, *bufferNode;
	node = m_pSceneMgr->getRootSceneNode()->createChildSceneNode(name);
	
	Vector3 shift = Vector3(0, 0, 0.9f * 1.5f);
	bufferNode = node->createChildSceneNode("controllerBuffer");
	bufferNode->attachObject(ent);
	Ogre::Real r = bufferNode->getAttachedObject("controller")->getBoundingRadius();	
	bufferNode->setScale(1.5/r, 1.5/r, 1.5/r);	// normalize the bounding radius to 1.5 unit (length ~3 units)
	bufferNode->setPosition(shift);				// shift the wiimote so that the "head" is centered at node

	return node;
}

//|||||||||||||||||||||||||||||||||||||||||||||||

// create materials for R, G, B lines
void ViewGestureState::createMaterialRGB()
{
	materialR = MaterialManager::getSingleton().create("materialR", "VM");
	materialR->setReceiveShadows(false);
	materialR->getTechnique(0)->setLightingEnabled(true);
	materialR->getTechnique(0)->getPass(0)->setDiffuse(0,0,1,0);
	materialR->getTechnique(0)->getPass(0)->setAmbient(0,0,1);
	materialR->getTechnique(0)->getPass(0)->setSelfIllumination(1,0,0);

	materialG = MaterialManager::getSingleton().create("materialG", "VM");
	materialG->setReceiveShadows(false);
	materialG->getTechnique(0)->setLightingEnabled(true);
	materialG->getTechnique(0)->getPass(0)->setDiffuse(0,0,1,0);
	materialG->getTechnique(0)->getPass(0)->setAmbient(0,0,1);
	materialG->getTechnique(0)->getPass(0)->setSelfIllumination(0,1,0);

	materialB = MaterialManager::getSingleton().create("materialB", "VM");
	materialB->setReceiveShadows(false);
	materialB->getTechnique(0)->setLightingEnabled(true);
	materialB->getTechnique(0)->getPass(0)->setDiffuse(0,0,1,0);
	materialB->getTechnique(0)->getPass(0)->setAmbient(0,0,1);
	materialB->getTechnique(0)->getPass(0)->setSelfIllumination(0,0,1);	
}


//|||||||||||||||||||||||||||||||||||||||||||||||

// Draw the RGB lines for XYZ coords at node's origin
void ViewGestureState::drawScalarAtNode(SceneNode* node)
{	
	String name = node->getName();
	ManualObject* objectX =  m_pSceneMgr->createManualObject("manualX" + name);
	ManualObject* objectY =  m_pSceneMgr->createManualObject("manualY" + name);
	ManualObject* objectZ =  m_pSceneMgr->createManualObject("manualZ" + name);
	
	objectX->begin("materialR", Ogre::RenderOperation::OT_LINE_LIST);
	objectX->position(0, 0, 0);
	objectX->position(2, 0, 0);
	objectX->end();

	objectY->begin("materialG", Ogre::RenderOperation::OT_LINE_LIST);
	objectY->position(0, 0, 0);
	objectY->position(0, 2, 0);
	objectY->end();

	objectZ->begin("materialB", Ogre::RenderOperation::OT_LINE_LIST);
	objectZ->position(0, 0, 0);
	objectZ->position(0, 0, 2);
	objectZ->end();

	node->attachObject(objectX);
	node->attachObject(objectY);
	node->attachObject(objectZ);	
}

//|||||||||||||||||||||||||||||||||||||||||||||||

void ViewGestureState::destroyScalarAtNode(SceneNode* node)
{	
	String name = node->getName();	
	if (m_pSceneMgr->hasManualObject("manualX" + name)){
		node->detachObject("manualX" + name);
		m_pSceneMgr->destroyManualObject("manualX" +name);
	}
	if (m_pSceneMgr->hasManualObject("manualY" + name)){
		node->detachObject("manualY" + name);
		m_pSceneMgr->destroyManualObject("manualY" +name);
	}
	if (m_pSceneMgr->hasManualObject("manualZ" + name)){
		node->detachObject("manualZ" + name);
		m_pSceneMgr->destroyManualObject("manualZ" +name);
	}
}

//|||||||||||||||||||||||||||||||||||||||||||||||

void ViewGestureState::playGesture(const Gesture& gesture)
{		
	mGestureToPlay = gesture;
	mFrameIdx = 0;	
}

//|||||||||||||||||||||||||||||||||||||||||||||||

Vector3 ViewGestureState::getSamplePos(GestureDef::XYZ xyz)
{
	return Vector3(xyz.x, xyz.y, xyz.z);
}

//|||||||||||||||||||||||||||||||||||||||||||||||

Quaternion ViewGestureState::getSampleOri(GestureDef::ORI ori)
{		
	return Quaternion(ori.w, ori.x, ori.y, ori.z);
}

//|||||||||||||||||||||||||||||||||||||||||||||||

void ViewGestureState::gotoNextGesture()
{
	mCurrGestureIdx++;
	if (mCurrGestureIdx==TOTAL_GESTURES)
		mCurrGestureIdx=0;

	// update gesture name & trial
	mCurrGestureName = getGestureName(mCurrGestureIdx);
	mCurrTrial = 1;
}

//|||||||||||||||||||||||||||||||||||||||||||||||

void ViewGestureState::gotoPrevGesture()
{
	if (mCurrGestureIdx==0)
		mCurrGestureIdx = TOTAL_GESTURES;
	mCurrGestureIdx--;

	// update gesture name & trial
	mCurrGestureName = getGestureName(mCurrGestureIdx);
	mCurrTrial = 1;
}

//|||||||||||||||||||||||||||||||||||||||||||||||

void ViewGestureState::gotoNextTester()
{
	mCurrTesterIdx++;	
	if (mCurrTesterIdx == mTestersList.size())
		mCurrTesterIdx = 0;

	mCurrTesterInfo = mTestersList.at(mCurrTesterIdx);	

	// update gesture name & trial
	mCurrGestureName = getGestureName(mCurrGestureIdx);
	mCurrTrial = 1;
}

//|||||||||||||||||||||||||||||||||||||||||||||||

void ViewGestureState::gotoPrevTester()
{
	if (mCurrTesterIdx == 0)
		mCurrTesterIdx = mTestersList.size();

	mCurrTesterIdx--;
	mCurrTesterInfo = mTestersList.at(mCurrTesterIdx);	

	// update gesture name & trial
	mCurrGestureName = getGestureName(mCurrGestureIdx);
	mCurrTrial = 1;
}

//|||||||||||||||||||||||||||||||||||||||||||||||

void ViewGestureState::gotoNextTrial()
{
	mCurrTrial++;
	if (mCurrTrial > 10)
		mCurrTrial = 1;	
}

//|||||||||||||||||||||||||||||||||||||||||||||||

void ViewGestureState::gotoPrevTrial()
{
	mCurrTrial--;
	if (mCurrTrial==0)
		mCurrTrial = 10;
}

//|||||||||||||||||||||||||||||||||||||||||||||||