/*
 * Copyright 2010, 
 *
 * Andrei Herdt
 * Olivier Stasse
 *
 * JRL, CNRS/AIST
 *
 * This file is part of walkGenJrl.
 * walkGenJrl is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * walkGenJrl is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * You should have received a copy of the GNU Lesser General Public License
 * along with walkGenJrl.  If not, see <http://www.gnu.org/licenses/>.
 *
 *  Research carried out within the scope of the 
 *  Joint Japanese-French Robotics Laboratory (JRL)
 */
/* \file This file tests A. Herdt's walking algorithm for
 * automatic foot placement giving an instantaneous CoM velocity reference.
 */
#include "Debug.hh"
#include "CommonTools.hh"
#include "TestObject.hh"

using namespace::PatternGeneratorJRL;
using namespace::PatternGeneratorJRL::TestSuite;
using namespace std;

enum Profiles_t {
  PROFIL_HERDT_ONLINE_WALKING,                 // 1
  PROFIL_HERDT_EMERGENCY_STOP,                 // 2
  PROFIL_HERDT_POWER_LAW                       // 3
};

class TestHerdt2010: public TestObject
{
  
private:
  /// Array of velocities.
  typedef std::vector<double> vector_double_t;
  std::vector<vector_double_t> queueOfVelocity_;
  unsigned long int indexQueueVelocity_;
  
public:
  TestHerdt2010(int argc, char *argv[], string &aString, int TestProfile):
    TestObject(argc,argv,aString)
  {
    m_TestProfile = TestProfile;
  };
  
  typedef void (TestHerdt2010::* localeventHandler_t)(PatternGeneratorInterface &);
  
  struct localEvent 
  {
    unsigned time;
    localeventHandler_t Handler ;
  };
  
protected:

  

  
  void startOnLineWalking(PatternGeneratorInterface &aPGI)
  {
    CommonInitialization(aPGI);
    
    {
      istringstream strm2(":SetAlgoForZmpTrajectory Herdt");
      aPGI.ParseCmd(strm2);
      
    }
    {
      istringstream strm2(":singlesupporttime 0.7");
      aPGI.ParseCmd(strm2);
    }
    {
      istringstream strm2(":doublesupporttime 0.1");
      aPGI.ParseCmd(strm2);
    }
    {
      //istringstream strm2(":HerdtOnline");
      istringstream strm2(":HerdtOnline 0.2 0.0 0.0");
      aPGI.ParseCmd(strm2);
    }
    {
      istringstream strm2(":numberstepsbeforestop 2");
      aPGI.ParseCmd(strm2);
    }
  }

  void startEmergencyStop(PatternGeneratorInterface &aPGI)
  {
    CommonInitialization(aPGI);
    
    {
      istringstream strm2(":SetAlgoForZmpTrajectory Herdt");
      aPGI.ParseCmd(strm2);
      
    }
    {
      istringstream strm2(":singlesupporttime 0.7");
      aPGI.ParseCmd(strm2);
    }
    {
      istringstream strm2(":doublesupporttime 0.1");
      aPGI.ParseCmd(strm2);
    }
    {
      //istringstream strm2(":HerdtOnline");
      istringstream strm2(":HerdtOnline 0.2 0.0 0.2");
      aPGI.ParseCmd(strm2);
    }
    {
      istringstream strm2(":numberstepsbeforestop 2");
      aPGI.ParseCmd(strm2);
    }
  }

  void startPowerLaw(PatternGeneratorInterface &aPGI)
  {
    startOnLineWalking(aPGI);

    std::string fileName("/home/ostasse/Projets/KOROIBOT/Motions/Trajectories/Arena_[3,2]_Shape_2_Beta_MinusThird.txt");
    loadFileOfVelocityProfile(fileName);
  }

  void startTurningLeft(PatternGeneratorInterface &aPGI)
  {
    {
      istringstream strm2(":setVelReference  0.2 0.0 6.0832");
      aPGI.ParseCmd(strm2);
    }
  }

  void startTurningRight(PatternGeneratorInterface &aPGI)
  {
    {
      //istringstream strm2(":setVelReference  0.2 0.0 -0.2");
      istringstream strm2(":setVelReference  0.2 0.0 -6.0832");
      aPGI.ParseCmd(strm2);
    }
  }

  void startTurningRight2(PatternGeneratorInterface &aPGI)
  {
    {
      istringstream strm2(":setVelReference  0.2 0.0 -0.2");
      aPGI.ParseCmd(strm2);
    }
  }

  void startTurningLeft2(PatternGeneratorInterface &aPGI)
  {
    {
      istringstream strm2(":setVelReference  0.0 0.0 0.4");
      aPGI.ParseCmd(strm2);
    }
  }

  void startTurningLeftOnSpot(PatternGeneratorInterface &aPGI)
  {
    {
      istringstream strm2(":setVelReference  0.0 0.0 10.0");
      aPGI.ParseCmd(strm2);
    }
  }

  void startTurningRightOnSpot(PatternGeneratorInterface &aPGI)
  {
    {
      istringstream strm2(":setVelReference  0.0 0.0 -10.");
      aPGI.ParseCmd(strm2);
    }
  }


  void stop(PatternGeneratorInterface &aPGI)
  {
    {
      istringstream strm2(":setVelReference 0.0 0.0 0.0");
      aPGI.ParseCmd(strm2);
    }
  }
  void walkForward(PatternGeneratorInterface &aPGI)
  {
    {
      istringstream strm2(":setVelReference  0.2 0.0 0.0");
      aPGI.ParseCmd(strm2);
    }
  }
  void walkSidewards(PatternGeneratorInterface &aPGI)
  {
    {
      istringstream strm2(":setVelReference  0.0 0.2 0.0");
      aPGI.ParseCmd(strm2);
    }
  }

  void stopOnLineWalking(PatternGeneratorInterface &aPGI)
  {
    {
      istringstream strm2(":setVelReference  0.0 0.0 0.0");
      aPGI.ParseCmd(strm2);
      istringstream strm3(":stoppg");
      aPGI.ParseCmd(strm3);
    }
  }

  void chooseTestProfile()
  {
    
    switch(m_TestProfile)
      {

      case PROFIL_HERDT_ONLINE_WALKING:
	startOnLineWalking(*m_PGI);
	break;
      case PROFIL_HERDT_EMERGENCY_STOP:
        startEmergencyStop(*m_PGI);
        break;
        
      default:
	throw("No correct test profile");
	break;
      }
  }


  void generateEventOnLineWalking()
  {

    struct localEvent 
    {
      unsigned time;
      localeventHandler_t Handler ;
    };

    #define localNbOfEvents 12
    struct localEvent events [localNbOfEvents] =
      { { 5*200,&TestHerdt2010::walkForward},
        {10*200,&TestHerdt2010::walkSidewards},
        {25*200,&TestHerdt2010::startTurningRightOnSpot},
        {35*200,&TestHerdt2010::walkForward},
        {45*200,&TestHerdt2010::startTurningLeftOnSpot},
        {55*200,&TestHerdt2010::walkForward},
        {65*200,&TestHerdt2010::startTurningRightOnSpot},
        {75*200,&TestHerdt2010::walkForward},
        {85*200,&TestHerdt2010::startTurningLeft},
	{95*200,&TestHerdt2010::startTurningRight},
	{105*200,&TestHerdt2010::stop},
	{110*200,&TestHerdt2010::stopOnLineWalking}};
    
    // Test when triggering event.
    for(unsigned int i=0;i<localNbOfEvents;i++)
      { 
	if ( m_OneStep.NbOfIt==events[i].time)
	  {
            ODEBUG3("********* GENERATE EVENT OLW ***********");
	    (this->*(events[i].Handler))(*m_PGI);
	  }
      }
  }

  void loadFileOfVelocityProfile(const std::string &fileName)
  {

    std::ifstream aif;
    aif.open(fileName.c_str(),std::ifstream::in);
    if (aif.is_open())
      {
        while (!aif.eof())
          {
            vector_double_t VelCompleteData(7);
            /// Read time from the start of the trajectory
            aif >> VelCompleteData[0];
            /// Read position
            aif >> VelCompleteData[1]; aif >> VelCompleteData[2];
            // Read Velocity
            aif >> VelCompleteData[3]; aif >> VelCompleteData[4]; 
            aif >> VelCompleteData[5];
            // Total speed
            aif >> VelCompleteData[6];
        
            queueOfVelocity_.push_back(VelCompleteData);
          }
      }
    aif.close();
    indexQueueVelocity_ = 0;
  }

  void generateEventPowerLawVelocity()
  {
    vector_double_t VelCompleteData,VelProf;
    VelCompleteData = queueOfVelocity_[indexQueueVelocity_];

    for(unsigned int li=0;li<3;li++)
      VelProf[li] = VelCompleteData[3+li];
    
    {
      ostringstream ostrm2(":setVelReference  ");
      ostrm2 << VelProf[0] << " " ;
      ostrm2 << VelProf[1] << " ";
      ostrm2 << VelProf[2];
      istringstream istrm2(ostrm2.str());

      m_PGI->ParseCmd(istrm2);
      std::cout << "strm2: " << ostrm2.str() << std::endl;

    }

    indexQueueVelocity_++;
  }

  void generateEventEmergencyStop()
  {

    #define localNbOfEventsEMS 4
    struct localEvent events [localNbOfEventsEMS] =
      { {5*200,&TestHerdt2010::startTurningLeft2},
        {10*200,&TestHerdt2010::startTurningRight2},
        {15.2*200,&TestHerdt2010::stop},
        {20.8*200,&TestHerdt2010::stopOnLineWalking}};
    
    // Test when triggering event.
    for(unsigned int i=0;i<localNbOfEventsEMS;i++)
      { 
	if ( m_OneStep.NbOfIt==events[i].time)
	  {
            ODEBUG3("********* GENERATE EVENT EMS ***********");
	    (this->*(events[i].Handler))(*m_PGI);
	  }
      }
  }

  void generateEvent()
  {
    switch(m_TestProfile)
      {
      case PROFIL_HERDT_ONLINE_WALKING:
        generateEventOnLineWalking();
        break;
      case PROFIL_HERDT_EMERGENCY_STOP:
        generateEventEmergencyStop();
        break;
      case PROFIL_HERDT_POWER_LAW:
        
        break;
      default:
        break;
      }
  }
};

int PerformTests(int argc, char *argv[])
{
  #define NB_PROFILES 3
  std::string TestNames[NB_PROFILES] = { "TestHerdt2010OnLine",
                                         "TestHerdt2010EmergencyStop",
                                         "TestHerdt2010PowerLaw"};
  int TestProfiles[NB_PROFILES] = { PROFIL_HERDT_ONLINE_WALKING,
                                    PROFIL_HERDT_EMERGENCY_STOP,
                                    PROFIL_HERDT_POWER_LAW};

  for (unsigned int i=NB_PROFILES-2;i<NB_PROFILES;i++)
    {
      TestHerdt2010 aTH2010(argc,argv,
			    TestNames[i],
			    TestProfiles[i]);
      aTH2010.init();


      try
	{
	  if (!aTH2010.doTest(std::cout))
	    {
	      cout << "Failed test " << i << endl;
	      return -1;
	    }
	  else
	    cout << "Passed test " << i << endl;
	}
      catch (const char * astr)
	{ cerr << "Failed on following error " << astr << std::endl;
	  return -1; }
    }
  return 0;
}

int main(int argc, char *argv[])
{
  try
    {
      return PerformTests(argc,argv);
    }
  catch (const std::string& msg)
    {
      std::cerr << msg << std::endl;
    }
  return 1;
}