// mainview.cpp : implementation file
//

#include "stdafx.h"
#include "deserv.h"
#include "mainview.h"

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CMainView

IMPLEMENT_DYNCREATE(CMainView, CFormView)

CMainView::CMainView()
  : CFormView(CMainView::IDD)
{
  m_s = INVALID_SOCKET;  // initialize socket to invalid handle

  //{{AFX_DATA_INIT(CMainView)
    // NOTE: the ClassWizard will add member initialization here
  //}}AFX_DATA_INIT
}

CMainView::~CMainView()
{
  // if the socket was opened successfully, close it

  if (m_s != INVALID_SOCKET)
  {
    closesocket(m_s);
    m_s = INVALID_SOCKET;
  }
}

void CMainView::DoDataExchange(CDataExchange* pDX)
{
  CFormView::DoDataExchange(pDX);
  //{{AFX_DATA_MAP(CMainView)
    // NOTE: the ClassWizard will add DDX and DDV calls here
  //}}AFX_DATA_MAP
}

void CMainView::OnInitialUpdate()
{
  char pszMessage[100]; // informational message

  // get pointer to list box used for status messages

  CListBox *plb = (CListBox *)GetDlgItem(IDC_LIST_STATUS);

  // create the socket and prepare it
  // for receiving data

  plb->InsertString(0, "Initializing...");

  m_s = socket(AF_INET, SOCK_DGRAM, 0);
  if (m_s == INVALID_SOCKET)
    plb->InsertString(0, "Datagram echo server could not create socket");
  else
  {
    // bind the socket, allowing WinSock to assign the service port

    m_addr.sin_family = AF_INET;                 // Internet address family
    m_addr.sin_port = 0;                         // let WinSock assign a port
    m_addr.sin_addr.s_addr = htonl(INADDR_ANY);  // any network interface

    if (bind(m_s, (LPSOCKADDR)&m_addr, sizeof(m_addr)) == SOCKET_ERROR)
      plb->InsertString(0, "Datagram echo server could not bind socket");
    else
    {
      // find out the port number WinSock assigned

      SOCKADDR_IN addr;
      int nAddrLen = sizeof(addr);

      if (getsockname(m_s, (LPSOCKADDR)&addr, &nAddrLen) == SOCKET_ERROR)
        plb->InsertString(0, "Datagram echo server could not get socket's port");
      else
      {
        wsprintf(pszMessage, "Datagram echo server using port %d",
         ntohs(addr.sin_port));
        plb->InsertString(0, pszMessage);

        // do the asynchronous select to wait for data

        if (WSAAsyncSelect(m_s, m_hWnd, WM_USER_ASYNC_SELECT,
         FD_READ | FD_WRITE) == SOCKET_ERROR)
          plb->InsertString(0, "Datagram echo server could not do async select");
      }
    }
  }
}

BEGIN_MESSAGE_MAP(CMainView, CFormView)
  //{{AFX_MSG_MAP(CMainView)
  ON_MESSAGE(WM_USER_ASYNC_SELECT, OnAsyncSelect)
  //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CMainView message handlers

/////////////////////////////////////////////////////////////////////////////
// CMainView::OnAsyncSelect()
//
// Receives data from a client and echoes the data back to the sending client.
// While there is data yet to be sent back to the sending client, the server
// will not receive any more data. (a single data buffer is used for
// incoming and outgoing data)
//
LONG CMainView::OnAsyncSelect(WPARAM wParam, LPARAM lParam)
{
  char pszMessage[100];        // informational message
  static char pBuf[101];       // send/recv buffer
  int nBytesRecv;              // number of bytes received
  int nBytesSent;              // number of bytes sent
  static int nBytesToSend = 0; // number of bytes to send
  int nError;                  // WinSock error
  static SOCKADDR_IN addrFrom; // address of client
  static int nAddrFromLen = sizeof(addrFrom); // length of client address struct
  static IN_ADDR inFrom;       // IP address of client

  // get pointer to list box used for status messages
  CListBox *plb = (CListBox *)GetDlgItem(IDC_LIST_STATUS);
 
  // check for an error
  if (WSAGETSELECTERROR(lParam) != 0)
  {
    wsprintf(pszMessage, "Datagrem echo server async select got error %d",
     WSAGETSELECTERROR(lParam));
    plb->InsertString(0, pszMessage);
    return 0L;
  }

  // what event are we being notified of?
  switch (WSAGETSELECTEVENT(lParam))
  {
    case FD_WRITE:
      // echo the data back to the client

      plb->InsertString(0, "FD_WRITE");

      // are there bytes to send?
      if (nBytesToSend != 0)
      {
        // send the data
        nBytesSent = sendto(m_s, pBuf, nBytesToSend, 0,
         (LPSOCKADDR)&addrFrom, nAddrFromLen);

        // check for send error
        if (nBytesSent == SOCKET_ERROR)
        {
          // if the error is just that the send would block,
          // don't do anything...we'll get another FD_WRITE soon
          nError = WSAGetLastError();
          if (nError != WSAEWOULDBLOCK)
          {
            wsprintf(pszMessage, "Error %d sending data to %s, %d",
             nError, inet_ntoa(inFrom), ntohs(addrFrom.sin_port));
            plb->InsertString(0, pszMessage);

            nBytesToSend = 0;

            // just in case the FD_READ was called but it didn't read
            // because the buffer still contained data to send
            PostMessage(WM_USER_ASYNC_SELECT, m_s, WSAMAKESELECTREPLY(FD_READ, 0));
          }
        }
        else
        {
          wsprintf(pszMessage, "Data sent (%s) to %s, %d",
           pBuf, inet_ntoa(inFrom), ntohs(addrFrom.sin_port));
          plb->InsertString(0, pszMessage);
          nBytesToSend = 0;
        }
      }
      break;

    case FD_READ:
      // receive data back from a client

      plb->InsertString(0, "FD_READ");

      // if there are still bytes waiting to be sent back (echoed)
      // to the client, don't do anything here (the FD_WRITE handler will
      // generate FD_READ when it is through with sending)
      if (nBytesToSend == 0)
      {
        // receive data
        nBytesRecv = recvfrom(m_s, pBuf, 100, 0, (LPSOCKADDR)&addrFrom, &nAddrFromLen);

        // check for receive error
        if (nBytesRecv == SOCKET_ERROR)
        {
          // if the error is just that the receive would block,
          // don't do anything...we'll get another FD_READ soon
          nError = WSAGetLastError();
          if (nError != WSAEWOULDBLOCK)
          {
            wsprintf(pszMessage, "Error %d receiving data", nError);
            plb->InsertString(0, pszMessage);
          }
        }
        else
        {
          // save sending client's IP address
          memcpy(&inFrom, &addrFrom.sin_addr.s_addr, 4);

          nBytesToSend = nBytesRecv;

          pBuf[nBytesToSend] = '\0';
          wsprintf(pszMessage, "Data received (%s) from %s, %d",
           pBuf, inet_ntoa(inFrom), ntohs(addrFrom.sin_port));
          plb->InsertString(0, pszMessage);

          // just in case the FD_WRITE was called but it didn't have
          // any data to send at that time (it has data to send now)
          PostMessage(WM_USER_ASYNC_SELECT, m_s, WSAMAKESELECTREPLY(FD_WRITE, 0));
        }
      }
      break;

    default:
      break;
  }

  return 0L;
}

