/*
 * *********************************************************
 * *	                                                   *
 * *     Simulated LLWAS Product Generator and Inserter    *
 * *                                                       *
 * *********************************************************
 * File: utils/examples/llwas_sim.C
 *
 *                 COPYRIGHT (c) 1999, 2002  BY
 *         SIGMET INCORPORATED, WESTFORD MASSACHUSETTS, U.S.A.
 * 
 * THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND  COPIED
 * ONLY  IN  ACCORDANCE WITH  THE  TERMS  OF  SUCH  LICENSE  AND WITH THE
 * INCLUSION OF THE ABOVE COPYRIGHT NOTICE.  THIS SOFTWARE  OR  ANY OTHER
 * COPIES  THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
 * OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF  THE  SOFTWARE  IS  HEREBY
 * TRANSFERED.
 */

#include <ctype.h>
#include <locale.h>
#include <math.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef HPUX
#include <string.h>
#endif
#include <unistd.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/un.h>

#include "sigtypes.h"
#include "signetwork.h"
#include "headers.h"
#include "setup.h"
#include "product.h"

#include "user_lib.h"
#include "config_lib.h"
#include "misc_lib.h"

#define MAX_PACKET_SIZE (90)

/* Function prototypes used just in this file */
static int init_tcpip
  ( int iport_a );

static void RunLoop
( void );

static void MakeInitialProduct
(struct llwas_runway_input pRunway_a[LLWAS_INPUT_MAX],
 const struct corridor_list *pCorList_a );

static void MakeSimulatedHeader
(struct llwas_header_input *pHeader_a,
 UINT4 iResultsCount_a );

static void PromptForUserChanges
(struct llwas_input *pData_a,
 UINT4 *pResultsCount_a );    /* Results count can be changed */

static void ReadString
( const char *sFormat_a,
  UINT4 iCorridor_a,
  const char *sCorridor_a,
  char *sData_a,
  UINT4 iDataSize_a );

static void SendMessage
( const struct llwas_input *pLlwas_a,
  UINT4 iResultsCount_a );

//static void upi_xmt_sigio_handler
//( int signal_a );

/* Shared variables */
static char shost_c[MAXHOSTNAMELEN];
static int socket_c;

/* =====================================================
 * This is an example program showing how to send a LLWAS socket message
 * to the iris integrator.
 */
int main( int iargc_a, char** sargv_a )
{
  int ierror, iarg;
  unsigned int iport;
  char *sptr;

  /* ------------------------------------------------------------------- */

  setlocale( LC_ALL, "" );

  iport   = PORT_LLWAS_SIMULATOR;
  ierror = gethostname( shost_c, MAXHOSTNAMELEN );
  if (ierror < 0)
    {
      perror( "gethostname" );
      exit(EXIT_FAILURE);
    }

  for (iarg=1; iarg<iargc_a; iarg++)
    {
      if ( 0 == strcmp( sargv_a[iarg], "-h" ) )
	{
	  printf( "llwas_sim command line options:\n" );
	  printf( "  -h      : help\n" );
	  printf( "  -n hhhh : specify host name to send to (defaults to localhost)\n" );
	  printf( "  -p pppp : specify port number (default %d)\n", PORT_LLWAS_SIMULATOR );
	  exit(EXIT_SUCCESS);
	}
      else if ( 0 == strcmp( sargv_a[iarg], "-n" ) )
	{
	  iarg++;
	  if (iarg<iargc_a)
	    {
	      /* Process the host name */
	      strncpy( shost_c, sargv_a[iarg], MAXHOSTNAMELEN-1 );
	      shost_c[MAXHOSTNAMELEN-1] = 0;
	    }
	  else
	    {
	      printf( "Missing host name after '-n'\n" );
	      exit(EXIT_FAILURE);
	    }
	}
      else if ( 0 == strcmp( sargv_a[iarg], "-p" ) )
	{
	  iarg++;
	  if (iarg<iargc_a)
	    {
	      /* Process the port number */
	      iport = strtol( sargv_a[iarg], &sptr, 10 );
	      if ((iport<MIN_USER_PORT) || (iport>65535))
		{
		  printf( "Port number %d invalid.\n", iport );
		  exit(EXIT_FAILURE);
		}
	    }
	  else
	    {
	      printf( "Missing port number after '-p'\n" );
	      exit(EXIT_FAILURE);
	    }
	}
      else
	{
	  printf( "Unrecognized command line argument '%s'\n", sargv_a[iarg] );
	  exit(EXIT_FAILURE);
	}
    }

  ierror = init_tcpip( iport );
  if (ierror != 0) exit(EXIT_FAILURE);

  RunLoop( );

  exit(EXIT_SUCCESS) ;
}

/* ===========================================================
 * Initialize the socket connection.  Returns -1 if there was an error, 0 if OK.
 */
static int init_tcpip
  ( int iport_a )
{
  int ierror, iaddrsize;
  struct sockaddr_in servaddr_in;
  struct sockaddr *pservaddr;
  struct hostent *hostptr;
  struct in_addr *paddress;

  /* Convert the server host name to an internet address:
   */
  hostptr = gethostbyname(shost_c);
  if (hostptr != NULL)
    {
      memset( &servaddr_in, 0, sizeof(servaddr_in));
      servaddr_in.sin_family = AF_INET;
      paddress = (struct in_addr *)hostptr->h_addr_list[0];
      servaddr_in.sin_addr = *paddress;
      servaddr_in.sin_port = htons(iport_a);
      
      pservaddr = (struct sockaddr *)(void *)&servaddr_in;
      iaddrsize = sizeof( servaddr_in );
    }
  else
    {
      perror( "gethostbyname" );
      return -1;
    }

  socket_c = socket(AF_INET,SOCK_DGRAM,0);
  if (socket_c == -1)
    {
      perror( "socket" );
      return -1;
    }
  /* We now have a valid opened socket, it must be closed if we exit.
   */

  ierror = connect(socket_c, pservaddr, iaddrsize);
  if (ierror >= 0)
    {
      printf( "Socket opened to port %d\n", iport_a );
      return 0;
    }
  else
    {
      perror( "connect" );
    }

  /* Close the socket, if there was an error before returning */

  ierror = close( socket_c );
  if (ierror == -1) perror( "close" );

  return -1;

}
/* ===========================================
 * Run a loop sending products to the specified machine 
 */
static void RunLoop
( void )
{
  UINT4 iResultsCount;
  struct llwas_input Llwas;

  {
    MESSAGE istatus;
    struct iris_setup ISetup;
    struct corridor_list CorList;
    istatus = irisload(NULL, &ISetup );
    if (istatus!=SS_NORMAL) sig_signal(istatus);
    
    GetCorridorList( &CorList, &ISetup.protect, ALL_AREAS_MASK );
    
    MakeInitialProduct( Llwas.Runway, &CorList );
  }

  iResultsCount = 1;
  while (TRUE)
    {
      PromptForUserChanges( &Llwas, &iResultsCount );
      MakeSimulatedHeader( &Llwas.Header, iResultsCount );

      printf( "Sending LLWAS message to integrator.\n\n" );
      SendMessage( &Llwas, iResultsCount );
    }

}
/* ==============================================================
 * Make the initial fill of data in the LLWAS product.  Just fill in
 * the corridors, and some simulated numbers.
 */
static void MakeInitialProduct
(struct llwas_runway_input pRunway_a[LLWAS_INPUT_MAX],
 const struct corridor_list *pCorList_a )
{
  SINT4 iCount;
  memset( pRunway_a, 0, LLWAS_INPUT_MAX*sizeof(struct llwas_runway_input) );
  for (iCount=0; iCount<LLWAS_INPUT_MAX; iCount++)
    {
      struct llwas_runway_input *pResults;
      const struct one_corridor  *pList;
      SINT4 iListEntry;

      /* Corridor names will match the first from the list, but the 
       * list may be short!
       */
      iListEntry = MIN( iCount, pCorList_a->iCount-1 );
      pResults = &pRunway_a[iCount];
      pList    = &pCorList_a->List[iListEntry];


      spacify_ncpy( pResults->sCorridor, pList->sCorridor, 4 );
      strncpy( pResults->sAlertType, "   ", 3 );
      strncpy( pResults->sLossGain, "150", 3 );
      pResults->sLossGainSign = '-';
      strncpy( pResults->sLocation, "000", 3 );
      strncpy( pResults->sThresholdDirection, "180", 3 );
      strncpy( pResults->sThresholdSpeed, "20", 2 );
    }

}
/* ===============================================================
 * Make a simulated product header for the TDWR product.
 */
static void MakeSimulatedHeader
(struct llwas_header_input *pHeader_a,
 UINT4 iResultsCount_a )
{
  struct ymds_time Time;
  FLT4 fSpeed, fDirection, fSimTime;
  SINT4 iSpeed, iDirection, iGustSpeed;
  char sTemp[40];
  /* -----------------------------------------------------------------------
   */
  GetTimeYmds( &Time, TIME_FLG_GMT );
  /* Zero it first
   */
  memset( pHeader_a, 0, sizeof(*pHeader_a) );

  fSimTime = ((FLT4)(Time.isec % 3600))/3600.0;
  fDirection = 36.0 * fSimTime;
  fSpeed     = 5 + 50.0 * fSimTime;
  iDirection = 10 * NINT(fDirection);
  iSpeed = NINT(fSpeed);
  iGustSpeed = NINT(1.5*fSpeed);

  strncpy( pHeader_a->sSourceID, "IIA ", 4 );
  pHeader_a->iMessageID = htons( 0x1200 );
  pHeader_a->sSystemID = '1';
  sprintf( sTemp, "%3.3d", iDirection );
  strncpy( pHeader_a->sCFDirection, sTemp, 3 );
  pHeader_a->iMessageLength = htons( LLWAS_HEADER_INPUT_SIZE + 
				     (iResultsCount_a*LLWAS_RUNWAY_INPUT_SIZE) );
  pHeader_a->sLlwasStatus = '3';    
  sprintf( sTemp, "%2.2d", iSpeed );
  strncpy( pHeader_a->sCFSpeed, sTemp, 2 );
  sprintf( sTemp, "%2.2d", iGustSpeed );
  strncpy( pHeader_a->sCFGustSpeed, sTemp, 2 );
  
}
/* ===============================================================
 * Prompt the user to override values from the simulator.
 */
static void PromptForUserChanges
(struct llwas_input *pData_a,
 UINT4 *pResultsCount_a )    /* Results count can be changed */
{
  char *sfile, *sptr, sline[80];
  UINT4 iCorridor;
  SINT4 iLength;

  printf( "Enter Corridor Count [%d]: ", *pResultsCount_a );
  sfile = fgets( sline, 80, stdin );
  if (NULL != sfile)
    {
      /* Trim trailing carriage return */
      iLength = strlen( sfile )-1;
      if (iLength>0)
	{
	  SINT4 iCount;
	  sfile[iLength] = 0;
	  iCount = strtol( sfile, &sptr, 10 );
	  if ((iCount>=0)&&(iCount<=LLWAS_INPUT_MAX)) *pResultsCount_a = iCount;
	}
    }

  for (iCorridor=0; iCorridor<*pResultsCount_a; iCorridor++)
    {
      struct llwas_runway_input *pResults;
      pResults = &pData_a->Runway[iCorridor];
      ReadString( "Enter Alert type for Corridor %d (%4.4s)[%3.3s]: ",
	      iCorridor, pResults->sCorridor, pResults->sAlertType, 3 );
      if (0 != strncmp( pResults->sAlertType, "   ", 3 ) )
	{
	  ReadString( "Enter Alert location for Corridor %d (%4.4s)[%3.3s]: ",
		      iCorridor, pResults->sCorridor, pResults->sLocation, 3 );
	  ReadString( "Enter Alert magnitude for Corridor %d (%4.4s)[%3.3s]: ",
		      iCorridor, pResults->sCorridor, pResults->sLossGain, 3 );
	  ReadString( "Enter Alert magnitude for Corridor %d (%4.4s)[%1.1s]: ",
		      iCorridor, pResults->sCorridor, &pResults->sLossGainSign, 1 );
	}
    }
}

/* ===================================================
 * Prompt and read a string for the simulated data
 */
static void ReadString
( const char *sFormat_a,
  UINT4 iCorridor_a,
  const char *sCorridor_a,
  char *sData_a,
  UINT4 iDataSize_a )
{
  SINT4 iLength; UINT4 iIndex;
  char sline[80], *sfile;
  char sString[40];
  /* Copy out the first three characters of the typed string,
   * replacing all non-printable characters with space.  We can
   * overrun sfile, because sline is longer than 3.
   */

  printf( sFormat_a, iCorridor_a, sCorridor_a, sData_a );
  sfile = fgets( sline, 80, stdin );

  /* Return on error */
  if (NULL == sfile) return;

  /* Trim trailing carriage return */
  iLength = strlen( sfile )-1;

  /* Return if nothing else typed */
  if (iLength<=0) return;

  memcpy( sString, sfile, iDataSize_a );
  for (iIndex=0; iIndex<iDataSize_a; iIndex++)
    {
      if ( !isprint(sString[iIndex]) ) sString[iIndex] = ' ';
    }
  strncpy( sData_a, sString, iDataSize_a );

  return;
}
/* =======================================================
 * Sends the LLWAS socket message to the integrator.
 */
static void SendMessage
( const struct llwas_input *pLlwas_a,
  UINT4 iResultsCount_a )
{
  struct llwas_input Llwas;
  UINT4 iCorridor, iLength;
  int ierror;

  /* Copy and convert to final output.  This just means blanking alert
   * information if the alert is off. */
  Llwas = *pLlwas_a;
  for (iCorridor=0; iCorridor<iResultsCount_a; iCorridor++)
    {
      struct llwas_runway_input *pResults;
      pResults = &Llwas.Runway[iCorridor];
      if (0 == strncmp( pResults->sAlertType, "   ", 3 ) )
	{
	  strncpy( pResults->sLossGain, "    " , 4 );
	  strncpy( pResults->sLocation, "   " , 3 );
	}
      strncpy( pResults->sThresholdDirection, pLlwas_a->Header.sCFDirection, 3 );
      strncpy( pResults->sThresholdSpeed,     pLlwas_a->Header.sCFSpeed, 2 );
    }

  iLength = LLWAS_HEADER_INPUT_SIZE + (iResultsCount_a*LLWAS_RUNWAY_INPUT_SIZE);
  ierror = write( socket_c, &Llwas, iLength );
  if (ierror == -1) perror( "write" );
}

/* ================================================================
 * Signal handler for SIGIO signal.  It just does a read, and processes
 * the result.
 */
#if 0
static void upi_xmt_sigio_handler( int signal_a )
{
  char data[100];
  int isize;

  isize = recv( socket_c, data, 99, 0 );
  if (isize > 0)
    {
      /* We got some valid data here.  Add terminating NULL, then process */
      data[isize] = 0;
      if (0 == strcmp( data, "NOP" ))
	{

	}
      else if (0 == strcmp( data, "EXIT" ))
	{
	  printf( "Got 'EXIT' message.\n" );
	  exit(EXIT_SUCCESS);
	}
      else
	{
	  printf( "recv read unknown message: '%s'\n", data );
	}


    }

  else if (isize < 0)
    {
      /* We an error here, signal all errors except EWOULDBLOCK */
      if (errno != EWOULDBLOCK)
	{
	  perror( "recv" );
	  exit(EXIT_FAILURE);
	}
    }

  else
    {
      /* Zero size indicates that the socket has been closed at the other end */
      printf( "Link broken\n" );
      exit(EXIT_SUCCESS);
    }
  return;

}
#endif  /* #ifdef 0 */
