/*
 * libjingle
 * Copyright 2006, Google Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *  3. The name of the author may not be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <iostream>
#include "libjingleplus.h"
#ifdef WIN32
#include "talk/base/win32socketserver.h"
#endif
#include "talk/base/physicalsocketserver.h"
#include "talk/base/logging.h"
#include "talk/examples/login/xmppauth.h"
#include "talk/examples/login/xmppsocket.h"
#include "talk/examples/login/xmpppump.h"
#include "presencepushtask.h"
#include "talk/app/status.h"
#include "talk/app/message.h"
#include "rostertask.h"
#include "talk/app/iqtask.h"
#include "talk/app/presenceouttask.h"
#include "talk/app/receivemessagetask.h"
#include "talk/app/rostersettask.h"
#include "talk/app/sendmessagetask.h"

enum {
  MSG_START,
  
  // main thread to worker
  MSG_LOGIN,
  MSG_DISCONNECT,
  MSG_SEND_PRESENCE,
  MSG_SEND_DIRECTED_PRESENCE,
  MSG_SEND_DIRECTED_MUC_PRESENCE,
  MSG_SEND_XMPP_MESSAGE,
  MSG_SEND_XMPP_IQ,
  MSG_UPDATE_ROSTER_ITEM,
  MSG_REMOVE_ROSTER_ITEM,

  // worker thread to main thread
  MSG_STATE_CHANGE,
  MSG_STATUS_UPDATE,
  MSG_STATUS_ERROR,
  MSG_ROSTER_REFRESH_STARTED,
  MSG_ROSTER_REFRESH_FINISHED,
  MSG_ROSTER_ITEM_UPDATED,
  MSG_ROSTER_ITEM_REMOVED,
  MSG_ROSTER_SUBSCRIBE,
  MSG_ROSTER_UNSUBSCRIBE,
  MSG_ROSTER_SUBSCRIBED,
  MSG_ROSTER_UNSUBSCRIBED,
  MSG_INCOMING_MESSAGE,
  MSG_IQ_COMPLETE,
  MSG_XMPP_INPUT,
  MSG_XMPP_OUTPUT
};

class LibjinglePlusWorker : public talk_base::MessageHandler, 
			    public XmppPumpNotify,
                            public sigslot::has_slots<> {
 public:	
  LibjinglePlusWorker(LibjinglePlus *ljp, LibjinglePlusNotify *notify) : 
    worker_thread_(NULL), ljp_(ljp), notify_(notify),
    ppt_(NULL), rmt_(NULL), rt_(NULL), is_test_login_(false) {

    main_thread_.reset(new talk_base::AutoThread());
#ifdef WIN32
    ss_.reset(new talk_base::Win32SocketServer(main_thread_.get()));
    main_thread_->set_socketserver(ss_.get());
#endif

    pump_.reset(new XmppPump(this));

    pump_->client()->SignalLogInput.connect(this, &LibjinglePlusWorker::OnInputDebug);
    pump_->client()->SignalLogOutput.connect(this, &LibjinglePlusWorker::OnOutputDebug);
    //pump_->client()->SignalStateChange.connect(this, &LibjinglePlusWorker::OnStateChange);
    }

  ~LibjinglePlusWorker() {
    if (worker_thread_) {
      worker_thread_->Send(this, MSG_DISCONNECT);
      delete worker_thread_;
    }
  }
  
  virtual void OnMessage(talk_base::Message *msg) {
    switch (msg->message_id) {
    case MSG_START:
      LoginW();
      break;
    case MSG_DISCONNECT:
      DisconnectW();
      break;
    case MSG_SEND_XMPP_MESSAGE:
      SendXmppMessageW(static_cast<SendMessageData*>(msg->pdata)->m_);
      delete msg->pdata;
      break;
    case MSG_SEND_XMPP_IQ:
      SendXmppIqW(static_cast<SendIqData*>(msg->pdata)->to_jid_,
                  static_cast<SendIqData*>(msg->pdata)->is_get_,
                  static_cast<SendIqData*>(msg->pdata)->xml_element_);
      delete msg->pdata;
      break;
    case MSG_SEND_PRESENCE:
      SendPresenceW(static_cast<SendPresenceData*>(msg->pdata)->s_);
      delete msg->pdata;
      break;
    case MSG_SEND_DIRECTED_PRESENCE:
      SendDirectedPresenceW(static_cast<SendDirectedPresenceData*>(msg->pdata)->j_,
			    static_cast<SendDirectedPresenceData*>(msg->pdata)->s_);
      delete msg->pdata;
      break;
    case MSG_SEND_DIRECTED_MUC_PRESENCE:
      SendDirectedMUCPresenceW(static_cast<SendDirectedMUCPresenceData*>(msg->pdata)->j_,
			       static_cast<SendDirectedMUCPresenceData*>(msg->pdata)->s_,
			       static_cast<SendDirectedMUCPresenceData*>(msg->pdata)->un_,
			       static_cast<SendDirectedMUCPresenceData*>(msg->pdata)->ac_,
			       static_cast<SendDirectedMUCPresenceData*>(msg->pdata)->am_,
			       static_cast<SendDirectedMUCPresenceData*>(msg->pdata)->role_);
      delete msg->pdata;
      break;
    case MSG_UPDATE_ROSTER_ITEM:
      UpdateRosterItemW(static_cast<UpdateRosterItemData*>(msg->pdata)->jid_,
			static_cast<UpdateRosterItemData*>(msg->pdata)->n_,
			static_cast<UpdateRosterItemData*>(msg->pdata)->g_,
			static_cast<UpdateRosterItemData*>(msg->pdata)->grt_);
      delete msg->pdata;
      break;
    case MSG_REMOVE_ROSTER_ITEM:
      RemoveRosterItemW(static_cast<JidData*>(msg->pdata)->jid_);
      delete msg->pdata;
      break;

    case MSG_STATUS_UPDATE:
      OnStatusUpdateW(static_cast<SendPresenceData*>(msg->pdata)->s_);
      delete msg->pdata;
      break;
    case MSG_STATUS_ERROR:
      OnStatusErrorW(static_cast<StatusErrorData*>(msg->pdata)->stanza_);
      delete msg->pdata;
      break;
    case MSG_STATE_CHANGE:
      OnStateChangeW(static_cast<StateChangeData*>(msg->pdata)->s_);
      delete msg->pdata;
      break;
    case MSG_ROSTER_REFRESH_STARTED:
      OnRosterRefreshStartedW();
      break;
    case MSG_ROSTER_REFRESH_FINISHED:
      OnRosterRefreshFinishedW();
      break;
    case MSG_ROSTER_ITEM_UPDATED:
      OnRosterItemUpdatedW(static_cast<RosterItemData*>(msg->pdata)->ri_);
      delete msg->pdata;
      break;
    case MSG_ROSTER_ITEM_REMOVED:
      OnRosterItemRemovedW(static_cast<RosterItemData*>(msg->pdata)->ri_);
      delete msg->pdata;
      break;
    case MSG_ROSTER_SUBSCRIBE:
      OnRosterSubscribeW(static_cast<JidData*>(msg->pdata)->jid_);
      delete msg->pdata;
      break;
    case MSG_ROSTER_UNSUBSCRIBE:
      OnRosterUnsubscribeW(static_cast<JidData*>(msg->pdata)->jid_);
      delete msg->pdata;
      break;
    case MSG_ROSTER_SUBSCRIBED:
      OnRosterSubscribedW(static_cast<JidData*>(msg->pdata)->jid_);
      delete msg->pdata;
      break;
    case MSG_ROSTER_UNSUBSCRIBED:
      OnRosterUnsubscribedW(static_cast<JidData*>(msg->pdata)->jid_);
      delete msg->pdata;
      break;
    case MSG_INCOMING_MESSAGE:
      OnIncomingMessageW(static_cast<XmppMessageData*>(msg->pdata)->m_);
      delete msg->pdata;
      break;
    case MSG_IQ_COMPLETE:
      OnIqCompleteW(static_cast<IqCompleteData*>(msg->pdata)->success_,
                    static_cast<IqCompleteData*>(msg->pdata)->stanza_);
      delete msg->pdata;
      break;
    case MSG_XMPP_OUTPUT:
      OnOutputDebugW(static_cast<StringData*>(msg->pdata)->s_);
      delete msg->pdata;
      break;
    case MSG_XMPP_INPUT:
      OnInputDebugW(static_cast<StringData*>(msg->pdata)->s_);
      delete msg->pdata;
      break;
    }
  }

  void Login(const std::string &jid, const std::string &password,
	     const std::string &machine_address, bool is_test, bool cookie_auth) {
    is_test_login_ = is_test;
  
    xcs_.set_user(jid);
    if (cookie_auth) {
	    xcs_.set_auth_cookie(password);
    } else {
	    talk_base::InsecureCryptStringImpl pass;
	    pass.password() = password;
	    xcs_.set_pass(talk_base::CryptString(pass));
    }
    xcs_.set_host(is_test ? "google.com" : "gmail.com");
    xcs_.set_resource("libjingleplus");
    xcs_.set_server(talk_base::SocketAddress(machine_address, 5222));
    xcs_.set_use_tls(!is_test);
    if (is_test) {
      xcs_.set_allow_plain(true);
    }

    worker_thread_ = new talk_base::Thread(&pss_);
    worker_thread_->Start(); 
    worker_thread_->Send(this, MSG_START);
  }
     
  void SendXmppMessage(const buzz::XmppMessage &m) {
    assert(talk_base::ThreadManager::CurrentThread() != worker_thread_);
    worker_thread_->Post(this, MSG_SEND_XMPP_MESSAGE, new SendMessageData(m));
  }

  void SendXmppIq(const buzz::Jid &to_jid, bool is_get,
                  const buzz::XmlElement *xml_element) {
    assert(talk_base::ThreadManager::CurrentThread() != worker_thread_);
    worker_thread_->Post(this, MSG_SEND_XMPP_IQ,
                         new SendIqData(to_jid, is_get, xml_element));
  }

  void SendPresence(const buzz::Status & s) {
    assert(talk_base::ThreadManager::CurrentThread() != worker_thread_);
    worker_thread_->Post(this, MSG_SEND_PRESENCE, new SendPresenceData(s));
  }
  
  void SendDirectedPresence (const buzz::Jid &j, const buzz::Status &s) {
    assert(talk_base::ThreadManager::CurrentThread() != worker_thread_);
    worker_thread_->Post(this, MSG_SEND_DIRECTED_PRESENCE, new SendDirectedPresenceData(j,s));
  }
  
  void SendDirectedMUCPresence(const buzz::Jid &j, const buzz::Status &s,
			       const std::string &un, const std::string &ac,
			       const std::string &am, const std::string &role) {
    assert(talk_base::ThreadManager::CurrentThread() != worker_thread_);
    worker_thread_->Post(this, MSG_SEND_DIRECTED_MUC_PRESENCE, new SendDirectedMUCPresenceData(j,s,un,ac,am, role));
  }
  
  void UpdateRosterItem(const buzz::Jid & jid, const std::string & name, 
			const std::vector<std::string> & groups, buzz::GrType grt) {
    assert(talk_base::ThreadManager::CurrentThread() != worker_thread_);
    worker_thread_->Post(this, MSG_UPDATE_ROSTER_ITEM, new UpdateRosterItemData(jid,name,groups,grt));
  }
  
  void RemoveRosterItemW(const buzz::Jid &jid) {
    buzz::RosterSetTask *rst = new buzz::RosterSetTask(pump_.get()->client());
    rst->Remove(jid);
    rst->Start();
  }

  void RemoveRosterItem(const buzz::Jid &jid) {
    assert(talk_base::ThreadManager::CurrentThread() != worker_thread_);
    worker_thread_->Post(this, MSG_REMOVE_ROSTER_ITEM, new JidData(jid));
  }
  
  void DoCallbacks() {
    assert(talk_base::ThreadManager::CurrentThread() != worker_thread_);
    talk_base::Message m;
    while (main_thread_->Get(&m, 0)) {
      main_thread_->Dispatch(&m);
    }
  }

 private:

  struct UpdateRosterItemData : public talk_base::MessageData {
    UpdateRosterItemData(const buzz::Jid &jid, const std::string &name, 
			 const std::vector<std::string> &groups, buzz::GrType grt) :
      jid_(jid), n_(name), g_(groups), grt_(grt) {}
    buzz::Jid jid_;
    std::string n_;
    std::vector<std::string> g_;
    buzz::GrType grt_;
  };
  
  void UpdateRosterItemW(const buzz::Jid &jid, const std::string &name,
			 const std::vector<std::string> &groups, buzz::GrType grt) {
    assert (talk_base::ThreadManager::CurrentThread() == worker_thread_);
    buzz::RosterSetTask *rst = new buzz::RosterSetTask(pump_.get()->client());
    rst->Update(jid, name, groups, grt);
    rst->Start();
  }

  struct StringData : public talk_base::MessageData {
    StringData(std::string s) : s_(s) {}
    std::string s_;
  };

  void OnInputDebugW(const std::string &data) {
    assert(talk_base::ThreadManager::CurrentThread() != worker_thread_);
    if (notify_)
      notify_->OnXmppInput(data);
  }

  void OnInputDebug(const char *data, int len) {
    assert (talk_base::ThreadManager::CurrentThread() == worker_thread_);
    main_thread_->Post(this, MSG_XMPP_INPUT, new StringData(std::string(data,len)));
    if (notify_)
      notify_->WakeupMainThread();
  }  

  void OnOutputDebugW(const std::string &data) {
    assert(talk_base::ThreadManager::CurrentThread() != worker_thread_);
    if (notify_)
      notify_->OnXmppOutput(data);
  }
  
  void OnOutputDebug(const char *data, int len) {
    assert (talk_base::ThreadManager::CurrentThread() == worker_thread_);
    main_thread_->Post(this, MSG_XMPP_OUTPUT, new StringData(std::string(data,len)));
    if (notify_)
      notify_->WakeupMainThread();
  }

  struct StateChangeData : public talk_base::MessageData {
    StateChangeData(buzz::XmppEngine::State state) : s_(state) {}
    buzz::XmppEngine::State s_;
  };
  
  void OnStateChange(buzz::XmppEngine::State state) {
    assert (talk_base::ThreadManager::CurrentThread() == worker_thread_);
    switch (state) {
    case buzz::XmppEngine::STATE_OPEN:           
      ppt_ = new buzz::PresencePushTask(pump_.get()->client());
      ppt_->SignalStatusUpdate.connect(this,
                                       &LibjinglePlusWorker::OnStatusUpdate);
      ppt_->SignalStatusError.connect(this,
                                      &LibjinglePlusWorker::OnStatusError);
      ppt_->Start();

      rmt_ = new buzz::ReceiveMessageTask(pump_.get()->client(), buzz::XmppEngine::HL_ALL);
      rmt_->SignalIncomingMessage.connect(this, &LibjinglePlusWorker::OnIncomingMessage);
      rmt_->Start();

      rt_ = new buzz::RosterTask(pump_.get()->client());
      rt_->SignalRosterItemUpdated.connect(this, &LibjinglePlusWorker::OnRosterItemUpdated);
      rt_->SignalRosterItemRemoved.connect(this, &LibjinglePlusWorker::OnRosterItemRemoved);
      rt_->SignalSubscribe.connect(this, &LibjinglePlusWorker::OnRosterSubscribe);
      rt_->SignalUnsubscribe.connect(this, &LibjinglePlusWorker::OnRosterUnsubscribe);
      rt_->SignalSubscribed.connect(this, &LibjinglePlusWorker::OnRosterSubscribed);
      rt_->SignalUnsubscribed.connect(this, &LibjinglePlusWorker::OnRosterUnsubscribed);
      rt_->SignalRosterRefreshStarted.connect(this, &LibjinglePlusWorker::OnRosterRefreshStarted);
      rt_->SignalRosterRefreshFinished.connect(this, &LibjinglePlusWorker::OnRosterRefreshFinished);
      rt_->Start();
      rt_->RefreshRosterNow();

      break;
    }
    main_thread_->Post(this, MSG_STATE_CHANGE, new StateChangeData(state));
    if (notify_)
      notify_->WakeupMainThread();
  }

  void OnStateChangeW(buzz::XmppEngine::State state) { 
    assert(talk_base::ThreadManager::CurrentThread() != worker_thread_);
    if (notify_)
      notify_->OnStateChange(state);
  }

  struct RosterItemData : public talk_base::MessageData {
    RosterItemData(const buzz::RosterItem &ri) : ri_(ri) {}
    buzz::RosterItem ri_;
  };

  void OnRosterItemUpdatedW(const buzz::RosterItem &ri) {
    assert(talk_base::ThreadManager::CurrentThread() != worker_thread_);
    if (notify_)
      notify_->OnRosterItemUpdated(ri);
  }

  void OnRosterItemUpdated(const buzz::RosterItem &ri, bool huh) {
    assert (talk_base::ThreadManager::CurrentThread() == worker_thread_);
    main_thread_->Post(this, MSG_ROSTER_ITEM_UPDATED, new RosterItemData(ri));
    if (notify_)
      notify_->WakeupMainThread();
  }

  void OnRosterItemRemovedW(const buzz::RosterItem &ri) { 
    assert(talk_base::ThreadManager::CurrentThread() != worker_thread_);
    if (notify_)
      notify_->OnRosterItemRemoved(ri);
  }
  
  void OnRosterItemRemoved(const buzz::RosterItem &ri) {
    assert (talk_base::ThreadManager::CurrentThread() == worker_thread_);
    main_thread_->Post(this, MSG_ROSTER_ITEM_REMOVED, new RosterItemData(ri));
    if (notify_)
      notify_->WakeupMainThread();
  }

  struct JidData : public talk_base::MessageData {
    JidData(const buzz::Jid& jid) : jid_(jid) {}
    const buzz::Jid jid_;
  };
  
  void OnRosterSubscribeW(const buzz::Jid& jid) {
    assert(talk_base::ThreadManager::CurrentThread() != worker_thread_);
    if (notify_)
      notify_->OnRosterSubscribe(jid);
  }

  void OnRosterSubscribe(const buzz::Jid& jid) {
    assert (talk_base::ThreadManager::CurrentThread() == worker_thread_);
    main_thread_->Post(this, MSG_ROSTER_SUBSCRIBE, new JidData(jid));
    if (notify_)
      notify_->WakeupMainThread();
  }

  void OnRosterUnsubscribeW(const buzz::Jid &jid) {
    assert(talk_base::ThreadManager::CurrentThread() != worker_thread_);
    if (notify_)
      notify_->OnRosterUnsubscribe(jid);
  }

  void OnRosterUnsubscribe(const buzz::Jid &jid) {
    assert (talk_base::ThreadManager::CurrentThread() == worker_thread_);
    main_thread_->Post(this, MSG_ROSTER_UNSUBSCRIBE, new JidData(jid));
    if (notify_)
      notify_->WakeupMainThread();
  }
  
  void OnRosterSubscribedW(const buzz::Jid &jid) {
    assert(talk_base::ThreadManager::CurrentThread() != worker_thread_);
    if (notify_)
      notify_->OnRosterSubscribed(jid);
  }

  void OnRosterSubscribed(const buzz::Jid &jid) {
    assert (talk_base::ThreadManager::CurrentThread() == worker_thread_);
    main_thread_->Post(this, MSG_ROSTER_SUBSCRIBED, new JidData(jid));
    if (notify_)
      notify_->WakeupMainThread();
  }
  
  void OnRosterUnsubscribedW(const buzz::Jid &jid) {
    assert(talk_base::ThreadManager::CurrentThread() != worker_thread_);
    if (notify_)
      notify_->OnRosterUnsubscribed(jid);
  }

  void OnRosterUnsubscribed(const buzz::Jid &jid) {
    assert (talk_base::ThreadManager::CurrentThread() == worker_thread_);
    main_thread_->Post(this, MSG_ROSTER_UNSUBSCRIBED, new JidData(jid));
    if (notify_)
      notify_->WakeupMainThread();
  }

  void OnRosterRefreshStartedW() {
    assert(talk_base::ThreadManager::CurrentThread() != worker_thread_);
    if (notify_)
      notify_->OnRosterRefreshStarted();
  }
  
  void OnRosterRefreshStarted() {
    assert (talk_base::ThreadManager::CurrentThread() == worker_thread_);
    main_thread_->Post(this, MSG_ROSTER_REFRESH_STARTED);
    if (notify_)
      notify_->WakeupMainThread();
  }

  void OnRosterRefreshFinishedW() {
    assert(talk_base::ThreadManager::CurrentThread() != worker_thread_);
    if (notify_)
      notify_->OnRosterRefreshFinished();
  }

  void OnRosterRefreshFinished() {
    assert (talk_base::ThreadManager::CurrentThread() == worker_thread_);
    main_thread_->Post(this, MSG_ROSTER_REFRESH_FINISHED);
    if (notify_)
      notify_->WakeupMainThread();
  }
  
  struct XmppMessageData : talk_base::MessageData {
    XmppMessageData(const buzz::XmppMessage &m) : m_(m) {}
    buzz::XmppMessage m_;
  };
  
  void OnIncomingMessageW(const buzz::XmppMessage &msg) {
    assert(talk_base::ThreadManager::CurrentThread() != worker_thread_);
    if (notify_)
      notify_->OnMessage(msg);
  }

  void OnIncomingMessage(const buzz::XmppMessage &msg) {
    assert (talk_base::ThreadManager::CurrentThread() == worker_thread_);
    main_thread_->Post(this, MSG_INCOMING_MESSAGE, new XmppMessageData(msg));
    if (notify_)
      notify_->WakeupMainThread();
  }

  void OnStatusUpdateW (const buzz::Status &status) {
    assert(talk_base::ThreadManager::CurrentThread() != worker_thread_);
    if (notify_)
      notify_->OnStatusUpdate(status);
  }

  void OnStatusUpdate (const buzz::Status &status) {
    assert (talk_base::ThreadManager::CurrentThread() == worker_thread_);
    main_thread_->Post(this, MSG_STATUS_UPDATE, new SendPresenceData(status));
    if (notify_)
      notify_->WakeupMainThread();
  }

  struct StatusErrorData : talk_base::MessageData {
    StatusErrorData(const buzz::XmlElement &stanza) : stanza_(stanza) {}
    buzz::XmlElement stanza_;
  };

  void OnStatusErrorW (const buzz::XmlElement &stanza) {
    assert(talk_base::ThreadManager::CurrentThread() != worker_thread_);
    if (notify_)
      notify_->OnStatusError(stanza);
  }

  void OnStatusError (const buzz::XmlElement &stanza) {
    assert (talk_base::ThreadManager::CurrentThread() == worker_thread_);
    main_thread_->Post(this, MSG_STATUS_ERROR, new StatusErrorData(stanza));
    if (notify_)
      notify_->WakeupMainThread();
  }

  void LoginW() {
    assert (talk_base::ThreadManager::CurrentThread() == worker_thread_);
    XmppSocket* socket = new XmppSocket(true);
    pump_->DoLogin(xcs_, socket, is_test_login_ ?  NULL : new XmppAuth());
    socket->SignalCloseEvent.connect(this,
        &LibjinglePlusWorker::OnXmppSocketClose);
  }

  void DisconnectW() {
    assert(talk_base::ThreadManager::CurrentThread() == worker_thread_);
    pump_->DoDisconnect();
  }

  void SendXmppMessageW(const buzz::XmppMessage &m) {
    assert (talk_base::ThreadManager::CurrentThread() == worker_thread_);
    buzz::SendMessageTask * smt = new buzz::SendMessageTask(pump_.get()->client());
    smt->Send(m);
    smt->Start();
  }

  void SendXmppIqW(const buzz::Jid &to_jid, bool is_get,
                   const buzz::XmlElement *xml_element) {
    assert (talk_base::ThreadManager::CurrentThread() == worker_thread_);
    buzz::IqTask *iq_task = new buzz::IqTask(pump_.get()->client(),
        is_get, to_jid, const_cast<buzz::XmlElement *>(xml_element));
    iq_task->SignalDone.connect(this, &LibjinglePlusWorker::OnIqComplete);
    iq_task->Start();
  }

 struct IqCompleteData : public talk_base::MessageData {
   IqCompleteData(bool success, const buzz::XmlElement *stanza) :
     success_(success), stanza_(*stanza) {}
   bool success_;
   buzz::XmlElement stanza_;
 };

  void OnIqCompleteW(bool success, const buzz::XmlElement& stanza) {
    assert(talk_base::ThreadManager::CurrentThread() != worker_thread_);
    if (notify_)
      notify_->OnIqDone(success, stanza);
  }

  void OnIqComplete(bool success, const buzz::XmlElement *stanza) {
    assert(talk_base::ThreadManager::CurrentThread() == worker_thread_);
    main_thread_->Post(this, MSG_IQ_COMPLETE,
       new IqCompleteData(success, stanza));
    if (notify_)
      notify_->WakeupMainThread();
  }

  void SendPresenceW(const buzz::Status & s) {
    assert (talk_base::ThreadManager::CurrentThread() == worker_thread_);
    buzz::PresenceOutTask *pot = new buzz::PresenceOutTask(pump_.get()->client());
    pot->Send(s);
    pot->Start();
  }

  void SendDirectedMUCPresenceW(const buzz::Jid & j, const buzz::Status & s, 
			       const std::string &user_nick, const std::string &api_capability,
			       const std::string &api_message, const std::string &role) {
    assert (talk_base::ThreadManager::CurrentThread() == worker_thread_);
    buzz::PresenceOutTask *pot = new buzz::PresenceOutTask(pump_.get()->client());
    pot->SendDirectedMUC(j,s,user_nick,api_capability,api_message, role);
    pot->Start();
  }
  
  void SendDirectedPresenceW(const buzz::Jid & j, const buzz::Status & s) {
    assert (talk_base::ThreadManager::CurrentThread() == worker_thread_);
    buzz::PresenceOutTask *pot = new buzz::PresenceOutTask(pump_.get()->client());
    pot->SendDirected(j,s);
    pot->Start();
  }

  void OnXmppSocketClose(int error) {
    notify_->OnSocketClose(error);
  }

 struct SendMessageData : public talk_base::MessageData {
   SendMessageData(const buzz::XmppMessage &m) : m_(m) {}
   buzz::XmppMessage m_;
  };

 struct SendIqData : public talk_base::MessageData {
   SendIqData(const buzz::Jid &jid, bool is_get, const buzz::XmlElement *m)
     : to_jid_(jid), is_get_(is_get), xml_element_(m) {}
   buzz::Jid to_jid_;
   bool is_get_;
   const buzz::XmlElement *xml_element_;
  };

 struct SendPresenceData : public talk_base::MessageData {
   SendPresenceData(const buzz::Status &s) : s_(s) {}
   buzz::Status s_;
  };  

 struct SendDirectedPresenceData : public talk_base::MessageData {
   SendDirectedPresenceData(const buzz::Jid &j, const buzz::Status &s) : j_(j), s_(s) {}
   buzz::Jid j_;
   buzz::Status s_;
 };

  struct SendDirectedMUCPresenceData : public talk_base::MessageData {
    SendDirectedMUCPresenceData(const buzz::Jid &j, const buzz::Status &s,
				const std::string &un, const std::string &ac,
				const std::string &am, const std::string &role)
      : j_(j), s_(s), un_(un), ac_(ac), am_(am), role_(role) {}
    buzz::Jid j_;
    buzz::Status s_;
    std::string un_;
    std::string ac_;
    std::string am_;
    std::string role_;
  };

  talk_base::scoped_ptr<talk_base::Win32SocketServer> ss_;
  talk_base::scoped_ptr<talk_base::Thread> main_thread_;
  talk_base::Thread *worker_thread_;

  LibjinglePlus *ljp_;
  LibjinglePlusNotify *notify_;
  buzz::XmppClientSettings xcs_;
  talk_base::PhysicalSocketServer pss_;

  talk_base::scoped_ptr<XmppPump> pump_;
  buzz::PresencePushTask * ppt_;
  buzz::ReceiveMessageTask * rmt_;
  buzz::RosterTask * rt_;

  bool is_test_login_;
};

LibjinglePlus::LibjinglePlus(LibjinglePlusNotify *notify)
{
  worker_ = new LibjinglePlusWorker(this, notify);
}

LibjinglePlus::~LibjinglePlus()
{
 delete worker_;
  worker_ = NULL;
}

void LibjinglePlus::Login(const std::string &jid,
		          const std::string &password,
		          const std::string &machine_address,
			  bool is_test, bool cookie_auth) {
  worker_->Login(jid, password, machine_address, is_test, cookie_auth);
}

void LibjinglePlus::SendPresence(const buzz::Status & s) {
  worker_->SendPresence(s);
}

void LibjinglePlus::SendDirectedPresence(const buzz::Jid & j, const buzz::Status & s) {
  worker_->SendDirectedPresence(j,s);
}

void LibjinglePlus::SendDirectedMUCPresence(const buzz::Jid & j,
    const buzz::Status & s, const std::string &user_nick,
    const std::string &api_capability, const std::string &api_message,
    const std::string &role) {
  worker_->SendDirectedMUCPresence(j,s,user_nick,api_capability,api_message,
      role);
}

void LibjinglePlus::SendXmppMessage(const buzz::XmppMessage & m) {
  worker_->SendXmppMessage(m);
}

void LibjinglePlus::SendXmppIq(const buzz::Jid &to_jid, bool is_get,
                               const buzz::XmlElement *iq_element) {
  worker_->SendXmppIq(to_jid, is_get, iq_element);
}

void LibjinglePlus::DoCallbacks() {
  worker_->DoCallbacks();
}
