// ScanServCtl.cpp : Implementation of the CScanServCtrl ActiveX Control class.

#include "stdafx.h"
#include "ScanServ.h"
#include "ScanServCtl.h"
#include "ScanServPpg.h"
#include "Winsock2.h"
#include "errno.h"

typedef unsigned long	ulong;
#define	TKNAME(a,b,c,d) (((d)<<24)|((c)<<16)|((b)<<8)|(a))
#include "sloppidef.h"




#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define	CloseSocket(s)	closesocket(s)

#define	MAXHOSTNAME	(128)

char    rcv[ 2048 ];			/* General receive buffer	    */
char    xmit[ 2048 ];			/* General transmit buffer	    */

unsigned int	listenSocket    = 0;
ulong	sock;
int     c = 0;
long    PacketToken = 0;
char    ProductID[ 1024 ];
char    ProductQuery[ 1024 ];
short   Token;

SLEVENT Event;

#define	mkstr(t)    # t
#define	mkstrstr(t) mkstr(t)

struct
  {
    SLOPPITOKEN	    token;
    char	   *tname;
  } nameToken[] =
  {
      { SLT_TERMINATE,	    mkstr( SLT_TERMINATE    ) },
      { SLT_CAPQUERY,	      mkstr( SLT_CAPQUERY     ) },
      { SLT_CAPRESPONSE,    mkstr( SLT_CAPRESPONSE  ) },
      { SLT_MODESETQUERY,   mkstr( SLT_MODESETQUERY ) },
      { SLT_MODERESPONSE,   mkstr( SLT_MODERESPONSE ) },
      { SLT_ERRREPORT,	    mkstr( SLT_ERRREPORT    ) },
      { SLT_PRODUCTQUERY,   mkstr( SLT_PRODUCTQUERY ) },
      { SLT_DATA,	          mkstr( SLT_DATA         )	},
      { SLT_REGISTRATION,   mkstr( SLT_REGISTRATION ) },
      { SLT_EVENT,          mkstr( SLT_EVENT        ) },
      { (SLOPPITOKEN) 0,    NULL }
  };


struct  { 
          long  SLT;
          short SL;
        } tokenTypes[] =  { 
                            { SLT_TERMINATE,	  SL_TERMINATE    },
                            { SLT_CAPQUERY,	    SL_CAPQUERY     },	   
                            { SLT_CAPRESPONSE,  SL_CAPRESPONSE  }, 
                            { SLT_MODESETQUERY, SL_MODESETQUERY },
                            { SLT_MODERESPONSE, SL_MODERESPONSE },
                            { SLT_ERRREPORT,	  SL_ERRREPORT    },	 
                            { SLT_PRODUCTQUERY, SL_PRODUCTQUERY },
                            { SLT_DATA,	        SL_DATA         },	       
                            { SLT_REGISTRATION, SL_REGISTRATION },
                            { SLT_EVENT,        SL_EVENT        },
                            { 0,                0               }
                          }; 


IMPLEMENT_DYNCREATE(CScanServCtrl, COleControl)


/////////////////////////////////////////////////////////////////////////////
// Message map

BEGIN_MESSAGE_MAP(CScanServCtrl, COleControl)
	//{{AFX_MSG_MAP(CScanServCtrl)
	// NOTE - ClassWizard will add and remove message map entries
	//    DO NOT EDIT what you see in these blocks of generated code !
	//}}AFX_MSG_MAP
	ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties)
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// Dispatch map

BEGIN_DISPATCH_MAP(CScanServCtrl, COleControl)
	//{{AFX_DISPATCH_MAP(CScanServCtrl)
	DISP_FUNCTION(CScanServCtrl, "Connect", Connect, VT_I2, VTS_I4)
	DISP_FUNCTION(CScanServCtrl, "Listen", Listen, VT_I2, VTS_NONE)
	DISP_FUNCTION(CScanServCtrl, "GetTrivialPacket", GetTrivialPacket, VT_BSTR, VTS_NONE)
	DISP_FUNCTION(CScanServCtrl, "SendFile", SendFile, VT_I4, VTS_BSTR)
	DISP_FUNCTION(CScanServCtrl, "SockSend", SockSend, VT_EMPTY, VTS_BSTR VTS_I4)
	DISP_FUNCTION(CScanServCtrl, "Close", Close, VT_EMPTY, VTS_NONE)
	DISP_FUNCTION(CScanServCtrl, "GetPacketType", GetPacketType, VT_I4, VTS_NONE)
	DISP_FUNCTION(CScanServCtrl, "GetProductID", GetProductID, VT_BSTR, VTS_NONE)
	DISP_FUNCTION(CScanServCtrl, "GetProductQuery", GetProductQuery, VT_BSTR, VTS_NONE)
	DISP_FUNCTION(CScanServCtrl, "Exit", Exit, VT_EMPTY, VTS_NONE)
	DISP_FUNCTION(CScanServCtrl, "SendText", SendText, VT_I4, VTS_BSTR)
	DISP_FUNCTION(CScanServCtrl, "SendDelayedText", SendDelayedText, VT_I4, VTS_BSTR VTS_I2)
	DISP_FUNCTION(CScanServCtrl, "SendTextBigEndian", SendTextBigEndian, VT_I4, VTS_BSTR)
	DISP_FUNCTION(CScanServCtrl, "SendTextLittleEndian", SendTextLittleEndian, VT_I4, VTS_BSTR)
	DISP_FUNCTION(CScanServCtrl, "GetToken", GetToken, VT_I2, VTS_NONE)
	DISP_FUNCTION(CScanServCtrl, "GetEventStarting", GetEventStarting, VT_I4, VTS_NONE)
	DISP_FUNCTION(CScanServCtrl, "GetEventEnding", GetEventEnding, VT_I4, VTS_NONE)
	DISP_FUNCTION(CScanServCtrl, "GetEventTicktime", GetEventTicktime, VT_I4, VTS_NONE)
	DISP_FUNCTION(CScanServCtrl, "GetEventUnitID", GetEventUnitID, VT_BSTR, VTS_NONE)
	//}}AFX_DISPATCH_MAP
END_DISPATCH_MAP()


/////////////////////////////////////////////////////////////////////////////
// Event map

BEGIN_EVENT_MAP(CScanServCtrl, COleControl)
	//{{AFX_EVENT_MAP(CScanServCtrl)
	// NOTE - ClassWizard will add and remove event map entries
	//    DO NOT EDIT what you see in these blocks of generated code !
	//}}AFX_EVENT_MAP
END_EVENT_MAP()


/////////////////////////////////////////////////////////////////////////////
// Property pages

// TODO: Add more property pages as needed.  Remember to increase the count!
BEGIN_PROPPAGEIDS(CScanServCtrl, 1)
	PROPPAGEID(CScanServPropPage::guid)
END_PROPPAGEIDS(CScanServCtrl)


/////////////////////////////////////////////////////////////////////////////
// Initialize class factory and guid

IMPLEMENT_OLECREATE_EX(CScanServCtrl, "SCANSERV.ScanServCtrl.1",
	0x87929f0a, 0x7cac, 0x11d5, 0xbb, 0x1c, 0, 0xe0, 0x7d, 0x93, 0xbf, 0xd9)


/////////////////////////////////////////////////////////////////////////////
// Type library ID and version

IMPLEMENT_OLETYPELIB(CScanServCtrl, _tlid, _wVerMajor, _wVerMinor)


/////////////////////////////////////////////////////////////////////////////
// Interface IDs

const IID BASED_CODE IID_DScanServ =
		{ 0x94d54b14, 0x7d27, 0x11d5, { 0xbb, 0x1c, 0, 0xe0, 0x7d, 0x93, 0xbf, 0xd9 } };
const IID BASED_CODE IID_DScanServEvents =
		{ 0x94d54b15, 0x7d27, 0x11d5, { 0xbb, 0x1c, 0, 0xe0, 0x7d, 0x93, 0xbf, 0xd9 } };


/////////////////////////////////////////////////////////////////////////////
// Control type information

static const DWORD BASED_CODE _dwScanServOleMisc =
	OLEMISC_INVISIBLEATRUNTIME |
	OLEMISC_SETCLIENTSITEFIRST |
	OLEMISC_INSIDEOUT |
	OLEMISC_CANTLINKINSIDE |
	OLEMISC_RECOMPOSEONRESIZE;

IMPLEMENT_OLECTLTYPE(CScanServCtrl, IDS_SCANSERV, _dwScanServOleMisc)


/////////////////////////////////////////////////////////////////////////////
// CScanServCtrl::CScanServCtrlFactory::UpdateRegistry -
// Adds or removes system registry entries for CScanServCtrl

BOOL CScanServCtrl::CScanServCtrlFactory::UpdateRegistry(BOOL bRegister)
{
	// TODO: Verify that your control follows apartment-model threading rules.
	// Refer to MFC TechNote 64 for more information.
	// If your control does not conform to the apartment-model rules, then
	// you must modify the code below, changing the 6th parameter from
	// afxRegApartmentThreading to 0.

	if (bRegister)
		return AfxOleRegisterControlClass(
			AfxGetInstanceHandle(),
			m_clsid,
			m_lpszProgID,
			IDS_SCANSERV,
			IDB_SCANSERV,
			afxRegApartmentThreading,
			_dwScanServOleMisc,
			_tlid,
			_wVerMajor,
			_wVerMinor);
	else
		return AfxOleUnregisterClass(m_clsid, m_lpszProgID);
}


/////////////////////////////////////////////////////////////////////////////
// CScanServCtrl::CScanServCtrl - Constructor

CScanServCtrl::CScanServCtrl()
{
	InitializeIIDs(&IID_DScanServ, &IID_DScanServEvents);

	// TODO: Initialize your control's instance data here.
}


/////////////////////////////////////////////////////////////////////////////
// CScanServCtrl::~CScanServCtrl - Destructor

CScanServCtrl::~CScanServCtrl()
{
	// TODO: Cleanup your control's instance data here.
}


/////////////////////////////////////////////////////////////////////////////
// CScanServCtrl::OnDraw - Drawing function

void CScanServCtrl::OnDraw(
			CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
	// TODO: Replace the following code with your own drawing code.
	pdc->FillRect(rcBounds, CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));
	pdc->Ellipse(rcBounds);
}


/////////////////////////////////////////////////////////////////////////////
// CScanServCtrl::DoPropExchange - Persistence support

void CScanServCtrl::DoPropExchange(CPropExchange* pPX)
{
	ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
	COleControl::DoPropExchange(pPX);

	// TODO: Call PX_ functions for each persistent custom property.

}


/////////////////////////////////////////////////////////////////////////////
// CScanServCtrl::OnResetState - Reset control to default state

void CScanServCtrl::OnResetState()
{
	COleControl::OnResetState();  // Resets defaults found in DoPropExchange

	// TODO: Reset any other control state here.
}


/////////////////////////////////////////////////////////////////////////////
// CScanServCtrl message handlers

short CScanServCtrl::Connect(long Port) 
  {
    char   myname[ MAXHOSTNAME + 1 ];
    struct hostent     *hp;
    struct sockaddr_in	sa   = {0};

    int	    one	    = 1;
    char   *onep    = (char *)&one;

    sock = 0;

    WORD wVersionRequested;
    WSADATA wsaData;
    int err;

    wVersionRequested = MAKEWORD( 2, 0 );       
    err = WSAStartup( wVersionRequested, &wsaData );

    if ( err != 0 ) 
      {
	fputs( "A useable WinSock DLL not fount", stdout );
	return -1;
      }

    gethostname( myname, MAXHOSTNAME );

    hp= gethostbyname( myname );	/* get our address info		    */

    if ( hp  ==  NULL )			/* we don't exist !?		    */
      {
	fputs( "gethostbyname failed", stdout );
	return	-1;
      }

    sa.sin_family   = hp->h_addrtype;	/* this is our host address	    */
    sa.sin_port	    = htons( (unsigned short) Port );	/* this is our port number	    */

    listenSocket	= socket( AF_INET, SOCK_STREAM, 0 );

    if ( listenSocket  <  0 )
      {
	fputs( "socket failed", stdout );
	return	-1;
      }

    setsockopt( listenSocket, IPPROTO_TCP, TCP_NODELAY, onep, sizeof one );

    if ( bind( listenSocket, (struct sockaddr *)&sa, sizeof (struct sockaddr_in))  <  0 )
      {
	fputs( "bind failed", stdout );
	CloseSocket( listenSocket );
	return	-1;
      }

    if ( listen( listenSocket, 1 ) == 0 )
	return listenSocket;
    else
	return -1;
  }


/*  Return the token from the packet.  When in doubt, return TERMINATE.
*/

static SLOPPITOKEN packetToken( SLPACKET *pkt )
  {
    long	len;
    SLOPPITOKEN	tkn;
    int		i;

    if ( !pkt )				/* If nothing passed		    */
	    return	SLT_TERMINATE;

    len	= ntohl( pkt->header.length );	/* Make sure it has a token	    */
    if ( len  <  sizeof (SLOPPIHEADER))
	    return	SLT_TERMINATE;

    tkn	= pkt->header.token;

    for ( i = 0; nameToken[ i ].token  !=  SLT_NOTATOKEN; i++ )
      {
	      if ( nameToken[ i ].token  ==  tkn )
	      return  tkn;
      }

    return  SLT_TERMINATE;
  }

/*  Remove trailing NULs and whitespace from a string, return the length.
*/
int  strip( char *string )
  {
    int	c, ch;

    if ( !string )
	return	0;

    c	= strlen( string );

    while ( c  >  0 )			/* Strip trailing nulls, whitespace */
      {
	ch  = string[ c - 1 ];
	if ( !ch  ||  isspace( ch ))
	    string[ --c ]   = 0;	/* NUL fill			    */
	else
	    break;
      }

    return  c;
  }


short CScanServCtrl::GetToken() 
{
    SLPACKET *ipkt   = (SLPACKET *)&rcv;

    return Token;
}


long CScanServCtrl::GetEventStarting() 
{
    return Event.starting;
}


long CScanServCtrl::GetEventEnding() 
{
    return Event.ending;
}


long CScanServCtrl::GetEventTicktime() 
{
    return Event.tickTime;
}


BSTR CScanServCtrl::GetEventUnitID() 
  {
    CString strResult;


    strResult = Event.unitID;
    return strResult.AllocSysString();
  }



short CScanServCtrl::Listen() 
{
    SLPACKET *ipkt   = (SLPACKET *)&rcv;
    int     pos;
    short   i;
    long    sc;
    fd_set  read_mask;
    struct  timeval  wait;

    memset( rcv, 0, sizeof rcv );	                /* Setwardship			    */

    c = 0;
    PacketToken = 0;
    memset( ProductID, 0, sizeof ProductID );	        /* Setwardship			    */
    memset( ProductQuery, 0, sizeof ProductQuery );	/* Setwardship			    */

    if ( sock )			/* Close last comm socket	    */
      {
	      CloseSocket( sock );
	      sock = 0;
      }

    wait.tv_sec	 = 0;
    wait.tv_usec	= 100000;	/* 10 ticks			    */

    sc  = -1;

    FD_ZERO( &read_mask );	/* Wait for data to be available    */
    FD_SET( listenSocket, &read_mask );

    sc	= select( FD_SETSIZE, &read_mask, NULL, NULL, &wait );

    if ( sc == 0 )
	    return 0;

    sock    = accept( listenSocket, NULL, NULL );

    if ( sock  <=  0 )
      {
	      if ( errno  ==  EINTR )	/* EINTR might happen on accept(),  */
	        return 0;		/*  try again			    */
	      else
	        return -1;          /* Fatal Error */
      }

    c   = recv( sock, rcv, sizeof rcv, 0 );

    if ( c  <  0 )
	    return 0;  		/*  try again			    */

    if ( rcv[0] )
	    return SL_TRIVIAL;

    /* else it is nominal */

    PacketToken = packetToken( ipkt );

    Token = 0;
    
    i = 0;

    while( tokenTypes[i].SLT )
    {
      if( PacketToken == tokenTypes[i].SLT )
        Token = tokenTypes[i].SL;

      i++;
    }

    if ( PacketToken ==  SLT_PRODUCTQUERY )
    {
      c   = strlen( ipkt->body.prodquery.product );
      memmove( ProductID, ipkt->body.prodquery.product, c + 1 );

      c   = strip( ProductID );

      if ( c  > 0 )	
      {
        /*  Input format is: UnitID<TAB>BarcodeData.  Find the first character
            after the tab, else use the entire string.
        */

        pos = strcspn( ProductID, "\t" );	
        ProductID[pos] = 0;

        strcpy( ProductQuery, ProductID + pos + 1 );
      }
    }
    else if ( PacketToken ==  SLT_EVENT )
    {
      Event.starting = ntohl( ipkt->body.events.starting );
      Event.ending   = ntohl( ipkt->body.events.ending );
      Event.tickTime = ntohl( ipkt->body.events.tickTime );
      strcpy (Event.unitID, ipkt->body.events.unitID);
    }

    return SL_NOMINAL;
}


BSTR CScanServCtrl::GetTrivialPacket() 
  {
    CString strResult;

    if ( c == 0 || rcv[0] == 0 )
	return NULL;

    else strResult = rcv;

    return strResult.AllocSysString();
  }


void CScanServCtrl::SockSend(LPCTSTR Buffer, long Size) 
  {
    int	ch;

    while ( Size )
      {
	      ch  = send( sock, Buffer, Size, 0 );

	      if ( ch  >=  0 )
	      {
	        Size	-= ch;
	        Buffer	+= ch;
	      }
	        else
	        Size	= 0;
        }
  }

long CScanServCtrl::SendFile(LPCTSTR Name) 
  {
    FILE   *imm;
    size_t  fsize;
    char   *fbuff;
    size_t  xfer    = 0;
    char    LT = '<';
    size_t  hdr;

    if ( !Name )
	return	0;

    imm = fopen( Name, "rb" );

    if ( imm )
      {
	fseek( imm, 0, SEEK_END );
	fsize   = ftell( imm );		/* How big is the file?		    */

	if ( !fsize )
	  {
	    fclose( imm );
	    return  0;
	  }

	fseek( imm, 0, SEEK_SET );


	hdr	= 1 + strlen( Name ) + 1 + 4;

	fbuff   = (char *) malloc( hdr + fsize );
	if ( !fbuff )
	  {
	    fclose( imm );
	    return  0;
	  }

/*  Read in the file.  If it works, send the file name, the four-byte file
    size, then the file.
*/
	xfer	= fread( fbuff + hdr, 1, fsize, imm );
	if ( xfer  ==  fsize )
	  {
	    sprintf( fbuff, "<%s", Name );
	    *(size_t *)(fbuff + hdr - 4)    = htonl( fsize );

	    SockSend( fbuff, fsize + hdr );

	    xfer    = 1;
	  }
	else
	    xfer    = 0;

	fclose( imm );
	free( fbuff );
      }

    return  xfer;
  }


long CScanServCtrl::SendText(LPCTSTR Text) 
  {
    SockSend( Text, strlen( Text ) + 1 );

    return 0;
  }


long CScanServCtrl::SendTextBigEndian(LPCTSTR Text) 
  {
    char   *bufp;
    size_t  len;

    len	    = strlen( (char *)Text );
    bufp    = (char *)malloc( len + 4 );
    if ( !bufp )
	return	-1;

    memset( bufp, 0, len + 3 );
    bufp[ 0 ]	= (char)0xFE;
    bufp[ 1 ]	= (char)0xFF;
    memmove( bufp + 2, Text, len );

    SockSend( bufp, len + 4 );

    free( bufp );
    return 0;
  }

long CScanServCtrl::SendTextLittleEndian(LPCTSTR Text) 
  {
    char   *bufp;
    size_t  len;

    len	    = strlen( (char *)Text );
    bufp    = (char *)malloc( len + 4 );
    if ( !bufp )
	return	-1;

    memset( bufp, 0, len + 4 );
    bufp[ 0 ]	= (char)0xFF;
    bufp[ 1 ]	= (char)0xFE;
    memmove( bufp + 2, Text, len );

    SockSend( bufp, len + 4 );

    free( bufp );
    return 0;
  }



//DEL void CScanServCtrl::SetProtocol(short Type) 
//DEL {
//DEL 	// TODO: Add your dispatch handler code here
//DEL 
//DEL }

void CScanServCtrl::Close() 
  {
    if ( sock )			/* Close last comm socket	    */
      {
	CloseSocket( sock );
	sock = 0;
      }

  }

void CScanServCtrl::Exit() 
  {
    if ( listenSocket )			/* Close the listen Socket    */
      {
	CloseSocket( listenSocket );
	listenSocket = 0;
      }

  }


long CScanServCtrl::GetPacketType() 
  {
    return PacketToken;
  }

BSTR CScanServCtrl::GetProductID() 
  {
    CString strResult;

    strResult = ProductID;
    return strResult.AllocSysString();
  }

BSTR CScanServCtrl::GetProductQuery() 
  {
    CString strResult;

    strResult = ProductQuery;
    return strResult.AllocSysString();
  }


long CScanServCtrl::SendDelayedText(LPCTSTR Text, short Delay) 
  {
    Sleep( Delay );

    SockSend( Text, strlen( Text ) + 1 );

    return 0;
  }
