#define SOCK_DEBUG   /* Enables socket method tracing */
#include <socket.hh>
#include <iostream>
#include <signal.h>

using namespace std;
using namespace sw;

#ifdef OS_WIN
sw::sock::winsock_initializer wsinit; // Windows only
#endif

// When receiving a signal, e.g. kill or CTRL-C,
// we set the flag "stop" to true.
volatile bool stop = false;
void signal_stop(int sig) {
  stop = true;
  switch(sig) {
    case SIGINT:  fprintf(stderr, "\n[SIGINT]\n"); break;
    #ifndef OS_WIN
    case SIGQUIT: fprintf(stderr, "\n[SIGQUIT]\n"); break;
    #endif
  }
  fflush(stderr);
}

//
// First we implement the class that actually handles a request,
// the server class itself only listens and creates new objects of
// this class, one for each request.
//
// We derive sw::sock::tcp_acceptor for that:
//
class echo_acceptor : public sw::sock::tcp_acceptor
{
  typedef sw::sock::tcp_acceptor socket_t; // Convenience typedef only

public:

  echo_acceptor() : socket_t() // <--- initialize tcp_acceptor, too
  { cerr << "echo_acceptor::echo_acceptor()" << endl; }

  virtual ~echo_acceptor()
  { cerr << "echo_acceptor::~echo_acceptor()" << endl; }

protected:

  // Called when the server has created the object and the socket connection is
  // successfully established. This callback method is already called in the
  // acceptor's own thread.
  void on_connected()
  { cerr << "echo_acceptor::on_connected()" << endl; }

  // This callback is invoked whenever data from the connected client were
  // received. Use it to deal with these new data:
  void on_receive()
  { cerr << "echo_acceptor::on_receive() ";
    string ss, s;
    // ::recv is inherited from tcp_acceptor -> tcp_socket -> basic_socket, this one
    // is the string receive method. Another one is for binary buffers (void* and size).
    while(socket_t::recv(s)) {
      ss += s;
      cerr << s;
    }
    // And we directly send the data back --> echo
    socket_t::send(ss);

    if(ss.find("exit") != string::npos) {
      // On "exit" we close the socket, this will automatically stop the thread, too,
      // and the server object will cleanup this object eventually.
      socket_t::close();
    } else if(ss.find("stop") != string::npos || ss.find("close") != string::npos) {
      // On "stop" we tell the server to shutdown, see below. The flag is globally declared
      // at top of this file.
      ::stop = true;
    }
  }

  // Called when data were sent
  void on_sent()
  { cerr << "echo_acceptor::on_sent()" << endl; }

  // Called when an error occurred
  void on_error(long no, const char* text)
  { cerr << "echo_acceptor::on_error(" << no << ", << " << text <<  ")" << endl; }

  // Called before the thread exits
  void on_close()
  { cerr << "echo_acceptor::on_close()" << endl; }

  // Called frequently, either when data were sent/received or when
  // wait() had a timeout (nothing received). It is not called when the
  // socket closed or closing.
  void on_update()
  { ; }
};


//
// The server is always a template, and specify your acceptor as
// typename acceptor_type. Normally you don't need to implement your own
// server class and instead use the sw::sock::tcp_server<acceptor_type>
// directly, but to tune the details you can easily derive it:
//
template <typename acceptor_type=echo_acceptor >
class echo_server : public sw::sock::tcp_server<acceptor_type>
{
  typedef acceptor_type acceptor_t; // Convenience typedef
  typedef sw::sock::tcp_server<acceptor_type> server_t; // Convenience typedef

public:

  echo_server(): server_t()
  { cout << "echo_server::echo_server()" << endl; }

  virtual ~echo_server()
  { cout << "echo_server::~echo_server()" << endl; }

private:

  // Called when the socket is bound and listening, and the thread started.
  // This method is already called in the server's own thread.
  void on_started()
  {
    cout << "echo_server::on_started(): "
         << "LISTENING ON " << this->ip().str() << ", PORT " << this->ip().port()
         << endl;
  }

  // Called just before the server thread exits.
  void on_stopped()
  {
    cout << "echo_server::on_stopped()";
    if(server_t::error()) cout << ", error= " << server_t::error_text();
    cout << endl;
  }

  // A client has connected, and we can return true to accept the connection,
  // or false to refuse it. The acceptor object that will take over this
  // connection is already instantiated and ready to start. Information, such
  // as the IP address of the client is already saved in the acceptor:
  bool on_request(acceptor_t & a)
  { cout << "echo_server::on_request(" <<  a.ip().str() <<  ")" << endl; return true; }

  // Called when an error of the listening socket is detected.
  void on_error(long no, const char* text)
  { cout << "echo_server::on_error(" << no << ": " << text << ")" << endl; }

  // Called frequently as long as the server is listening.
  void on_update()
  { /* thread loop function */ }
};

//
// And main ...
//
int main(int argc, char** argv)
{
  (void) argc; (void) argv;
  // Signals to fire a server shutdown
  ::signal(SIGINT, signal_stop);
  #ifndef OS_WIN
  ::signal(SIGQUIT, signal_stop);
  #endif

  // Instantiate the server
  echo_server<> svr;

  // Set IP and port to bind/listen to
  svr.ip("[::1]:1315");

  // Start the thread
  if(!svr.start()) {
    cout << "Failed to start server: " << svr.error_text() << endl << endl;
  }

  // The main loop simply keeps idling until the server has stopped
  // or the stop flag is set.
  while(svr.running()) {
    // Note: You can also say svr.stop(), but in this example we let the
    // destructor do all this, which is called when exiting the main() function.
    if(stop) break;
    sw::thread::usleep(1000);
  }

  // Show possible errors
  if(svr.error()) cout << "Error: " << svr.error_text() << endl << endl;

  // To see that the server destructor and the acceptors destructors are called
  // after returning.
  fprintf(stderr, "MAIN RETURN\n"); fflush(stderr);
  return 0;
}
