/* ---------------------------------------------------
   tcp_server.cxx
   --------------------------------------------------- */
#include <simgear/compiler.h>
#include <simgear/debug/logstream.hxx>
#include <string>
#include <iostream>
#ifdef _MSC_VER
#include <conio.h>
#endif

#include "sg_socket.hxx"

#define DEF_HOST "localhost"
//#define DEF_HOST "192.168.1.174"
#define DEF_PORT "5050"
#define DEF_TYPE "tcp"
#define DEF_DIR  SG_IO_BI

char * HostName = DEF_HOST;
char * HostPort = DEF_PORT;
char * HostType = DEF_TYPE;
SGProtocolDir HostDir = DEF_DIR;

#define EndBuf(a) ( a + strlen(a) )

static int read_counter = 0;

using std::string;
using std::cout;

class TcpServer
{
public:
    TcpServer();
    ~TcpServer();
    bool open();
    bool process();
    bool close();

private:
    SGIOChannel* channel;
};

TcpServer::TcpServer()
{
    channel = new SGSocket( HostName, HostPort, HostType );
}

TcpServer::~TcpServer()
{
    delete channel;
}

bool
TcpServer::open()
{
    return channel->open( HostDir );
}

void print_hex(char * buf, int len)
{
    static char _s_hex_buf[256];
    static char _s_asc_buf[32];
    char * ph = _s_hex_buf;
    char * pa = _s_asc_buf;
    int i, c, off;
    *ph = 0;
    off = 0;
    for (i = 0; i < len; i++)
    {
        c = buf[i];
        sprintf(EndBuf(ph),"%02X ",c);
        if ((c < ' ')||(c >= 0x7f))
            c = '.';
        pa[off++] = c;
        if (off == 16) {
            pa[off] = 0;
            printf("%s %s\n", ph, pa);
            *ph = 0;
            off = 0;
        }
    }
    if (off) {
        pa[off] = 0;
        while (off < 16) {
            strcat(ph, "   ");
            off++;
        }
        printf("%s %s\n", ph, pa);
    }
}

#define DEF_READ_LEN 648

bool
TcpServer::process()
{
    char buf[1024];
    int len;
    //while ((len = channel->readline( buf, sizeof(buf) )) > 0)
    //while ((len = channel->read( buf, DEF_READ_LEN )) > 0)
    len = channel->read( buf, DEF_READ_LEN );
    if (len > 0) {
	    //cout << len << ": " << buf;
        print_hex(buf,len);
        read_counter++;
    } else if (len == -2) {
        // just a TIME OUT
    } else if (len == 0) {
        // just nothing read (after a connection)
    } else {
        SG_LOG( SG_IO, SG_ALERT, "Channel read returned [" << len << "]");
        return false;
    }
    return true;
}

bool
TcpServer::close()
{
    return channel->close();
}

#ifdef _MSC_VER
int check_key(void)
{
    int chr = _kbhit();
    if (chr)
        chr = _getch();
    return chr;
}
#endif

int run_server(void)
{
    int iret = 1;
    SG_LOG( SG_IO, SG_INFO, "Creating TCP server: host " << HostName <<
        ", port " << HostPort << ", Direction " <<
        ((HostDir == SG_IO_BI) ? "BI" : (HostDir == SG_IO_IN) ? "IN" : "OUT"));

    TcpServer server;
    if (! server.open() ) {
        SG_LOG( SG_IO, SG_ALERT, "Server OPEN FAILED!");
        return iret;
    }

#ifdef _MSC_VER
    SG_LOG( SG_IO, SG_INFO, "Any key to exit...");
#endif // _MSC_VER

    iret = 0;
    while (1) {
        if ( !server.process() ) {
            iret = 1;
            break;
        }
#ifdef _MSC_VER
        if (check_key())
            break;
        Sleep(1000);
        if (check_key())
            break;
#else
        sleep(1);
#endif
    }

    SG_LOG( SG_IO, SG_INFO, "TCP server exit...");
    
    server.close();

    return iret;
}

void give_help(void)
{
    printf("tcp_server [Options]\n");
    printf("Options:\n");
    printf(" -?        This help.\n");
    printf(" -d <dir>  Set Direction. Only 'BI', 'IN' or 'OUT' allowed. (def=%s)\n",
        ((HostDir == SG_IO_BI) ? "BI" :
        (HostDir == SG_IO_BI) ? "IN" : "OUT"));
    printf(" -h <host> Set HOST name (def=%s)\n", HostName);
    printf(" -p <port> Set PORT value (def=%s)\n", HostPort);
    printf(" -t <type> Set connection type. Only 'tcp' or 'udp' allowed. (def=%s)\n",
        HostType);
    printf("Set up a TCP server, and wait for traffic. Any key to EXIT.\n");
}

int main(int argc, char * argv[])
{
    int i, c;
    char * arg;
    sglog().setLogLevels( SG_ALL, SG_INFO );
    for (i = 1; i < argc; i++) {
        arg = argv[i];
        if (*arg == '-') {
            arg++;
            while (*arg == '-') arg++;
            c = *arg;
            switch (c)
            {
            case '?':
                give_help();
                return 0;
                break;
            case 'd':
                i++;
                if (i < argc) {
                    arg = argv[i];
                    HostDir = (strcmp(arg,"BI")==0) ? SG_IO_BI :
                        (strcmp(arg,"IN")==0) ? SG_IO_IN :
                        (strcmp(arg,"OUT")==0) ? SG_IO_OUT : (SGProtocolDir)-1;
                    if (HostDir == (SGProtocolDir)-1) {
                        SG_LOG(SG_IO,SG_ALERT,"Only 'BI', 'IN', 'OUT' allowed! Not [" << arg << "] Aborting");
                        return 1;
                    }
                    SG_LOG(SG_IO,SG_INFO,"Set Direction to [" << arg << "]");
                } else {
                    i++;
                    goto Req_Fol;
                }
                break;
            case 'h':
                i++;
                if (i < argc) {
                    arg = argv[i];
                    HostName = arg;
                    SG_LOG(SG_IO,SG_INFO,"Set HOST to [" << HostName << "]");
                } else {
                    i++;
                    goto Req_Fol;
                }
                break;
            case 'p':
                i++;
                if (i < argc) {
                    arg = argv[i];
                    HostPort = arg;
                    SG_LOG(SG_IO,SG_INFO,"Set PORT to [" << HostPort << "]");
                } else {
                    i++;
                    goto Req_Fol;
                }
                break;
            case 't':
                i++;
                if (i < argc) {
                    arg = argv[i];
                    HostType = arg;
                    if ((strcmp(arg,"tcp")==0)||(strcmp(arg,"udp")==0)) {
                        SG_LOG(SG_IO,SG_INFO,"Set TYPE to [" << HostType << "]");
                    } else {
                        SG_LOG(SG_IO,SG_ALERT,"Connection TYPE can ONLY be 'tcp' or 'upd', NOT [" << HostType << "]! Aborting");
                        return 1;
                    }
                } else {
                    i++;
                    goto Req_Fol;
                }
                break;
            default:
                goto Bad_Arg;
            }

        } else {
Req_Fol:
             SG_LOG( SG_IO, SG_ALERT, "ERROR: argument [" <<
                  argv[i] << "] requires a following argument! Aborting...");
             return 1;
Bad_Arg:
            SG_LOG( SG_IO, SG_ALERT, "ERROR: Unknown argument [" <<
                argv[i] << "] Aborting...");
            return 1;
        }
    }

    i = run_server();

    return i;
}
