// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (C) 1993, 1994  Microsoft Corporation.  All Rights Reserved.
//
//  MODULE:   misc.c
//
//  PURPOSE:  Contains all helper functions "global" to the application.
//
//  FUNCTIONS:
//    CenterWindow - Center one window over another.
//    ReceiveInBox - Reads incoming socket data.
//    SendOutBox   - Writes outgoing socket data.
//    AtoH         - Converts ascii string to network order hex
//    BtoH         - Converts ascii byte to hex
//    CleanUp      - closes sockets and detaches winsock dll
//    GetAddrString - Puts IPX address into <network>.<node>.<socket> string format
//    HtoA          - Converts network order hex to ascii string
//    HtoB          - Converts hex byte to asci string
//
//  COMMENTS:
//
//

#include <windows.h>            // required for all Windows applications
#include <windowsx.h>
#include <wsipx.h>
#include "globals.h"            // prototypes specific to this application



//
//  FUNCTION: CenterWindow(HWND, HWND)
//
//  PURPOSE:  Center one window over another.
//
//  PARAMETERS:
//    hwndChild - The handle of the window to be centered.
//    hwndParent- The handle of the window to center on.
//
//  RETURN VALUE:
//
//    TRUE  - Success
//    FALSE - Failure
//
//  COMMENTS:
//
//    Dialog boxes take on the screen position that they were designed
//    at, which is not always appropriate. Centering the dialog over a
//    particular window usually results in a better position.
//

BOOL CenterWindow(HWND hwndChild, HWND hwndParent)
{
    RECT    rcChild, rcParent;
    int     cxChild, cyChild, cxParent, cyParent;
    int     cxScreen, cyScreen, xNew, yNew;
    HDC     hdc;

    // Get the Height and Width of the child window
    GetWindowRect(hwndChild, &rcChild);
    cxChild = rcChild.right - rcChild.left;
    cyChild = rcChild.bottom - rcChild.top;

    // Get the Height and Width of the parent window
    GetWindowRect(hwndParent, &rcParent);
    cxParent = rcParent.right - rcParent.left;
    cyParent = rcParent.bottom - rcParent.top;

    // Get the display limits
    hdc = GetDC(hwndChild);
    cxScreen = GetDeviceCaps(hdc, HORZRES);
    cyScreen = GetDeviceCaps(hdc, VERTRES);
    ReleaseDC(hwndChild, hdc);

    // Calculate new X position, then adjust for screen
    xNew = rcParent.left + ((cxParent - cxChild) / 2);
    if (xNew < 0)
    {
        xNew = 0;
    }
    else if ((xNew + cxChild) > cxScreen)
    {
        xNew = cxScreen - cxChild;
    }

    // Calculate new Y position, then adjust for screen
    yNew = rcParent.top  + ((cyParent - cyChild) / 2);
    if (yNew < 0)
    {
        yNew = 0;
    }
    else if ((yNew + cyChild) > cyScreen)
    {
        yNew = cyScreen - cyChild;
    }

    // Set it, and return
    return SetWindowPos(hwndChild,
                        NULL,
                        xNew, yNew,
                        0, 0,
                        SWP_NOSIZE | SWP_NOZORDER);
}

//
//  FUNCTION: ReceiveInBox(HWND, WPARAM, LPARAM, char *, int)
//
//  PURPOSE:  Reads incoming data from socket
//
//  PARAMETERS:
//    hWnd      - Handle to current window
//    uParam    - WPARAM (unused)
//    lParam    - LPARAM contains event (FD_READ or FD_CLOSE).
//    szRBuf    - Receive Buffer
//    cRBufLen  - size of Receive Buffer
//
//  RETURN VALUE:
//
//    TRUE  - Data Read
//    FALSE - If FD_CLOSE message
//
//  COMMENTS:
//
//    Called if socket has data OR if it is closed.  If closed post
//    WM_DISCONNECTED message.  Else read data and make sure it is
//    NULL terminated.
//

BOOL ReceiveInBox(HWND hWnd, WPARAM uParam, LPARAM lParam, char * szRBuf, int cRBufLen)
{
    char * pRBuf;     // temp buf pointer
    int cBytesRead;   // count of bytes actually read

    
    if (LOWORD(lParam) == FD_CLOSE)                   // Is this a FD_CLOSE event?
    {
        SendMessage(hWnd, MW_DISCONNECTED, 0, 0);     // Yes, post message
        return(FALSE);
    }

    pRBuf = szRBuf;   // Set temp pointer
    cRBufLen--;       // Save room for null terminator
          
    // read socket
    if((cBytesRead = recv(sock, pRBuf, cRBufLen, 0)) != SOCKET_ERROR)
        pRBuf += cBytesRead;    // Move temp pointer to end of buffer
              
    *pRBuf = 0;   // Null terminate - if recv() failed, then prBuf will 
                  // point to first byte of the buffer
    
    return (TRUE);   // We've got a buffer to display
}

//
//  FUNCTION: SendOutBox(char *, int)
//
//  PURPOSE:  Reads incoming data from socket
//
//  PARAMETERS:
//    szSBuf    - Send Buffer
//    cSBufLen  - size of Send Buffer
//
//  COMMENTS:
//
//    Writes send buffer to socket -- repeats until all data is sent.
//

void SendOutBox(char * szSBuf, int cSBufLen)
{
    char * pSBuf;
    int cBytesSent;

    pSBuf = szSBuf; // Set temp pointer
    
    while((cBytesSent = send(sock,
                             pSBuf,
                             cSBufLen,
                             0)) != SOCKET_ERROR)
    {
        pSBuf += cBytesSent;
        cSBufLen -= cBytesSent;
        if(!cSBufLen) return;
    }
}

//
//  FUNCTION: AtoH(char *, char *, int)
//
//  PURPOSE:  Converts ascii string to network order hex
//
//  PARAMETERS:
//    src    - pointer to input ascii string
//    dest   - pointer to output hex
//    destlen - size of dest
//
//  COMMENTS:
//
//    2 ascii bytes make a hex byte so must put 1st ascii byte of pair
//    into upper nibble and 2nd ascii byte of pair into lower nibble.
//

void AtoH(char * src, char * dest, int destlen)
{
    char * srcptr;

    srcptr = src;

    while(destlen--)
    {
    *dest = BtoH(*srcptr++) << 4;    // Put 1st ascii byte in upper nibble.
    *dest++ += BtoH(*srcptr++);      // Add 2nd ascii byte to above.
    }
}

//
//  FUNCTION: BtoH(char *, char *, int)
//
//  PURPOSE:  Converts ascii byte to numeric
//
//  PARAMETERS:
//    ch - ascii byte to convert
//
//  RETURNS:
//    associated numeric value
//
//  COMMENTS:
//
//    Will convert any hex ascii digit to its numeric counterpart.
//    Puts in 0xff if not a valid hex digit.
//

unsigned char BtoH(char ch)
{
    if (ch >= '0' && ch <= '9') return (ch - '0');        // Handle numerals
    if (ch >= 'A' && ch <= 'F') return (ch - 'A' + 0xA);  // Handle capitol hex digits
    if (ch >= 'a' && ch <= 'f') return (ch - 'a' + 0xA);  // Handle small hex digits
    return(255);
}

//
//  FUNCTION: CleanUp(void)
//
//  PURPOSE:  Protocol specific cleanup function
//
//  COMMENTS:
//
//    Deletes our two possible sockets (they might not exist which
//    just means closesocket might return an error -- we don't care).
//

void CleanUp(void)
{
    closesocket(SrvSock);  // Close our server side socket
    closesocket(sock);     // Close our connection specific socket
    WSACleanup();          // Nix the DLL
}

//
//  FUNCTION: GetAddrString(PSOCKADDR_IPX, char *)
//
//  PURPOSE:  Converts IPX address to ascii string for displaying
//
//  PARAMETERS:
//    pSAddr - pointer to socket address struc
//    dest   - pointer to destination string
//
//  COMMENTS:
//
//    Address is in network order to use HtoA to convert to ascii.
//    
//    Final format is 
//      <8 char network address>.<12 char node address>.<4 char sock address>
//

void GetAddrString(PSOCKADDR_IPX pSAddr, char * dest)
{
    char abuf[15];                                // temp buffer
    char * currptr;                               // temp destination pointer
    int saddrlen = sizeof(struct sockaddr_ipx);   // sizeof address struc

    currptr = dest; // initialize destination pointer

    HtoA((char *)&pSAddr->sa_netnum, abuf, 4);    // convert network number
    lstrcpy(currptr, abuf);
    currptr += 8;
    lstrcat(currptr, ".");                        // don't forget seperator
    currptr++;
    
    HtoA((char *)&pSAddr->sa_nodenum, abuf, 6);   // convert node number
    lstrcat(currptr, abuf);
    currptr += 12;
    lstrcat(currptr, ".");                        // seperator
    currptr++;

    HtoA((char *)&pSAddr->sa_socket, abuf, 2);    // convert socket number
    lstrcat(currptr, abuf);
   
}

//
//  FUNCTION: HtoA(char *, char *, int)
//
//  PURPOSE:  Converts network ordered hex to ascii string
//
//  PARAMETERS:
//    src     - pointer to network ordered hex
//    dest    - pointer to ascii string
//    srclen  - size of hex number in bytes
//
//  COMMENTS:
//
//    1 byte hex = 2 bytes ascii so convert high order nibble with HtoB()
//    then convert low order nibble. dest buffer better be 2*srclen + 1.
//

void HtoA(char * src, char * dest, int srclen)
{
    char * destptr; // temp pointers
    UCHAR * srcptr;
        
    srcptr = (UCHAR *)src;
    destptr = dest;

    while(srclen--)
    {
    *destptr++ = HtoB((UCHAR)(*srcptr >> 4));      // Convert high order nibble
    *destptr++ = HtoB((UCHAR)(*srcptr++ & 0x0F));  // Convert low order nibble
    }
    *destptr = 0;  // Null terminator
}

//
//  FUNCTION: HtoB(UCHAR)
//
//  PURPOSE:  Converts hex byte to ascii byte
//
//  PARAMETERS:
//    ch - Hex byte
//                 
//  RETURNS:
//    ascii byte
//
//  COMMENTS:
//
//    We actually only convert a nibble since 1 byte hex = 2 bytes ascii.
//    So if ch > 0xf we just return 'X'.
//

char HtoB(UCHAR ch)
{
    if (ch <= 9) return ('0' + ch);             // handle decimal values
    if (ch <= 0xf) return ('A' + ch - 10);      // handle hexidecimal specific values
    return('X');                                // Someone screwed up
}
