// -*- C++ -*-
/*!
 * @file  PortAudioInput.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 "PortAudioInput.h"

// Module specification
// <rtc-template block="module_spec">
static const char* portaudioinput_spec[] =
  {
    "implementation_id", "PortAudioInput",
    "type_name",         "PortAudioInput",
    "description",       "Audio input component using portaudio (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>

int StreamCB( const void *inputBuffer,
			  void *outputBuffer,
			  unsigned long framesPerBuffer,
			  const PaStreamCallbackTimeInfo *timeInfo,
			  PaStreamCallbackFlags statusFlags,
			  void *userData )
{
  PortAudioInput *p = (PortAudioInput *)userData;
  unsigned long nbytes = p->m_totalframes;
  if(FRAMES_PER_BUFFER != framesPerBuffer){
    nbytes = framesPerBuffer * p->m_totalframes / FRAMES_PER_BUFFER;
  }
  p->ReadBufferCB(inputBuffer, nbytes);
  return 0;
}

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

    // </rtc-template>
{
}

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

RTC::ReturnCode_t PortAudioInput::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_samplerate, "16000");
  bindParameter("InputSampleByte", m_formatstr, "int16");
  bindParameter("InputChannelNumbers", m_channels, "1");

  return RTC::RTC_OK;
}

PaSampleFormat PortAudioInput::getFormat(std::string str)
{
  if(str == "int8")
  {
    return paInt8;
  }
  else if (str == "int16")
  {
    return paInt16;
  }
  else if (str == "int24")
  {
    return paInt24;
  }
  else
  {
    return paInt16;
  }
}

RTC::ReturnCode_t PortAudioInput::onActivated(RTC::UniqueId ec_id)
{
  PaStreamParameters inputParameters;

  try {
    m_err = Pa_Initialize();
    if( m_err != paNoError )
    {
      throw m_err;
    }

    m_format = getFormat(m_formatstr);
    m_totalframes = FRAMES_PER_BUFFER * m_channels;
    m_err = Pa_GetSampleSize(m_format);
    if( m_err > 0 )
    {
      m_totalframes *= m_err;
    }

    inputParameters.device = Pa_GetDefaultInputDevice(); //!< default input device
    if( inputParameters.device < 0 )
    {
      throw (paNotInitialized);
    }

    inputParameters.channelCount = m_channels;
    inputParameters.sampleFormat = m_format;
    inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
    inputParameters.hostApiSpecificStreamInfo = NULL;
#if 0
    m_err = Pa_OpenStream(
          &m_stream,         //!< PortAudioStream
          &inputParameters,  //!< InputParameters
          NULL,              //!< outputParameters
          m_samplerate,      //!< sampleRate
          FRAMES_PER_BUFFER, //!< framesPerBuffer
          paClipOff,         //!< streamFlags:we won't output out of range samples so don't bother clipping
          NULL,              //!< streamCallback:no callback, use blocking API
          NULL );            //!< no callback, so no callback userData
#endif
	m_err = Pa_OpenStream(
          &m_stream,         //!< PortAudioStream
          &inputParameters,  //!< InputParameters
          NULL,              //!< outputParameters
          m_samplerate,      //!< sampleRate
          FRAMES_PER_BUFFER, //!< framesPerBuffer
          paClipOff,         //!< streamFlags:we won't output out of range samples so don't bother clipping
          StreamCB,          //!< streamCallback
          this );            //!< callback userData
    if( m_err != paNoError )
    {
      throw m_err;
    }

    m_err = Pa_StartStream( m_stream );
    if( m_err != paNoError )
    {
      throw m_err;
    }

  } catch (...) {
    std::cout << Pa_GetErrorText(m_err) << std::endl;
  }

  return RTC::RTC_OK;
}

void PortAudioInput::ReadBufferCB(const void *data, unsigned long fream_len)
{
  m_mutex.lock();
  m_out_data.data.length(fream_len); //!< set outport data length
  memcpy((void *)&(m_out_data.data[0]), data, fream_len);
  m_out_dataOut.write();
  m_mutex.unlock();
  return;
}

RTC::ReturnCode_t PortAudioInput::onExecute(RTC::UniqueId ec_id)
{
  //! The data read from the device is output.
#if 0
  m_mutex.lock();
  if ( Pa_IsStreamActive(m_stream) )
  {
    m_out_data.data.length(m_totalframes); //!< set outport data length
    m_err = Pa_ReadStream( m_stream, (void *)&(m_out_data.data[0]), FRAMES_PER_BUFFER );
    if( m_err != paNoError )
    {
      std::cout << Pa_GetErrorText(m_err) << std::endl;
    }
    m_out_dataOut.write();
  }
  m_mutex.unlock();
#endif
  return RTC::RTC_OK;
}

RTC::ReturnCode_t PortAudioInput::onDeactivated(RTC::UniqueId ec_id)
{
  m_mutex.lock();
  try {
    if (m_stream)
    {
      if(!Pa_IsStreamStopped( m_stream ))
      {
        m_err = Pa_AbortStream( m_stream );
        if( m_err != paNoError )
        {
          throw m_err;
        }
      }

      m_err = Pa_CloseStream( m_stream );
      if( m_err != paNoError )
      {
          throw m_err;
      }
    }
  } catch (...) {
    std::cout << Pa_GetErrorText(m_err) << std::endl;
  }
  m_mutex.unlock();
  return RTC::RTC_OK;
}

RTC::ReturnCode_t PortAudioInput::onFinalize()
{
  m_mutex.lock();
  try {
    m_out_dataOut.deactivateInterfaces();
    if (m_stream)
    {
      if(!Pa_IsStreamStopped( m_stream ))
      {
        m_err = Pa_AbortStream( m_stream );
        if( m_err != paNoError )
        {
          throw m_err;
        }
      }

      m_err = Pa_CloseStream( m_stream );
      if( m_err != paNoError )
      {
        throw m_err;
      }
    }
  } catch (...) {
    std::cout << Pa_GetErrorText(m_err) << std::endl;
  }
  Pa_Terminate();
  m_mutex.unlock();
  return RTC::RTC_OK;
}

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

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

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

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

  PortAudioInputInit(manager);
  comp = manager->createComponent("PortAudioInput");

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


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

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

  input_manager.start();
  return 0;
}
