/*
Copyright (c) by respective owners including Yahoo!, Microsoft, and
individual contributors. All rights reserved.  Released under a BSD (revised)
license as described in the file LICENSE.
 */
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>
#include <cstring>
#include <cerrno>
#include <cstdlib>
#ifdef _WIN32
#include <WinSock2.h>
#else
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#endif

using namespace std;

int open_socket(const char* host, unsigned short port)
{
  hostent* he;
  he = gethostbyname(host);

  if (he == nullptr)
    {
      stringstream msg;
      msg << "gethostbyname(" << host << "): " << strerror(errno);
      cerr << msg.str() << endl;
      throw runtime_error(msg.str().c_str());
    }
  int sd = socket(PF_INET, SOCK_STREAM, 0);
  if (sd == -1)
    {
      stringstream msg;
      msg << "socket: " << strerror(errno);
      cerr << msg.str() << endl;
      throw runtime_error(msg.str().c_str());
    }
  sockaddr_in far_end;
  far_end.sin_family = AF_INET;
  far_end.sin_port = htons(port);
  far_end.sin_addr = *(in_addr*)(he->h_addr);
  memset(&far_end.sin_zero, '\0',8);
  if (connect(sd,(sockaddr*)&far_end, sizeof(far_end)) == -1)
    {
      stringstream msg;
      msg << "connect(" << host << ':' << port << "): " << strerror(errno);
      cerr << msg.str() << endl;
      throw runtime_error(msg.str().c_str());
    }
  return sd;
}

int recvall(int s, char* buf, int n){
    int total=0;
    int ret=recv(s, buf, n, 0);
    while(ret>0 && total<n){
        total+=ret;
        if(buf[total-1]=='\n')
            break;
        ret=recv(s, buf+total, n, 0);
    }
    return total;
}

int main(int argc, char* argv[]){
    char buf[256]; 
    char* toks,*itok,*ttag;
    string tag;
    const char* host="localhost";
    unsigned short port=~0;
    ssize_t pos;
    int s,ret,queries=0;
    string line;
    
    if(argc>1){
        host = argv[1];
    }
    if(argc>2){
        port=atoi(argv[2]);
    }
    if(port <= 1024 || port==(unsigned short)(~0)){
        port = 26542;
    }
    
    s=open_socket(host, port);
    size_t id=0;
    ret=send(s,&id,sizeof(id),0);
    if(ret<0){
        const char* msg = "Could not perform handshake!";
        cerr << msg << endl;
        throw runtime_error(msg);
    }
    
    while(getline(cin,line)){
        line.append("\n");
        int len=line.size();
        const char* cstr = line.c_str();
        const char* sp = strchr(cstr,' ');
        ret=send(s,sp+1,len-(sp+1-cstr),0);
        if(ret<0){
            const char* msg = "Could not send unlabeled data!";
            cerr << msg << endl;
            throw runtime_error(msg);
        }
        ret=recvall(s, buf, 256);
        if(ret<0){
	    const char* msg = "Could not receive queries!";
            cerr << msg << endl;
            throw runtime_error(msg);
        }
        buf[ret]='\0';
        toks=&buf[0];
        strsep(&toks," ");
        ttag=strsep(&toks," ");
        tag=ttag?string(ttag):string("'empty");
        itok=strsep(&toks,"\n");
        if(itok==nullptr || itok[0]=='\0'){
            continue;
        }

        queries+=1;
        string imp=string(itok)+" "+tag+" |";
        pos = line.find_first_of ("|");
        line.replace(pos,1,imp); 
        cstr = line.c_str();
        len = line.size();
        ret = send(s,cstr,len,0);
        if(ret<0){
	    const char* msg = "Could not send labeled data!";
            cerr << msg << endl;
            throw runtime_error(msg);
        }
        ret=recvall(s, buf, 256);
        if(ret<0){
	    const char* msg = "Could not receive predictions!";
            cerr << msg << endl;
            throw runtime_error(msg);
        }
    }
    close(s);
    cout << "Went through the data by doing " << queries << " queries" << endl;
    return 0;
}

