//============================================================================
// Name        : core_fpga_client.cpp
// Author      : Shengye Wang
// Version     :
// Copyright   : Shengye Wang All Rights Reserved
// Description : Hello World in C++, Ansi-style
//============================================================================

#include    <sys/stat.h>
#include    <sys/types.h>
#include    <sys/socket.h>
#include    <stdio.h>
#include    <string.h>
#include    <netdb.h>
#include    <fcntl.h>
#include    <unistd.h>
#include    <netinet/in.h>
#include    <arpa/inet.h>
#include	<stdlib.h>

#include    <iostream>
#include    <string>

using namespace std;

const int  RES_LENGTH = 10240;

const int CMD_LEN = 47;

string buf('0', CMD_LEN);

const string ALLZEROS("00000000000000000000000000000000000000000000000");

int    connect_socket(const char * server,int serverPort){
	int    sockfd=0;
	struct    sockaddr_in    addr;
	struct    hostent        * phost;
	//Register a socket
	//AF_INET stands for IPv4
	//SOCK_STREAM stands for TCP
	if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0){
		herror("Init socket error!");
		return -1;
	}
	bzero(&addr,sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(serverPort);
	addr.sin_addr.s_addr = inet_addr(server);//Initialize with IP address

	if(addr.sin_addr.s_addr == INADDR_NONE){//If is domain name
		phost = (struct hostent*)gethostbyname(server);
		if(phost==NULL){
			herror("Init socket s_addr error!");
			return -1;
		}
		addr.sin_addr.s_addr =((struct in_addr*)phost->h_addr)->s_addr;
	}
	if(connect(sockfd,(struct sockaddr*)&addr, sizeof(addr))<0)
	{
		perror("Connect server fail!");
		return -1; //0 for success, -1 for failure
	}
	else
		return sockfd;
}



int close_socket(int sockfd)
{
	close(sockfd);
	return 0;
}

int sck;

char recvbuf[RES_LENGTH];
char sendbuf[RES_LENGTH];

int send_buf()
{
    for (int i = 0; i < CMD_LEN; ++ i) {
    	sendbuf[i] = buf[i];
    }
    sendbuf[CMD_LEN] = '\n';
    return send(sck,sendbuf,CMD_LEN+1,0);
}

void recv_buf()
{
	recv(sck, recvbuf, RES_LENGTH, 0);
    buf = ALLZEROS;
    for (int i = 0; i < CMD_LEN; ++ i) {
        buf[i] = recvbuf[i];
    }
}

void send_recv_buf()
{
	//cout << "SEND : " << buf << endl;
    send_buf();
    recv_buf();
    //cout << "RECV : " << buf << endl;
}

long long fix_data(long long data)
{
	data &= ((1LL << 34) - 1);
	if (data & (1LL << 33)) data |= ~((1LL << 34) - 1);
    return data;
}

bool is_in_req()
{
    return buf[43] == '1';
}

bool is_out_req()
{
    return buf[42] == '1';
}

int in_ch_num()
{
    return (buf[41] - '0') * 8 + (buf[40] - '0') * 4 + (buf[39] - '0') * 2 + (buf[38] - '0') * 1;
}

int out_ch_num()
{
    return (buf[37] - '0') * 8 + (buf[36] - '0') * 4 + (buf[35] - '0') * 2 + (buf[34] - '0') * 1;
}

void do_in()
{
    long long num;
    cin >> num;
    for (int i = 0; i < 34 ; ++ i) {
        buf[i] = (num & (1LL << i)) ? '1' : '0';
    }
}

long long out_data()
{
    long long num;
    num = 0LL;
    for (int i = 0; i < 34 ; ++ i) {
        if (buf[i] == '1') num |= (1LL << i);
    }
    num = fix_data(num);
    return num;
}

int main()
{
    cout << "Input server IP address : ";
    //string ip_addr = "10.211.55.3";
    string ip_addr;
    cin >> ip_addr;
    cout << "Input port number : ";
    //int port_num = 1337;
    int port_num;
    cin >> port_num;
    sck = connect_socket(ip_addr.c_str(), port_num);
    cout << "Connected to server" << endl;
    buf = ALLZEROS;
    buf [46] = '1';
    send_recv_buf();
    usleep(500);
    buf = ALLZEROS;
    send_recv_buf();
    buf = ALLZEROS;
    send_recv_buf();

    while (1) {

    	usleep(500);

        if (is_in_req()) {
            cout << "Input request on channel " << in_ch_num() << " : " ;
            do_in();
            buf[45] = '1';
            send_recv_buf();
            buf = ALLZEROS;
            send_recv_buf();
            continue;
        }

        if (is_out_req()) {
            cout << "Output request on channel " << out_ch_num() << " : " << out_data() << endl;
            buf[44] = '1';
            send_recv_buf();
            buf = ALLZEROS;
            send_recv_buf();
            continue;
        }

        buf = ALLZEROS;
        send_recv_buf();

    }
}
