/*!
  \file
  \brief シミュレーション時のメイン関数実行用

  \author Satofumi KAMIMURA

  $Id: SimulationMain.cpp 1449 2009-10-25 19:30:03Z satofumi $
*/

#include "SimulationMain.h"
#include "ExecutionType.h"
#include "ModelManager.h"
#include "fileExist.h"
#include "luabindInit.h"
#include "luabindSimulator.h"
#include "luabindObstacles.h"
#include "luabindGeometry.h"
#include "luabindSystem.h"
#include "LuaHandler.h"
#include "log_printf.h"

using namespace qrk;
using namespace std;


struct SimulationMain::pImpl
{
  EventScheduler event_scheduler_;
  MainFunction* function_;
  int argc_;
  char **argv_;


  pImpl(MainFunction* function, int argc, char *argv[])
    : function_(function), argc_(argc), argv_(argv)
  {
  }


  void loadSettings(const char* file = "")
  {
    string setting_file = file;
    if (setting_file.empty()) {
      // カレント・ディレクトリ、~/.qrkrc から探索
      const string search_path[] = {
        "./simulation_settings.lua",
        "~/.qrkrc/simulation_settings.lua",
      };

      size_t n = sizeof(search_path) / sizeof(search_path[0]);
      size_t i;
      for (i = 0; i < n; ++i) {
        string path = search_path[i];

        // !!! 専用の関数にするべき
        if (path[0] == '~') {
          const char* home_path = getenv("HOME");
          if (home_path) {
            path.replace(0, 1, home_path);
          }
        }

        if (fileExist(path.c_str())) {
          setting_file = path;
          break;
        }
      }
      if (i == n) {
        log_printf("no simulation_settings.lua file.\n");
        return;
      }
    }

    lua_State* L = luabindInit(LuaHandler::newthread(LuaHandler::pointer()));
    luabindSystem(L);
    luabindGeometry(L);
    luabindSimulator(L);
    luabindObstacles(L);
    LuaHandler::dofile(L, setting_file.c_str());
  }
};


SimulationMain::SimulationMain(MainFunction* function, int argc, char *argv[])
  : pimpl(new pImpl(function, argc, argv))
{
}


SimulationMain::~SimulationMain(void)
{
}


void SimulationMain::run(void)
{
  // デバイスへのポート割り振りの初期化
  // !!! プログラム実行時の引数でファイルが指定されていれば
  // !!! それを使うようにする
  pimpl->loadSettings();

  // Monitoring モードのときは simulation_settings で登録されたモデルを
  // クリアする。必要ならば、ユーザプログラム内で配置するよう説明すること
  ExecutionType::Type type = ExecutionType::object()->type();
  if ((type == ExecutionType::Monitoring) ||
      (type == ExecutionType::Playback)) {
    ModelManager model_manager;
    model_manager.clear();
  }

  // イベント・スケジューラの動作開始
  event_scheduler_.start();

  // 指定された関数を呼び出す
  pimpl->function_(pimpl->argc_, pimpl->argv_);

  // !!! GUI 上のログ・コンソールに出力すべき
  event_scheduler_.terminate();

  if (type == qrk::ExecutionType::Simulation) {
    fprintf(stderr, "simulation");
  } else if (type == qrk::ExecutionType::Playback) {
    fprintf(stderr, "playback");
  } else if (type == qrk::ExecutionType::Monitoring) {
    fprintf(stderr, "monitoring");
  }
  fprintf(stderr, " end.\n");
}
