/*!
  \file
  \brief デバイス管理

  \author Satofumi KAMIMURA

  $Id: DeviceManager.cpp 1535 2009-11-18 14:58:34Z satofumi $
*/

#include "DeviceManager.h"
#include "EventScheduler.h"
#include "ExecutionType.h"
#include "BeegoDrive_device.h"
#include "UrgDevice_device.h"
#include "log_printf.h"
#include <map>
#include <string>
#include <cstring>

using namespace qrk;
using namespace luabind;
using namespace std;


namespace
{
  enum {
    FirstPort = 9608,
  };


  typedef map<string, long> DevicePorts;
  typedef map<string, Device*> DeviceAliases;
  typedef map<string, OdeModel*> ModelAliases;


  class Event_device : public Device
  {
    object function_;

  public:
    Event_device(const object& function) : function_(function)
    {
    }


    ~Event_device(void)
    {
    }


    void setParameter(const char* type, const char* parameter)
    {
      static_cast<void>(type);
      static_cast<void>(parameter);

      // 実装しない
    }


    void activate(void)
    {
      // 実装しない
    }


    void execute(void)
    {
      if (function_ && function_.is_valid()) {
        function_();
      }
    }


    size_t nextExecuteInterval(void) const
    {
      return 1;
    }
  };


  class DummyDevice : public Device, public OdeModel
  {
  public:
    void setParameter(const char* type, const char* parameter)
    {
      static_cast<void>(type);
      static_cast<void>(parameter);
    }


    void activate(void)
    {
    }


    void execute(void)
    {
    }


    size_t nextExecuteInterval(void) const
    {
      return 1000;
    }


    void setPosition(const Position<long>& position,
                     OdeModel* model, bool fixed)
    {
      static_cast<void>(position);
      static_cast<void>(model);
      static_cast<void>(fixed);
    }


    Position<long> position(void)
    {
      return Position<long>(0, 0, deg(0));
    }


    void setPosition3d(const Point3d<long>& position,
                       double ax, double ay, double az,
                       const Angle& angle,
                       OdeModel* model, bool fixed)
    {
      static_cast<void>(position);
      static_cast<void>(ax);
      static_cast<void>(ay);
      static_cast<void>(az);
      static_cast<void>(angle);
      static_cast<void>(model);
      static_cast<void>(fixed);
    }


    void position3d(long& px, long& py, long& pz,
                    double& ax, double& ay, double& az)
    {
      static_cast<void>(px);
      static_cast<void>(py);
      static_cast<void>(pz);
      static_cast<void>(ax);
      static_cast<void>(ay);
      static_cast<void>(az);
    }


    dBodyID objectId(void) const
    {
      return 0;
    }
  };
}


struct DeviceManager::pImpl
{
  long next_port_;
  DevicePorts device_ports_;
  DeviceAliases device_aliases_;
  ModelAliases model_aliases_;
  EventScheduler event_scheduler_;

  DummyDevice dummy_device_;


  pImpl(void) : next_port_(FirstPort)
  {
  }


  static pImpl* object(void)
  {
    static pImpl singleton_object;
    return &singleton_object;
  }


  void terminate(void)
  {
    for (DeviceAliases::iterator it = device_aliases_.begin();
         it != device_aliases_.end(); ++it) {
      delete it->second;
    }
    device_aliases_.clear();
  }
};


DeviceManager::DeviceManager(void) : pimpl(pImpl::object())
{
}


DeviceManager::~DeviceManager(void)
{
}


void DeviceManager::terminate(void)
{
  pimpl->terminate();
}


long DeviceManager::nextPort(void)
{
  return pimpl->next_port_++;
}


void DeviceManager::registerDevicePort(const char* device, long port)
{
  pimpl->device_ports_[device] = port;
}


long DeviceManager::port(const char* device) const
{
  DevicePorts::iterator it = pimpl->device_ports_.find(device);
  if (it == pimpl->device_ports_.end()) {
    return 0;
  } else {
    return it->second;
  }
}


void DeviceManager::createDevice(const char* device_name,
                                 const char* class_name, const char* alias)
{
  if (ExecutionType::object()->type() == ExecutionType::Playback) {
    // Playback 時にはデバイスを登録しない
    return;
  }

  Device* device = NULL;
  OdeModel* model = NULL;
  if (! strcmp(class_name, "BeegoDrive")) {
    BeegoDrive_device* p = new BeegoDrive_device(device_name);
    device = p;
    model = p;

  } else if (! strcmp(class_name, "UrgDevice")) {
    UrgDevice_device* p = new UrgDevice_device(device_name);
    device = p;
    model = p;
  }

  if (! device) {
    // 不明なクラスが指定された場合
    log_printf("DeviceManager::createDevice(): Unknown device: %s\n",
               class_name);
    return;
  }

  // 作成したデバイスの登録
  pimpl->device_aliases_[alias] = device;
  pimpl->model_aliases_[alias] = model;
  pimpl->event_scheduler_.registerDevice(device);
}


void DeviceManager::createEvent(const luabind::object& function,
                                const char* alias)
{
  Event_device* event = new Event_device(function);
  pimpl->device_aliases_[alias] = event;
  pimpl->event_scheduler_.registerDevice(event);
}


void DeviceManager::removeEvent(const char* alias)
{
  // !!! 存在しなければ戻る

  Device* event = pimpl->device_aliases_[alias];
  pimpl->event_scheduler_.removeDevice(event);
  delete event;
  pimpl->device_aliases_.erase(alias);
}


Device* DeviceManager::device(const char* alias)
{
  DeviceAliases::iterator it = pimpl->device_aliases_.find(alias);
  if (it == pimpl->device_aliases_.end()) {
    return &pimpl->dummy_device_;
  } else {
    return it->second;
  }
}


OdeModel* DeviceManager::model(const char* alias)
{
  ModelAliases::iterator it = pimpl->model_aliases_.find(alias);
  if (it == pimpl->model_aliases_.end()) {
    return &pimpl->dummy_device_;
  } else {
    return it->second;
  }
}
