// -*- C++ -*-
/*!
 * @file  PulseAudioInput.cpp
 * @author Yosuke Matsusaka <yosuke.matsusaka@aist.go.jp> and OpenHRI development team
 *
 * Copyright (C) 2010
 *     Intelligent Systems Research Institute,
 *     National Institute of
 *         Advanced Industrial Science and Technology (AIST), Japan
 *     All rights reserved.
 *
 * @date $Date$
 *
 * $Id$
 */

#include "PulseAudioInput.h"

// Module specification
// <rtc-template block="module_spec">
static const char* pulseaudioinput_spec[] =
  {
    "implementation_id", "PulseAudioInput",
    "type_name",         "PulseAudioInput",
    "description",       "Audio input component using pulseaudio (C++ implementation)",
    "version",           "0.0.1",
    "vendor",            "AIST",
    "category",          "communication",
    "activity_type",     "DataFlowComponent",
    "max_instance",      "1",
    "language",          "C++",
    "lang_type",         "script",
    "conf.default.InputSampleRate", "16000",
    "conf.default.InputSampleByte", "int16",
    "conf.default.InputChannelNumbers", "1",
    "conf.__widget__.InputSampleByte", "radio",
    "conf.__constraints__.InputSampleByte", "(int8, int16, int24)",
    "conf.__widget__.InputChannelNumbers", "spin",
    "conf.__constraints__.InputChannelNumbers", "x >= 1",
    "conf.__widget__.InputSampleRate", "spin",
    "conf.__constraints__.InputSampleRate", "x >= 1",
    ""
  };
// </rtc-template>

/*!
 * @brief constructor
 * @param manager Maneger Object
 */
PulseAudioInput::PulseAudioInput(RTC::Manager* manager)
    // <rtc-template block="initializer">
  : RTC::DataFlowComponentBase(manager),
    m_out_dataOut("SampleOutPort0", m_out_data)

    // </rtc-template>
{
}

/*!
 * @brief destructor
 */
PulseAudioInput::~PulseAudioInput()
{
}

RTC::ReturnCode_t PulseAudioInput::onInitialize()
{
  // Registration: InPort/OutPort/Service
  // <rtc-template block="registration">
  // Set InPort buffers

  // Set OutPort buffer
  registerOutPort("SampleOutPort0", m_out_dataOut);

  // Set service provider to Ports

  // Set service consumers to Ports

  // Set CORBA Service Ports

  // </rtc-template>
  bindParameter("InputSampleRate", m_spec.rate, "16000");
  bindParameter("InputSampleByte", m_formatstr, "int16");
  bindParameter("InputChannelNumbers", m_channels, "1");

  return RTC::RTC_OK;
}

pa_sample_format PulseAudioInput::getFormat(std::string str)
{
  if(str == "int8")
  {
    return PA_SAMPLE_U8;
  }
  else if (str == "int16")
  {
    return PA_SAMPLE_S16LE;
  }
  else if (str == "int24")
  {
    return PA_SAMPLE_S24LE ;
  }
  else
  {
    return PA_SAMPLE_S16LE;
  }
}

RTC::ReturnCode_t PulseAudioInput::onActivated(RTC::UniqueId ec_id)
{
  try {
    m_spec.format = getFormat(m_formatstr);
    m_spec.channels = (uint8_t)m_channels;

    m_simple = pa_simple_new(
                  NULL,               //!< Server name, or NULL for default
                  "PulseAudioInput",  //!< A descriptive name for this client (application name, ...)
                  PA_STREAM_RECORD,   //!< Open this stream for recording or playback?
                  NULL,               //!< Sink (resp. source) name, or NULL for default
                  "record",           //!< A descriptive name for this client (application name, song title, ...)
                  &m_spec,            //!< The sample type to use
                  NULL,               //!< The channel map to use, or NULL for default
                  NULL,               //!< Buffering attributes, or NULL for default
                  &m_err );           //!< A pointer where the error code is stored when the routine returns NULL. It is OK to pass NULL here.
    if( m_simple == NULL )
    {
      throw m_err;
    }
  } catch (...) {
    std::cout << pa_strerror(m_err) << std::endl;
  }

  return RTC::RTC_OK;
}

RTC::ReturnCode_t PulseAudioInput::onExecute(RTC::UniqueId ec_id)
{
  m_mutex.lock();
  if( m_simple )
  {
    int r;
    simple_recast *psimple = (simple_recast *)m_simple;
    pa_threaded_mainloop_lock(psimple->mainloop);

    while (!psimple->read_data) {
      r = pa_stream_peek(psimple->stream, &psimple->read_data, &psimple->read_length);
      if (!psimple->read_data)
      {
        pa_threaded_mainloop_wait(psimple->mainloop);
      }
    }
    m_out_data.data.length(psimple->read_length);  //!< set outport data length
    memcpy((void *)&(m_out_data.data[0]), (const uint8_t*) psimple->read_data, psimple->read_length);

    r = pa_stream_drop(psimple->stream);
    psimple->read_data = NULL;
    psimple->read_length = 0;
    psimple->read_index = 0;

    m_out_dataOut.write();

    pa_threaded_mainloop_unlock(psimple->mainloop);
  }
  m_mutex.unlock();
  return RTC::RTC_OK;
}

RTC::ReturnCode_t PulseAudioInput::onDeactivated(RTC::UniqueId ec_id)
{
  m_mutex.lock();
  if(m_simple)
  {
    pa_simple_free(m_simple);
    m_simple = NULL;
  }
  m_mutex.unlock();
  return RTC::RTC_OK;
}

RTC::ReturnCode_t PulseAudioInput::onFinalize()
{
  m_mutex.lock();
  if(m_simple)
  {
    pa_simple_free(m_simple);
    m_simple = NULL;
  }
  m_mutex.unlock();
  return RTC::RTC_OK;
}

/*!
 * @brief constructor
 */
PulseAudioInputManager::PulseAudioInputManager()
{
  m_manager = RTC::Manager::init(0,NULL);
  m_manager->init(0,NULL);
  m_manager->setModuleInitProc(ModuleInit);
  m_manager->activateManager();
}

/*!
 * @brief destructor
 */
PulseAudioInputManager::~PulseAudioInputManager()
{
}

void PulseAudioInputManager::start()
{
  m_manager->runManager();
}

void PulseAudioInputManager::ModuleInit(RTC::Manager* manager)
{
  RTC::RtcBase* comp;

  PulseAudioInputInit(manager);
  comp = manager->createComponent("PulseAudioInput");

  if (comp == NULL)
  {
  }
  return;
}


extern "C"
{
  void PulseAudioInputInit(RTC::Manager* manager)
  {
    coil::Properties profile(pulseaudioinput_spec);
    manager->registerFactory(profile,
                           RTC::Create<PulseAudioInput>,
                           RTC::Delete<PulseAudioInput>);
  }
};

int main(int argc, char** argv)
{
  PulseAudioInputManager input_manager;

  input_manager.start();
  return 0;
}
