#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

#include "/usr/local/freeway/include/dliicp.h"
#include "/usr/local/freeway/include/dliprot.h"
 
/* LOCAL_UDP_PORT and FOREIGN_UDP_PORT must be defined at compile time */
#define FOREIGN_ADDR     "192.168.1.255"

/* These structures show the format of the messages sent/received from ipapi */
typedef struct          _DLI_ICP_HDR { 
   unsigned short      usClientID;         /* client's id (su_id)            */
   unsigned short      usServerID;         /* server's id (sp_id)            */
   unsigned short      usDataBytes;        /* databytes following (count)    */
   unsigned short      usCommand;          /* command                        */
   short               iStatus;            /* status                         */
   unsigned short      usParms[3];         /* 3 extra parameters             */
} DLI_ICP_HDR;
  
typedef struct          _DLI_PROT_HDR
{
    unsigned short      usCommand;         /* icp command                    */
             short      iModifier;         /* icp modifier/subcommand        */
    unsigned short      usLinkID;          /* link ID                        */
    unsigned short      usCircuitID;       /* circuit ID                     */
    unsigned short      usSessionID;       /* session ID                     */
    unsigned short      usSequence;        /* sequence                       */
    unsigned short      usReserved[2];     /* reserved ***2***               */
}                       DLI_PROT_HDR;

typedef struct {
   DLI_ICP_HDR         dli_icp_hdr;
   DLI_PROT_HDR        dli_prot_hdr;
   unsigned char       data [1024];
} MSG_BUFFER;

MSG_BUFFER test_message =
{
   { 0, 0, 0, 0, 0, { 0, 0, 0 } },  { 0, 0, 0, 0, 0, 0, { 0, 0 } },
   "abcdefghijklmnopqrst abcdefghijklmnopqrst abcdefghijklmnopqrst"
};

MSG_BUFFER rcv_message;

#define ATTEMPT( x )   if ( (x) < 0 )                                 \
                       {                                              \
                          perror( "\nERROR: apisim_aws.c " );         \
                          printf( "Died at line %d, errno= %d.\n",    \
                                  __LINE__, errno );                  \
                          exit( 1 );                                  \
                       }
           
int main()
{
   int       iTotSize;
   short int usDataBytes;
   int       iTrueFlag;

   struct sockaddr_in l_addr, l_addr2;
   struct sockaddr_in f_addr, f_addr2;

   int sock  = socket( AF_INET, SOCK_DGRAM, 0);
   int sock2 = socket( AF_INET, SOCK_DGRAM, 0);

   bzero( (char *)&l_addr,  sizeof( struct sockaddr_in ) );
   bzero( (char *)&l_addr2, sizeof( struct sockaddr_in ) );
   bzero( (char *)&f_addr,  sizeof( struct sockaddr_in ) );
   bzero( (char *)&f_addr2, sizeof( struct sockaddr_in ) );

   l_addr.sin_family = l_addr2.sin_family = AF_INET;
   f_addr.sin_family = f_addr2.sin_family = AF_INET;
   l_addr.sin_port                        = htons( LOCAL_UDP_PORT       );
   l_addr2.sin_port                       = htons( LOCAL_UDP_PORT + 1   );
   f_addr.sin_port                        = htons( FOREIGN_UDP_PORT     );
   f_addr2.sin_port                       = htons( FOREIGN_UDP_PORT + 1 );
   f_addr.sin_addr.s_addr = f_addr2.sin_addr.s_addr = inet_addr(FOREIGN_ADDR);

   ATTEMPT( bind( sock,  (struct sockaddr *)&l_addr,  sizeof( l_addr  ) ) )
   ATTEMPT( bind( sock2, (struct sockaddr *)&l_addr2, sizeof( l_addr2 ) ) )

   iTrueFlag = 1;
   ATTEMPT( setsockopt( sock,  SOL_SOCKET, SO_BROADCAST,
                        (char *)&iTrueFlag, sizeof(iTrueFlag)) )
   iTrueFlag = 1;
   ATTEMPT( setsockopt( sock2, SOL_SOCKET, SO_BROADCAST,
                        (char *)&iTrueFlag, sizeof(iTrueFlag)) )

   test_message.dli_icp_hdr.usCommand = htons( DLI_ICP_CMD_WRITE );

   usDataBytes = strlen( test_message.data );
   test_message.dli_icp_hdr.usDataBytes =
                                   htons( usDataBytes + sizeof(DLI_PROT_HDR) );
   iTotSize = usDataBytes + sizeof( DLI_ICP_HDR ) + sizeof(DLI_PROT_HDR);

   test_message.dli_prot_hdr.usCommand     = htons( DLI_PROT_SEND_NORM_DATA );
   test_message.dli_prot_hdr.iModifier     = htons(  0 );
   test_message.dli_prot_hdr.usLinkID      = htons(  0 );
   test_message.dli_prot_hdr.usCircuitID   = htons(  0 );
   test_message.dli_prot_hdr.usSessionID   = htons(  2 );
   test_message.dli_prot_hdr.usReserved[0] = htons(  1 );
   test_message.dli_prot_hdr.usReserved[1] = htons( usDataBytes );

   bzero( &rcv_message, sizeof( rcv_message ) );

   printf( "apisim: send %d bytes, %d byte string: %c%c%c%c, to port 0x%x.\n",
           iTotSize, usDataBytes,
           test_message.data[0], test_message.data[1],
           test_message.data[2], test_message.data[3],
           FOREIGN_UDP_PORT );

   ATTEMPT( sendto( sock, &test_message, iTotSize, 0,
                    (struct sockaddr *)&f_addr, sizeof(f_addr) ) );

   ATTEMPT( iTotSize = read( sock2, &rcv_message, sizeof( rcv_message ) ) );

   printf( "apisim: received %d bytes; %d byte string: %c%c%c%c.\n",
           iTotSize,
           iTotSize - sizeof( DLI_ICP_HDR ) - sizeof( DLI_PROT_HDR ),
           rcv_message.data[0], rcv_message.data[1],
           rcv_message.data[2], rcv_message.data[3] );

   close( sock  );
   close( sock2 );
   exit( 0 );
}