// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/renderer/tts_dispatcher.h"

#include "base/strings/utf_string_conversions.h"
#include "chrome/common/tts_messages.h"
#include "chrome/common/tts_utterance_request.h"
#include "content/public/renderer/render_thread.h"
#include "third_party/blink/public/platform/web_speech_synthesis_utterance.h"
#include "third_party/blink/public/platform/web_speech_synthesis_voice.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/platform/web_vector.h"

using blink::WebSpeechSynthesisUtterance;
using blink::WebSpeechSynthesisVoice;
using blink::WebSpeechSynthesizerClient;
using blink::WebString;
using blink::WebVector;
using content::RenderThread;

int TtsDispatcher::next_utterance_id_ = 1;

TtsDispatcher::TtsDispatcher(WebSpeechSynthesizerClient* client)
    : synthesizer_client_(client) {
  RenderThread::Get()->AddObserver(this);
}

TtsDispatcher::~TtsDispatcher() {
  RenderThread::Get()->RemoveObserver(this);
}

bool TtsDispatcher::OnControlMessageReceived(const IPC::Message& message) {
  IPC_BEGIN_MESSAGE_MAP(TtsDispatcher, message)
    IPC_MESSAGE_HANDLER(TtsMsg_SetVoiceList, OnSetVoiceList)
    IPC_MESSAGE_HANDLER(TtsMsg_DidStartSpeaking, OnDidStartSpeaking)
    IPC_MESSAGE_HANDLER(TtsMsg_DidFinishSpeaking, OnDidFinishSpeaking)
    IPC_MESSAGE_HANDLER(TtsMsg_DidPauseSpeaking, OnDidPauseSpeaking)
    IPC_MESSAGE_HANDLER(TtsMsg_DidResumeSpeaking, OnDidResumeSpeaking)
    IPC_MESSAGE_HANDLER(TtsMsg_WordBoundary, OnWordBoundary)
    IPC_MESSAGE_HANDLER(TtsMsg_SentenceBoundary, OnSentenceBoundary)
    IPC_MESSAGE_HANDLER(TtsMsg_MarkerEvent, OnMarkerEvent)
    IPC_MESSAGE_HANDLER(TtsMsg_WasInterrupted, OnWasInterrupted)
    IPC_MESSAGE_HANDLER(TtsMsg_WasCancelled, OnWasCancelled)
    IPC_MESSAGE_HANDLER(TtsMsg_SpeakingErrorOccurred, OnSpeakingErrorOccurred)
  IPC_END_MESSAGE_MAP()

  // Always return false because there may be multiple TtsDispatchers
  // and we want them all to have a chance to handle this message.
  return false;
}

void TtsDispatcher::UpdateVoiceList() {
  RenderThread::Get()->Send(new TtsHostMsg_InitializeVoiceList());
}

void TtsDispatcher::Speak(const WebSpeechSynthesisUtterance& web_utterance) {
  int id = next_utterance_id_++;

  utterance_id_map_[id] = web_utterance;

  TtsUtteranceRequest utterance;
  utterance.id = id;
  utterance.text = web_utterance.GetText().Utf8();
  utterance.lang = web_utterance.Lang().Utf8();
  utterance.voice = web_utterance.Voice().Utf8();
  utterance.volume = web_utterance.Volume();
  utterance.rate = web_utterance.Rate();
  utterance.pitch = web_utterance.Pitch();
  RenderThread::Get()->Send(new TtsHostMsg_Speak(utterance));
}

void TtsDispatcher::Pause() {
  RenderThread::Get()->Send(new TtsHostMsg_Pause());
}

void TtsDispatcher::Resume() {
  RenderThread::Get()->Send(new TtsHostMsg_Resume());
}

void TtsDispatcher::Cancel() {
  RenderThread::Get()->Send(new TtsHostMsg_Cancel());
}

WebSpeechSynthesisUtterance TtsDispatcher::FindUtterance(int utterance_id) {
  base::hash_map<int, WebSpeechSynthesisUtterance>::const_iterator iter =
      utterance_id_map_.find(utterance_id);
  if (iter == utterance_id_map_.end())
    return WebSpeechSynthesisUtterance();
  return iter->second;
}

void TtsDispatcher::OnSetVoiceList(const std::vector<TtsVoice>& voices) {
  WebVector<WebSpeechSynthesisVoice> out_voices(voices.size());
  for (size_t i = 0; i < voices.size(); ++i) {
    out_voices[i] = WebSpeechSynthesisVoice();
    out_voices[i].SetVoiceURI(WebString::FromUTF8(voices[i].voice_uri));
    out_voices[i].SetName(WebString::FromUTF8(voices[i].name));
    out_voices[i].SetLanguage(WebString::FromUTF8(voices[i].lang));
    out_voices[i].SetIsLocalService(voices[i].local_service);
    out_voices[i].SetIsDefault(voices[i].is_default);
  }
  synthesizer_client_->SetVoiceList(out_voices);
}

void TtsDispatcher::OnDidStartSpeaking(int utterance_id) {
  if (utterance_id_map_.find(utterance_id) == utterance_id_map_.end())
    return;

  WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
  if (utterance.IsNull())
    return;

  synthesizer_client_->DidStartSpeaking(utterance);
}

void TtsDispatcher::OnDidFinishSpeaking(int utterance_id) {
  WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
  if (utterance.IsNull())
    return;

  synthesizer_client_->DidFinishSpeaking(utterance);
  utterance_id_map_.erase(utterance_id);
}

void TtsDispatcher::OnDidPauseSpeaking(int utterance_id) {
  WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
  if (utterance.IsNull())
    return;

  synthesizer_client_->DidPauseSpeaking(utterance);
}

void TtsDispatcher::OnDidResumeSpeaking(int utterance_id) {
  WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
  if (utterance.IsNull())
    return;

  synthesizer_client_->DidResumeSpeaking(utterance);
}

void TtsDispatcher::OnWordBoundary(int utterance_id, int char_index) {
  CHECK(char_index >= 0);

  WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
  if (utterance.IsNull())
    return;

  synthesizer_client_->WordBoundaryEventOccurred(
      utterance, static_cast<unsigned>(char_index));
}

void TtsDispatcher::OnSentenceBoundary(int utterance_id, int char_index) {
  CHECK(char_index >= 0);

  WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
  if (utterance.IsNull())
    return;

  synthesizer_client_->SentenceBoundaryEventOccurred(
      utterance, static_cast<unsigned>(char_index));
}

void TtsDispatcher::OnMarkerEvent(int utterance_id, int char_index) {
  // Not supported yet.
}

void TtsDispatcher::OnWasInterrupted(int utterance_id) {
  WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
  if (utterance.IsNull())
    return;

  // The web speech API doesn't support "interrupted".
  synthesizer_client_->DidFinishSpeaking(utterance);
  utterance_id_map_.erase(utterance_id);
}

void TtsDispatcher::OnWasCancelled(int utterance_id) {
  WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
  if (utterance.IsNull())
    return;

  // The web speech API doesn't support "cancelled".
  synthesizer_client_->DidFinishSpeaking(utterance);
  utterance_id_map_.erase(utterance_id);
}

void TtsDispatcher::OnSpeakingErrorOccurred(int utterance_id,
                                            const std::string& error_message) {
  WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
  if (utterance.IsNull())
    return;

  // The web speech API doesn't support an error message.
  synthesizer_client_->SpeakingErrorOccurred(utterance);
  utterance_id_map_.erase(utterance_id);
}
