/*****************************
File:       RemoteTCPServer.CPP
Language:   C++ (header)
Project:    H3DNetworkingUtils
The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/

Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
License for the specific language governing rights and limitations
under the License.

The Original Code is H3DNetworkingUtils v1.0.

The Initial Developer of the Original Code is CSIRO.
Portions created by the Initial Developer are Copyright (C) 1009 CSIRO. All Rights Reserved.
Contributor(s):
    Chris Gunn  <Chris.Gunn@csiro.au> <ChrisJGunn@gmail.com>
***************************/
#include "H3DNetworkingUtils/RemoteTCPServer.h"
#include <stdio.h>
#include "H3DNetworkingUtils/sockwrap.h"
#include "H3DNetworkingUtils/fail.h"
#include "H3DNetworkingUtils/RemoteSync.h"


using namespace H3D;
using namespace H3DNetworkingUtils;

// ----------------------------------------------------------------------------
H3DNodeDatabase RemoteTCPServer::database(   
  "RemoteTCPServer", 
  &(newInstance<RemoteTCPServer>),
  typeid(RemoteTCPServer),
  &RemoteServer::database);


// ----------------------------------------------------------------------------
RemoteTCPServer::RemoteTCPServer() :
listening_threadP (0),
stop_listening_thread (false) {

  type_name = "RemoteTCPServer";
  database.initFields( this );
}

// ----------------------------------------------------------------------------
RemoteTCPServer::~RemoteTCPServer() {
   stop_listening_thread = true;
   while (listeningLoopRunning()) {
      Sleep(50);
   }
   ///if (listening_threadP) {
   /// listening_threadP->cancel();
   /// listening_threadP->wait();
   /// listening_threadP = NULL;
  ///}
}

// ----------------------------------------------------------------------------
void RemoteTCPServer::startServer(int port_number) {
  listen_data.port = port_number;
  listen_data.periodic_send = periodicSend->getValue();
  listening_threadP = new Thread((ThreadFunction)listeningThread, this);
}

// ----------------------------------------------------------------------------
void RemoteTCPServer:: listeningLoop() {
  // Called in listening thread.

  InetAddr * myAddr = new InetAddr;
  char str[80];
  sprintf(str, "%d", listen_data.port);
  myAddr->scanPort(str);

  TCPSock * listening_sockP = new TCPSock(0);

  char tmp[120];
  sprintf(tmp, "Listening using TCP on %d ....", listen_data.port);
  showInfo(tmp);
  FailErr(listening_sockP->bind(myAddr, 4), "Cannot serve on that port");

  setListeningLoopRunning(true);
  ///while (!isShuttingDown()) {
  while (keepListening()) {

    // Only listen for a connection if we are not currently connected.
    // (don't want to connect to 2 clients at once).
    if (sockP) {
      sock_lock.lock();
      delete sockP;
      sockP = 0;
      sock_lock.unlock();
    }


    if (listening_sockP->dataReady()) {
       sockP = listening_sockP->accept();
         ///    setSendTimeout(sockP, 100);
      if (sockP->remoteAddress()) {
         char temp[80];
         sprintf(temp, "TCP connection from %s\n", sockP->remoteAddress()->text());
         showInfo(temp);
      }
      requestOpenComms();
      startReceiving();   // Returns when we become disconnected.
    } else {
       Thread::sleep(1);
    }
  }
  setListeningLoopRunning(false);
}

// ----------------------------------------------------------------------------
void RemoteTCPServer::startReceiving() {
  // Called in listening thread.

  // Pass the listening thread across to be the receiving thread.
  // until this call returns.
  receive_loop_threadP = listening_threadP;
  listening_threadP = NULL;
  setListeningLoopRunning(false);
  connected_sem.wait(); // Wait for graphics thread to say we are connected.
  receiveLoop();  // Note  : Won't return from this until receiving
  // loop quits (ie when it becomes disconnected.
}

// ----------------------------------------------------------------------------
void RemoteTCPServer::setListeningLoopRunning(bool v) {
   listening_loop_lock.lock();
   listening_loop_running = v;
   listening_loop_lock.unlock();
}

// ----------------------------------------------------------------------------
bool RemoteTCPServer::listeningLoopRunning() {
   listening_loop_lock.lock();
   bool v = listening_loop_running;
   listening_loop_lock.unlock();
   return v;
}

// ----------------------------------------------------------------------------
bool RemoteTCPServer::keepListening() {
  bool stop;
  listening_loop_lock.lock();
  stop = stop_listening_thread;
  listening_loop_lock.unlock();
  return !stop;
}

// ----------------------------------------------------------------------------
void RemoteTCPServer::stopListeningThread() {
  listening_loop_lock.lock();
  stop_listening_thread = true;
  listening_loop_lock.unlock();
}
