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

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

using namespace std;


//
// Your TCP client is derived from sw::sock::tcp_client
//
class myclient : public sw::sock::tcp_client
{
  typedef sw::sock::tcp_client client_t;

public:

  myclient() : client_t()
  { cerr << "myclient::myclient()" << endl; }

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

protected:

  // Called when the socket connection is established and
  // the client thread running. This function is already called
  // in the client's own thread.
  void on_connected()
  { cerr << "myclient::on_connected()" << endl; }

  // Called when data were received
  void on_receive()
  {
    cerr << "myclient::on_receive() ";
    string s;
    while(client_t::recv(s)) cout << s;
  }

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

  // Called when the socket had problems.
  void on_error(long no, const char* text)
  { cerr << "myclient::on_error(" << no << ", << " << text <<  ")" << endl; }

  // Called before the client thread quits.
  void on_close()
  { cerr << "myclient::on_close()" << endl; }

  // Called frequently, either when data were sent/received or when wait()
  // returned without receiving/sending (wait timeout).
  void on_update()
  { ; }
};

//
// Main
//
int main(int argc, char** argv)
{
  // CLI ARGS
  if(argc < 2 || !argv[1]) {
    // Usage
    cerr << "Usage: " << (argv[0] ? argv[0] : "tcp_client") << "[--http <url>] [<address/dns>:<port>]" << endl;
    return 1;
  } else if(string(argv[1]) == "--http") {
    // Make a HTTP request
    if(argc < 3 || !argv[2]) { cerr << "Missing URL" << endl; return 1; }

    // The socket class uses sw::sock::ip_address to connect. ip_address simplifies
    // generating inet compliant addresses, has string conversion functions and the
    // "resource address parser" function shown below:
    sw::sock::ip_address adr;

    // This static class function can be used to get the chunks and address of a
    // location specification, e.g.
    //
    //  - http://example.com/path // service, host lookup, resource path
    //  - ftp://user@127.0.0.1/   // service, user, V4 address
    //  - ::1                     // V6 address
    //  - [abce::0bbb]:8080       // V6 address, port
    //  - etc...
    //
    string service, user, host, path;
    sw::sock::ip_address::parse_resource_address(argv[2], adr, service, user, host, path, true);
    if(!adr.port()) adr.port(80);
    cout << "SERVICE= " << service << endl;
    cout << "HOST = " << host << endl;
    cout << "PORT = " << adr.port() << endl;
    cout << "PATH = " << path << endl;
    cout << "ADR  = " << adr.str() << endl;
    cout << "IPV6 = " << adr.is_v6() << endl;

    // Build a simple GET request:
    string request = string("")
      + "GET / HTTP/1.1\n"
      + "Host: " + host + "\n"
      + "User-Agent: Mozilla/5.0\n"
      + "Connection: close\n"
      + "\n";
    cout << request << endl;

    // Now the client: Make a new one ...
    myclient client;

    // ... connect ...
    if(!client.connect(adr)) {
      cerr << "Failed to connect: " << client.error_text() << endl;
    }

    // ... send the request ...
    client.send(request);

    // ... and wait for the socket to be closed.
    while(!client.closed()) {
      sw::thread::usleep(10000);
    }

  } else {
    // Connect to a server, no HTTP request, more like telnet
    myclient client;
    if(!client.connect(argv[1])) {
      cerr << "Failed to connect: " << client.error_text() << endl;
    }
    cerr << "CTRL-C to exit." << endl;
    // We send from this main thread, the received data are shown in our
    // client class on_receive().
    while(client.connected()) {
      string s;
      cin >> s;
      s += "\n";
      // This is the string send method, there is also one for raw binary with
      // void* and data size.
      client.send(s);
    }
  }
  return 0;
}
