/*!
  \file
  \brief モニタ対応の Connection クラス

  \author Satofumi KAMIMURA

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

#include "mConnection.h"
#include "MonitorDataHandler.h"
#include "DeviceManager.h"
#include "LogNameHolder.h"

using namespace qrk;
using namespace std;


struct mConnection::pImpl
{
  Connection* connection_;
  ExecutionType::Type type_;
  MonitorDataHandler* handler_;

  long baudrate_;


  pImpl(Connection* connection)
    : connection_(connection), type_(ExecutionType::object()->type()),
      baudrate_(0)
  {
    if ((type_ == ExecutionType::Recording) ||
        (type_ == ExecutionType::Playback)) {
      string log_name = LogNameHolder::object()->name("mConnection");
      handler_ = new MonitorDataHandler(log_name, type_);
    }
  }


  ~pImpl(void)
  {
    delete handler_;
  }


  bool connect(const char* device, long port)
  {
    // connect() については、return 情報のみ管理する
    bool ret = false;
    if (type_ != ExecutionType::Playback) {
      ret = connection_->connect(device, port);
    }

    if ((type_ == ExecutionType::Recording) ||
        (type_ == ExecutionType::Playback)) {
      if (handler_->fetch(ret) < 0) {
        disconnect();
        return false;
      }
    }
    return ret;
  }


  void disconnect(void)
  {
    connection_->disconnect();
  }


  const char* what(void)
  {
    // エラーメッセージを記録し、再生できるようにする
    if ((type_ == ExecutionType::Recording) ||
        (type_ == ExecutionType::Playback)) {
      string error_message = connection_->what();
      handler_->fetch(error_message);
      return connection_->what();

    } else {
      return connection_->what();
    }
  }


  bool setBaudrate(long baudrate)
  {
    // ボーレート値と戻り値を記録する
    bool ret = false;
    if (type_ != ExecutionType::Playback) {
      ret = connection_->setBaudrate(baudrate);
    }

    if ((type_ == ExecutionType::Recording) ||
        (type_ == ExecutionType::Playback)) {
      handler_->fetch(ret);
      handler_->fetch(baudrate);
      baudrate_ = baudrate;
    }
    return ret;
  }


  long baudrate(void)
  {
    return ((type_ == ExecutionType::Playback) ?
            baudrate_ : connection_->baudrate());
  }


  bool isConnected(void)
  {
    // 戻り値のみを記録する
    bool ret = false;
    if (type_ != ExecutionType::Playback) {
      ret = connection_->isConnected();
    }

    if ((type_ == ExecutionType::Recording) ||
        (type_ == ExecutionType::Playback)) {
      handler_->fetch(ret);
    }
    return ret;
  }


  int send(const char* data, size_t count)
  {
    int send_size = 0;
    if (type_ != ExecutionType::Playback) {
      send_size = connection_->send(data, count);
    }

    if (type_ == ExecutionType::Recording) {
      // 記録する必要はないが、デバッグ情報として使うために残しておく
      handler_->fetch(const_cast<char*>(data), send_size, "S");

    } else if (type_ == ExecutionType::Playback) {
      // 再生時は、送信しない
      send_size = handler_->fetch(static_cast<char*>(NULL), count);
      if (send_size < 0) {
        disconnect();
      }
    }
    return send_size;
  }


  int receive(char* data, size_t count, int timeout)
  {
    int receive_size = 0;
    if (type_ != ExecutionType::Playback) {
      receive_size = connection_->receive(data, count, timeout);
    }

    if (type_ == ExecutionType::Recording) {
      // !!! 同じ時間のデータを１行にまとめるようにする
      // !!! 実際に読み出した文字数が記録できないのであれば、非現実的
      handler_->fetch(data, receive_size, "R");

    } else if (type_ == ExecutionType::Playback) {
      receive_size = handler_->fetch(data, count);
      if (receive_size < 0) {
        disconnect();
      }
    }
    return receive_size;
  }


  size_t size(void)
  {
    int data_size = 0;
    if (type_ != ExecutionType::Playback) {
      data_size = connection_->size();
    }

    if ((type_ == ExecutionType::Recording) ||
        (type_ == ExecutionType::Playback)) {
      handler_->fetch(data_size);
    }
    return data_size;
  }
};


mConnection::mConnection(Connection* connection) : pimpl(new pImpl(connection))
{
}


mConnection::~mConnection(void)
{
}


const char* mConnection::what(void) const
{
  return pimpl->what();
}


bool mConnection::connect(const char* device, long port)
{
  return pimpl->connect(device, port);
}


void mConnection::disconnect(void)
{
  pimpl->disconnect();
}


bool mConnection::setBaudrate(long baudrate)
{
  return pimpl->setBaudrate(baudrate);
}


long mConnection::baudrate(void) const
{
  return pimpl->baudrate();
}


bool mConnection::isConnected(void) const
{
  return pimpl->isConnected();
}


int mConnection::send(const char* data, size_t count)
{
  if (! isConnected()) {
    return -1;
  }
  return pimpl->send(data, count);
}


int mConnection::receive(char* data, size_t count, int timeout)
{
  if (! isConnected()) {
    return -1;
  }
  return pimpl->receive(data, count, timeout);
}


size_t mConnection::size(void) const
{
  return pimpl->size();
}


void mConnection::flush(void)
{
  pimpl->connection_->flush();
}


void mConnection::clear(void)
{
  pimpl->connection_->clear();
}


void mConnection::ungetc(const char ch)
{
  pimpl->connection_->ungetc(ch);
}
