// Copyright (c) 2009 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/worker/nativewebworker_impl.h"

#include "base/compiler_specific.h"

#undef LOG

#include "base/logging.h"
#include "base/thread.h"
#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
#include "third_party/WebKit/WebKit/chromium/public/WebKitClient.h"
#include "third_party/WebKit/WebKit/chromium/public/WebWorkerClient.h"

// TODO(sehr): This will be changed to point to the real NaCl headers once
// the builds are integrated.
#include "chrome/worker/nativewebworker_stub.h"

namespace {
// Remember the main thread's message loop, so that the listener thread
// can post messages to it when the worker wants to post to the renderer.
static MessageLoop* g_main_thread_message_loop;

// PostMessageTask encapsulates sending messages from native web workers to
// the renderer by placing them on the main thread's message loop.
class PostMessageTask : public Task {
 public:
  PostMessageTask(const char* bufp, WebKit::WebWorkerClient* client)
      : message_string_(WebKit::WebString::fromUTF8(bufp)),
        client_(client) {
  }
  ~PostMessageTask() { }
  void Run() {
    WebKit::WebMessagePortChannelArray empty_array;
    client_->postMessageToWorkerObject(message_string_, empty_array);
  }

 private:
  WebKit::WebString message_string_;
  WebKit::WebWorkerClient* client_;

  DISALLOW_COPY_AND_ASSIGN(PostMessageTask);
};

// PostToRenderer places a string in bufp in a message and enqueues
// a task to send the message to the renderer.
static void PostToRenderer(const char* bufp,
                           WebKit::WebWorkerClient* client) {
  g_main_thread_message_loop->PostTask(FROM_HERE,
                                       new PostMessageTask(bufp, client));
}

class ListenerTask : public Task {
 public:
  ListenerTask(WebKit::WebWorkerClient* client,
               struct NaClDesc* chrome_desc)
      : client_(client),
        chrome_desc_(chrome_desc) { }
  ~ListenerTask() { }
  void Run() {
    NaClSrpcListenerLoop(chrome_desc_, PostToRenderer, client_);
  }

 private:
  WebKit::WebWorkerClient* client_;
  struct NaClDesc* chrome_desc_;

  DISALLOW_COPY_AND_ASSIGN(ListenerTask);
};
}

// NativeWebWorkerListenerThread encapsulates a listener for SRPC messages from
// native web workers.
class NativeWebWorkerListenerThread : public base::Thread {
 public:
  explicit NativeWebWorkerListenerThread(const char* str) : Thread(str) { }
  ~NativeWebWorkerListenerThread() {}
  static NativeWebWorkerListenerThread* Create() {
    return new NativeWebWorkerListenerThread("NativeWebWorkerListener");
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(NativeWebWorkerListenerThread);
};

// Utility function to convert to C strings.
static char* WebStringToCharp(const WebKit::WebString& str, size_t* len) {
  // Convert source from webString data to char*
  *len = str.length();
  char* bufp = new char[*len + 1];
  const WebKit::WebUChar* datap = str.data();
  for (size_t i = 0; i < *len; ++i) {
    bufp[i] = static_cast<char>(datap[i]);
  }
  bufp[*len] = 0;
  return bufp;
}

// Used for debugging purposes for now.
static int retval;

WebKit::WebWorker* NativeWebWorkerImpl::create(
    WebKit::WebWorkerClient* client) {
  return new NativeWebWorkerImpl(client);
}

NativeWebWorkerImpl::NativeWebWorkerImpl(WebKit::WebWorkerClient* client)
    : client_(client),
      nap_(NULL),
      channel_(NULL),
      upcall_thread_(NULL) {
  descs_[0] = NULL;
  descs_[1] = NULL;
}

NativeWebWorkerImpl::~NativeWebWorkerImpl() {
}

void NativeWebWorkerImpl::startWorkerContext(const WebKit::WebURL& script_url,
                              const WebKit::WebString& user_agent,
                              const WebKit::WebString& source) {
  size_t len;
  char* bufp = WebStringToCharp(source, &len);
  // Start NaCl using the nexe.
  retval = NaClStartNativeWebWorker(bufp, len, &nap_, &channel_);
  // Free the string.
  delete[] bufp;

  // Remember the main thread's message loop.
  g_main_thread_message_loop = MessageLoop::current();
  // Start the upcall listener thread.
  upcall_thread_ = NativeWebWorkerListenerThread::Create();
  upcall_thread_->Start();
  // Put an SRPC listener loop on the listener thread.
  NaClCreateUpcallChannel(descs_);
  Task* task = new ListenerTask(client_, descs_[1]);
  upcall_thread_->message_loop()->PostTask(FROM_HERE, task);
  // Send upcall listener channel descriptor to the native worker.
  retval = NaClSrpcSendUpcallDesc(channel_, descs_[0]);
}

void NativeWebWorkerImpl::terminateWorkerContext() {
  // Close the descriptors.
  NaClDestroyUpcallChannel(descs_);
  // Shut down the sel_ldr instance for this native web worker.
  retval = NaClTerminateNativeWebWorker(&nap_, &channel_);
  // Shut down the upcall thread.
  upcall_thread_->Stop();
}

void NativeWebWorkerImpl::postMessageToWorkerContext(
    const WebKit::WebString& message,
    const WebKit::WebMessagePortChannelArray& channel) {
  size_t len;
  char* bufp = WebStringToCharp(message, &len);
  // Send a message to NaCl object
  retval = NaClPostMessageToNativeWebWorker(bufp, len, &nap_, &channel_);
  delete[] bufp;
}

void NativeWebWorkerImpl::workerObjectDestroyed() {
}

void NativeWebWorkerImpl::clientDestroyed() {
}