#include <iostream>
#include <string>
#include <boost/bind.hpp>
#include <boost/smart_ptr.hpp>
#include <asio.hpp>

#define max_length 128

using asio::ip::tcp;
using std::cin;
using std::cout;
using std::endl;
typedef boost::shared_ptr<tcp::socket> socket_ptr;

void reader(socket_ptr sock) {
	try {
		boost::array<char, max_length> buf;
		for (;;) {
			asio::error_code error;
			size_t len = sock->read_some(asio::buffer(buf), error);
			if (error == asio::error::eof)
					break; // Connection closed cleanly by peer.
			else if (error)
					throw asio::system_error(error); // Some other error.
			cout.write(buf.data(), len);
		}
	} catch (std::exception& e) {
		std::cerr << "Exception in thread: " << e.what() << "\n";
	}
}

int main(int argc, char* argv[]) {
  try {
    if (argc != 3) {
      cout << "Usage: cubbi_telnet <server> <port>" << endl;
      cout << "Example:" << endl;
      cout << "  cubbi_telnet localhost 21" << endl;
      return 1;
    }

	// All programs that use asio need to have at least one of these
    asio::io_service io_service;

    // Get a list of endpoints corresponding to the server name.
    tcp::resolver resolver(io_service);
    tcp::resolver::query query(argv[1], argv[2]);
    tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
    tcp::resolver::iterator end;

    // Try each endpoint until we successfully establish a connection.
    socket_ptr sock(new tcp::socket(io_service));
    asio::error_code error = asio::error::host_not_found;
    while (error && endpoint_iterator != end) {
      sock->close();
      sock->connect(*endpoint_iterator++, error);
    }
    if (error)
		throw asio::system_error(error);

	// read from the socket and print on a separate thread
	asio::thread t(boost::bind(reader, sock));

	// read from stdin and write to the socket
	std::string request;
	for(;;) {
		getline(cin, request);
		request.push_back('\n');
		asio::write(*sock, asio::buffer(request));
	}

  }
  catch (std::exception& e)
  {
	  cout << "Exception: " << e.what() << "\n";
  }
  return 0;
}

