/*
 * Copyright (c) Microsoft Corporation
 *
 * Module Name :
 *        main.c
 *
 * This is the main file containing the client code.
 *
 *
 * Sadagopan Rajaram -- Oct 14, 1999
 *
 */

// Can kill this program on a normal NT console using the
// Alt - X Key combination. Just a shortcut, that is all.
// Serves no useful purpose.

#include "tcclnt.h"
#include "tcsrvc.h"

WSABUF ReceiveBuffer;
CHAR RecvBuf[MAX_BUFFER_SIZE];
IO_STATUS_BLOCK IoStatus;
HANDLE InputHandle;
DWORD bytesRecvd;
WSAOVERLAPPED junk;
SOCKET cli_sock;
DWORD flags;

#if _MSC_FULL_VER >= 13008827
#pragma warning(push)
#pragma warning(disable:4715)			// Not all control paths return (due to infinite loop)
#endif

DWORD
inputUpdate(
    PVOID dummy
    )
{
    // Runs in a single thread getting all the inputs
    // from the keyboard.

    ULONG result;
    // gets a multibyte string for every character
    // pressed on the keyboard.
    CHAR r[MB_CUR_MAX + 1];

    while(1){
         r[0] = _T('\0');
         inchar(r);
         // BUGBUG - Performance issues in sending a single character
         // at a time across the n/w
        if(strlen(r)){
            // may send a single byte or two bytes.
            send(cli_sock,r,strlen(r),0);
        }
    }
    return 1;

}

#if _MSC_FULL_VER >= 13008827
#pragma warning(pop)
#endif

VOID sendUpdate(
    IN DWORD dwError,
    IN DWORD cbTransferred,
    IN LPWSAOVERLAPPED lpOverLapped,
    IN DWORD dwFlags
    )
{
    int error,i;
    // Receives a packet and sends it through the stream parser
    // BUGBUG - For effeciency it can be made inline.
    // I am not sure of the performance increase, but it should
    // be substantial as we will be sending a lot of data.

    if(dwError != 0){
        exit(1);
    }
    for(i=0;i < (int)cbTransferred;i++){
        PrintChar(ReceiveBuffer.buf[i]);
    }
    // Repost the receive on the socket.

    error = WSARecv(cli_sock,
                    &ReceiveBuffer,
                    1,
                    &bytesRecvd,
                    &flags,
                    &junk,
                    sendUpdate
                    );
    if((error == SOCKET_ERROR)
       &&(WSAGetLastError()!=WSA_IO_PENDING)){
        // Implies something wrong with the socket.
        exit(1);
    }
    return;

}

int __cdecl
main(
    IN int argc,
    char *argv[]
    )
/*++
   Opens a single port, binds to the tcserver and passes information back and forth.
--*/
{
    struct sockaddr_in srv_addr,cli_addr;
    LPHOSTENT host_info;
    CLIENT_INFO SendInfo;
    int status;
    WSADATA data;
    #ifdef UNICODE
    // BUGBUG - Trying to write a code that works for
    // both Unicode and ASCII. Gets multi byte sequences
    // Confusion when the tcclnt and tcclnt are in different
    // modes.
    ANSI_STRING Src;
    UNICODE_STRING Dest;
    #endif
    NTSTATUS Status;
    HANDLE Thread;
    DWORD ThreadId;
    COORD coord;
    SMALL_RECT rect;
    int RetVal;
    struct hostent *ht;
    ULONG r;
    TCHAR Buffer[80];



    if((argc<2) || (argc >4)){
        // Error in running the program
        printf("Usage - tcclnt COMPORTNAME [ipaddress]\n");
        exit(0);
    }

    ThreadId = GetEnvironmentVariable(_T("TERM"),Buffer , 80);
    // We need to know if we have a vt100 screen or an ANSI screen.
    AttributeFunction = ProcessTextAttributes;
    if(ThreadId >0){
        // Terminal type exists in the environment.
        // Use it
        if((_tcsncmp(Buffer, _T("VT100"), 5) == 0)||
            _tcsncmp(Buffer, _T("vt100"),5) ==0 )
            AttributeFunction = vt100Attributes;
    }

    hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
    coord.X = MAX_TERMINAL_WIDTH;
    coord.Y = MAX_TERMINAL_HEIGHT;
    rect.Left = rect.Top = 0;
    rect.Right = MAX_TERMINAL_WIDTH -1;
    rect.Bottom = MAX_TERMINAL_HEIGHT -1;

    if(hConsoleOutput == NULL){
        printf("Could not get current console handle %d\n", GetLastError());
        return 1;
    }

    RetVal = SetConsoleScreenBufferSize(hConsoleOutput,
                                        coord
                                        );

    RetVal = SetConsoleWindowInfo(hConsoleOutput,
                                  TRUE,
                                  &rect
                                  );
    if (RetVal == FALSE) {
        printf("Could not set window size %d\n", GetLastError());
        return 1;
    }
    RetVal = SetConsoleMode(hConsoleOutput,ENABLE_PROCESSED_OUTPUT);
    if(RetVal == FALSE){
        printf("Could not console mode %d\n", GetLastError());
        return 1;
    }

    /* Set up client socket */
    InputHandle = GetStdHandle(STD_INPUT_HANDLE);
    if(InputHandle == NULL) return 1;
    SetConsoleMode(InputHandle, 0);
    status=WSAStartup(514,&data);

    if(status){
        printf("Cannot start up %d\n",status);
        return(1);
    }

    cli_sock=WSASocket(PF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);

    if (cli_sock==INVALID_SOCKET){
        printf("Windows Sockets error %d: Couldn't create socket.",
                WSAGetLastError());
        return(1);
    }

    cli_addr.sin_family=AF_INET;
    cli_addr.sin_addr.s_addr=INADDR_ANY;
    cli_addr.sin_port=0;                /* no specific port req'd */

    /* Bind client socket to any local interface and port */

    if (bind(cli_sock,(LPSOCKADDR)&cli_addr,sizeof(cli_addr))==SOCKET_ERROR){
        printf("Windows Sockets error %d: Couldn't bind socket.",
                WSAGetLastError());
        return(1);
    }

    srv_addr.sin_family = AF_INET;
    if(argc == 3){
        srv_addr.sin_addr.s_addr = inet_addr(argv[2]);
        if (srv_addr.sin_addr.s_addr == INADDR_NONE) {
            ht = gethostbyname(argv[2]);
            if(!ht || !ht->h_addr){ // cannot resolve the name
                printf("Cannot resolve %s", argv[2]);
                exit(1);
            }
            memcpy((&(srv_addr.sin_addr.s_addr)),ht->h_addr, ht->h_length);
        }
    }
    else{
        srv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    }
    srv_addr.sin_port=htons(SERVICE_PORT);

    /* Connect to FTP server at address SERVER */

    if (connect(cli_sock,(LPSOCKADDR)&srv_addr,sizeof(srv_addr))==SOCKET_ERROR){
        printf("Windows Sockets error %d: Couldn't connect socket.\n",
               WSAGetLastError());
        return(1);
    }

    SendInfo.len = sizeof(CLIENT_INFO);

    #ifdef UNICODE
    Src.Buffer = argv[1];
    Src.Length = (USHORT)strlen(argv[1]);
    Dest.Buffer = SendInfo.device;
    Dest.MaximumLength = MAX_BUFFER_SIZE;
    Status = RtlAnsiStringToUnicodeString(&Dest, &Src, FALSE);
    if (!NT_SUCCESS(Status)) {
        printf("RtlAnsiStringToUnicodeString failed, ec = 0x%08x\n",Status);
        exit(1);
    }
    send(cli_sock, (PCHAR) &SendInfo, sizeof(CLIENT_INFO), 0);
    #else
    // We are sending to an ANSI String
    strcpy(SendInfo.device, argv[1]);
    send(cli_sock, (PCHAR) &SendInfo, sizeof(CLIENT_INFO), 0);
    #endif
    ReceiveBuffer.len = MAX_BUFFER_SIZE;
    ReceiveBuffer.buf = RecvBuf;
    status=WSARecv(cli_sock,
            &ReceiveBuffer,
            1,
            &bytesRecvd,
            &flags,
            &junk,
            sendUpdate
            );
    if((status == SOCKET_ERROR)
       &&(WSAGetLastError() != WSA_IO_PENDING)){
        printf("Error in recv %d\n",WSAGetLastError());
        exit(1);
    }
    // Create a thread that gets input from the console
    // to send to the bridge.
    Thread = CreateThread(NULL,
                          0,
                          inputUpdate,
                          NULL,
                          0,
                          &ThreadId
                          );
    if (Thread== NULL) {
        exit(1);
    }
    CloseHandle(Thread);

    while(1){
        // Put this thread in an alertable
        // state so that the receive calls can
        // asynchronously terminate within the
        // context of this thread.
        status=SleepEx(INFINITE,TRUE);
    }
    // We never return here.
    closesocket(cli_sock);
    return 0;
}