/*
-----------------------------------------------------------------------------
This source file is part of OpenSpace3D
For the latest info, see http://www.openspace3d.com

Copyright (c) 2010 I-maginer

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA, or go to
http://www.gnu.org/copyleft/lesser.txt

You may alternatively use this source under the terms of a specific version of
the OpenSpace3D Unrestricted License provided you have obtained such a license from
I-maginer.
-----------------------------------------------------------------------------
*/


/*! \class Recognition
  *  \brief Management of the recognition class
  *  \
  *  \version 1.0
  *  \date mai  2010
  */

//! utils libraries
#include "speech.h"

//! Speech Event Callback
void __stdcall NotifyCallbackFunction(WPARAM wParam, LPARAM lParam)
{
   Speech* speech = (Speech*)lParam;
   speech->callbackEvent();
}


//! Speech Constructor
Speech::Speech()
{
	HRESULT hr = S_FALSE;
  pVoice = 0;
	m_bPause = FALSE; 
  m_bStop = TRUE;  
	m_DefaultRate = 0;
	m_DefaultVolume = 0;
  s_text = std::string("");
    
	hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&pVoice);	

	// We're interested in all TTS events
	if(SUCCEEDED(hr))
		hr = pVoice->SetInterest(SPFEI_ALL_TTS_EVENTS, SPFEI_ALL_TTS_EVENTS);
  else
    MMechostr(MSKDEBUG,"Error CoCreateInstance\n");

  if(SUCCEEDED(hr))
    hr = pVoice->SetNotifyCallbackFunction(NotifyCallbackFunction, NULL, LPARAM(this));
  else
    MMechostr(MSKDEBUG,"Error SetInterest\n");

	// Get default rate and volume
	if(SUCCEEDED(hr))
	  hr = pVoice->GetRate(&m_DefaultRate);
  else
    MMechostr(MSKDEBUG,"Error SetNotifyCallbackFunction\n");

	if(SUCCEEDED(hr))
    hr = pVoice->GetVolume(&m_DefaultVolume);
  else
    MMechostr(MSKDEBUG,"Error GetRate\n");
}


//! Speech Callback function
void Speech::callbackEvent()
{
  CSpEvent event;

  SPVOICESTATUS Stat;
  HRESULT hr = S_FALSE;
  std::string* param;

  while(event.GetFrom(pVoice) == S_OK)
  {
    switch(event.eEventId)
    {
      case SPEI_START_INPUT_STREAM:
        PostMessage(HScol, SPEECH_START_CB, (int)this, (LPARAM)NULL);
        break;

      case SPEI_END_INPUT_STREAM:
        m_bStop = TRUE;
				m_bPause = FALSE;
				PostMessage(HScol, SPEECH_END_CB, (int)this, (LPARAM)NULL);
        break;

      case SPEI_WORD_BOUNDARY:
        hr = pVoice->GetStatus(&Stat, NULL);
        if(FAILED(hr)) break;
        param = new std::string(s_text.substr(Stat.ulInputWordPos, Stat.ulInputWordLen));
			  if (!param->empty())
          PostMessage(HScol, SPEECH_WORD_CB, (int)this, (LPARAM)param);
        else
          SAFE_DELETE(param);
        break;

			case SPEI_SENTENCE_BOUNDARY:
        hr = pVoice->GetStatus(&Stat, NULL);
        if(FAILED(hr)) break;
        param = new std::string(s_text.substr(Stat.ulInputSentPos, Stat.ulInputSentLen));
        if (!param->empty())
				  PostMessage(HScol, SPEECH_TEXT_CB, (int)this, (LPARAM)param);
        else
          SAFE_DELETE(param);
        break;

			case SPEI_VISEME:
				PostMessage(HScol, SPEECH_VISEME_CB, (int)this, (LPARAM)(event.Viseme()));
        break;

			case SPEI_PHONEME:
        PostMessage(HScol, SPEECH_PHONEME_CB, (int)this, (LPARAM)(event.Phoneme()));
        break;

			case SPEI_TTS_PRIVATE:
        break;

      default:
        break;
    }
  }
}


//! Speech Destructor
Speech::~Speech()
{
    this->stop();
		pVoice->Release();
		pVoice = NULL;
}


//! To set the speech text
void Speech::SetSpeechText(std::string text)
{
	s_text = text;
}


//! To play the speech instance
void Speech::play()
{
	HRESULT hr = S_OK;

	if(SUCCEEDED(hr))
	{
		m_bStop = FALSE;
		if(!m_bPause)
		{
      wchar_t* wc = convertCharToLPCWSTR((char*)s_text.c_str()) ;
      hr = pVoice->Speak(wc, SPF_ASYNC|SPF_IS_XML|SPF_PURGEBEFORESPEAK, 0);
      SAFE_DELETE(wc);

      if( FAILED( hr ))
        MMechostr(MSKDEBUG,"Error : Speak\n");
		}

		pVoice->Resume();
		m_bPause = FALSE;
	}
}


//! To pause/resume the speech instance
void Speech::pauseResume()
{
	if(!m_bStop)
	{
		if(!m_bPause)
    {
	    // Pause the voice...
	    pVoice->Pause();
	    m_bPause = TRUE;   
		}
	}
}


//! To stop the speech instance
void Speech::stop()
{
	// Stop current rendering with a PURGEBEFORESPEAK...
	if(FAILED( pVoice->Speak(NULL, SPF_PURGEBEFORESPEAK, 0)))
		MMechostr(MSKDEBUG,">>>>>>>> Stop error\n");

	m_bPause = FALSE;
	m_bStop = TRUE;
}


//! To get the speech volume
int Speech::getVolumeSpeech()
{
  // Declare local identifiers:
	HRESULT hr = S_OK;
	int volumeSpeech = 0;
	hr = pVoice->GetVolume((USHORT*)&volumeSpeech);
	if(FAILED(hr)) 
		return -1;
	else
		return volumeSpeech;
}


//! To set the speech volume
void Speech::setVolumeSpeech(int volumeSpeech)
{
	pVoice->SetVolume((USHORT)volumeSpeech);
}


//! To get the speech rate
int Speech::getRateSpeech()
{
  // Declare local identifiers:
	HRESULT hr = S_OK;
	int rateSpeech = 0;
	hr = pVoice->GetRate((long*)&rateSpeech);
	if(FAILED(hr)) 
		return -1;
	else
		return rateSpeech;
}


//! To set the speech rate
void Speech::setRateSpeech(int rateSpeech)
{
	pVoice->SetRate(rateSpeech);
}


//! To get the speech voices
std::list <char *> Speech::getVoiceAll()
{
  ISpObjectToken* pToken;
  IEnumSpObjectTokens*  cpIEnum;
  
  // Declare local identifiers:
	HRESULT hr = S_OK;
	std::list <char *> lParamSp;
  ULONG sizel;

	hr = SpEnumTokens(SPCAT_VOICES, NULL/*L"Gender=Female;Language=409"*/, NULL/*L"Vendor=VoiceVendor1;Age=Child"*/, &cpIEnum);

  if (FAILED(hr))
  {
		MMechostr(MSKDEBUG,"getVoiceAll ->  Erreur : EnumTokens\n");
    return lParamSp;
  }
  else
	{
    cpIEnum->GetCount(&sizel);
    if (sizel <=0)
    {
		  MMechostr(MSKDEBUG,"getVoiceAll ->  Erreur : EnumTokens\n");
      return lParamSp;
    }
    else
 		while (cpIEnum->Next(1, &pToken, NULL) == S_OK)
		{
      CSpDynamicString dstrText;
		  hr = SpGetDescription(pToken, &dstrText);
		  if (SUCCEEDED(hr))
			  lParamSp.push_back(dstrText.CopyToChar());
      else
				MMechostr(MSKDEBUG,"getVoiceAll -> SpGetDescription : ERROR\n");
      pToken->Release();

      //$MS
      dstrText.Clear();
      delete dstrText;
		}
	}
  cpIEnum->Release();

	return lParamSp;
}


//! To set the speech voice
void Speech::SetSpeechVoice(std::string voice)
{
  HRESULT hr = S_FALSE;
  IEnumSpObjectTokens* cpIEnum;
  ISpObjectToken* pToken;
  bool testVoice = true;
  
	hr = SpEnumTokens(SPCAT_VOICES, NULL, NULL, &cpIEnum);
	if (SUCCEEDED(hr))
	{
    wchar_t* dstrTextTest;
    char* lpszVoiceId;
 
		while ((cpIEnum->Next(1, &pToken, NULL) == S_OK) && (testVoice == true))
		{
      hr = SpGetDescription(pToken, &dstrTextTest);
      char* textV = convertWcharToChar(dstrTextTest);
   
      if (SUCCEEDED(hr))
			{
        if (strcmp(textV, voice.c_str()) == 0)
				{
          hr = pVoice->SetVoice(pToken);
          if (SUCCEEDED(hr)) 
            MMechostr(MSKDEBUG,"SetSpeechVoice -> SetVoice : OK \n");
          else
            MMechostr(MSKDEBUG,"SetSpeechVoice -> SetVoice : ERROR\n");
					testVoice = false;
				}
        else 
          MMechostr(MSKDEBUG,"SetSpeechVoice -> lpszVoiceId <> voice\n");
			}
      else
          MMechostr(MSKDEBUG,"SetSpeechVoice -> SpGetDescription : ERROR\n");

      SAFE_DELETE(textV);
      pToken->Release();
		}
	}
  else 
    MMechostr(MSKDEBUG,"SetSpeechVoice -> Erreur EnumTokens : \n");

 cpIEnum->Release();
}


//! To get the speech voice
std::string Speech::getSpeechVoice()
{
  ISpObjectToken* pToken;
  std::string token;
  
  HRESULT hr = S_FALSE;
  CSpDynamicString dstrText;
	
  pVoice->GetVoice(&pToken);
  hr = SpGetDescription(pToken, &dstrText);
  token = dstrText.CopyToChar();

  dstrText.Clear();
  delete  dstrText;
  pToken->Release();

  return token;
}


