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

  \author Satofumi KAMIMURA

  $Id: DeviceManager.cpp 1386 2009-10-09 08:00:11Z satofumi $
*/

#include "DeviceManager.h"
#include "EventScheduler.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;
    }
  };
}


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


  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)
{
  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 NULL;
  } 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 NULL;
  } else {
    return it->second;
  }
}
