/* **********************************************************************
 * *	                                                                *
 * *               Real Time Display V2 Transmitter                     *
 * *                                                                    *
 * **********************************************************************
 * File: libs/rtq/rtd_v2_xmt.C
 *
 *                 COPYRIGHT (c) 2003, 2004, 2005 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 <errno.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>  /* place after netinet/in.h on some platforma */

#include "sigtypes.h"
#include "setup.h"
#include "sig_rtdisp.h"

#include "user_lib.h"
#include "rtq_lib.h"

#include "RtdSupport_h.h"

static FILE *iLogFile_c;
static volatile struct rtdisp_control *pRtqGlobal_c;
static SINT4 iLastSequenceNum_c;

static void rtd_exit_handler(void);

static SINT4 iread_disq
( UINT1 **pBuffer_a,
  int iChannel_a );

static SINT4 iReadRtdV2
( union rtd_v2_packet **pBuffer_a );

static void HeaderFromElementV2
( struct rtd_v2_state      *pState_a,            /* Fill in internal state here */
  const struct rtq_volume_header *pRtqHeader_a );

static UINT1 lVHeaderChanged
( const struct rtq_volume_header *pLastRtqHeader_a,  /* Header last converted */
  const struct rtq_volume_header *pThisRtqHeader_a );     /* Header of this ray */

static UINT2 RayFromElementV2
( struct rtd_v2_ray_header  *pRay_a,
  struct rtd_v2_state *pState_a,
  const struct RtqQueueElement *pElement_a );

static void RayHeaderFromElementV2
(struct rtd_v2_ray_header *prthead_a,
 const struct rtd_v2_state *pState_a,
 const struct rtq_ray_header *phead_a,   /* Input ray header */
 UINT2 *pisize_a );                  /* Increment for size used */

static void HeaderHostToNet
( struct rtd_v2_vol_header *pNetVHeader_a,
  const struct rtd_v2_vol_header *pHostVHeader_a );

int main( int iArgc_a, char *sArgv_a[] )
{
  int ierror, iChannel;
  MESSAGE istatus ;

  setlocale( LC_ALL, "" ) ;
  iLogFile_c = NULL;  /* Skip logging until we open the file */

  iChannel = iGetChannel( iArgc_a, sArgv_a );
  
  /* Open the logging file, if any */
  {
    char sLogPath[PATHNAME_SIZE]; char *sptr;
    char sDirectory[DIRECTORY_SIZE];
    sptr = getenv( "IRIS_LOG" );
    if (!sptr)
      {
	fprintf( stderr, "Cannot translate IRIS_LOG\n" );
	exit(EXIT_FAILURE);
      }
    strncpy( sDirectory, sptr, DIRECTORY_SIZE-1 );
    sDirectory[PATHNAME_SIZE-1] = 0;

    /* Origin 1 log file name */
    sprintf( sLogPath, "%srtd_v2_xmt%d.log", sDirectory, iChannel+1 );
    iLogFile_c = fopen( sLogPath, "w" );
    if (NULL == iLogFile_c)
      {
	fprintf( stderr, "Can not open log file: '%s'\n", sLogPath );
	exit(EXIT_FAILURE);
      }
  }

  istatus = RtqBufferAttach( &pRtqGlobal_c, RTQ_CLIENT_READER );
  if( istatus != SS_NORMAL )
    {
      sig_signal( istatus );
      exit(EXIT_FAILURE);
    }
  ierror = atexit( rtd_exit_handler );
  if (ierror == -1) sig_signal( errno_to_message_va
				( errno, 1, "main(atexit())" ) ) ;

  if (iArgc_a>1)
    sig_establish( RtdSignalHandler, sArgv_a[1] );
  else
    sig_establish( RtdSignalHandler, "IRIS_V2_XMT" );

  /* Set the priority. */
  istatus = sig_setpriority( pRtqGlobal_c->RSetup.iPriority );
  if ( istatus != SS_NORMAL) sig_signal( istatus );

  /* This will return when the sender exits */
  iLastSequenceNum_c = RtqGetCurrentSequence( pRtqGlobal_c );
  istatus = RtdXmtOn( pRtqGlobal_c, iread_disq, iChannel );
  exit(EXIT_SUCCESS);
}

/* ======================================================================
 * This is the exit handler used by the RTD_XMT processes.
 */
static void rtd_exit_handler(void)
{
  MESSAGE istatus;
  
  istatus = RtqBufferRelease(pRtqGlobal_c);
  if (istatus!=SS_NORMAL) sig_signal(istatus);

}


/* ============================================================
 * Read the next ray from the display buffer.  Returns both the
 * size of write in bytes, and the buffer pointer.
 */
static SINT4 iread_disq
( UINT1 **pBuffer_a,
  int iChannel_a )
{
  SINT4 iSize;

  iSize = iReadRtdV2( (union rtd_v2_packet**)pBuffer_a );

  return iSize;
}

struct rtd_v2_state
{
  struct rtd_v2_vol_header VHeader;  /* Saved in host byte order */
  rtq_volume_header LastInVHeader;  /* Input volume header we last converted */
  SINT4 iDataIndex;
  SINT4 iRangeIndex;
  SINT4 iBinsToSend;   /* Number of bins to send in this ray */
  UINT1 iInDataRes;
  UINT1 iRaysSinceHeader;
  UINT1 lRayReadyToSend;
  UINT1 lSendHeader;
};

/* ============================================================
 * Read the next ray from the display buffer.  Handles SIGMET RTD_V2 format.
 */
static SINT4 iReadRtdV2
( union rtd_v2_packet **pBuffer_a )
{
  UINT1 lValid;
  
  static struct RtqQueueElement SavedRtqElement; /* Last RTQ element */

  static struct rtd_v2_state  SavedState;

  /* Buffers used to return the three possible results */
  static union  rtd_v2_packet SavedPacket;     /* stored in network byte order */
  static SINT4 iRaySize=0;
  static struct rtd_v2_vol_header NetVHeader;  /* stored in network byte order */
  static UINT2 iCode;

  static UINT1 lFirstCall = TRUE;

  if (lFirstCall)
    {
      memset( &SavedRtqElement, 0, sizeof(SavedRtqElement) );
      memset( &SavedState,      0, sizeof(SavedState) );
      memset( &SavedPacket,     0, sizeof(SavedPacket) );
      memset( &NetVHeader,      0, sizeof(NetVHeader) );
    }


  /* Periodically, send a new header to deal with possibly missed data.
   */
  if (SavedState.iRaysSinceHeader > 25)
    {
      SavedState.iRaysSinceHeader=0;
      *pBuffer_a = (union rtd_v2_packet*)(void*)&NetVHeader;
      return sizeof( struct rtd_v2_vol_header );
    }

  /* Send the previous ray if it is ready to go */
  if (SavedState.lRayReadyToSend)
    {
      iRaySize = RayFromElementV2( &(SavedPacket.RHeader), &SavedState, &SavedRtqElement );
      *pBuffer_a = &SavedPacket;
      return iRaySize;
    }

  /* Get new info from the RTQ */
  lValid = RtqGetRay( pRtqGlobal_c, &SavedRtqElement, iLastSequenceNum_c );

  /* Return zero size if nothing available */
  if (!lValid)
    {
      *pBuffer_a = NULL;
      return 0;
    }

  /* We have a valid element from the RTQ, latch the new sequence number */
  iLastSequenceNum_c = SavedRtqElement.iSequenceNum;

  /* If volume end flag, send it through */
  if (SavedRtqElement.VHeader.iFlags & RTQ_FLAG_VOLUME_END)
    {
      iCode = RTD_PACKET_TYPE_END | 2;
      iCode = htons( iCode );
      *pBuffer_a = (union rtd_v2_packet*)&iCode;
      return 2;
    }

  /* Ray is now ready to send! */
  SavedState.lRayReadyToSend=TRUE;

  /* Send the header if this is the first call, it is the start of
   * a volume scan, or if it has change significantly. */
  if (lFirstCall || (SavedRtqElement.VHeader.iFlags & RTQ_FLAG_VOLUME_START) ||
      lVHeaderChanged(&SavedState.LastInVHeader, &(SavedRtqElement.VHeader) ) )
    {
      lFirstCall = FALSE;
      /* Convert the header */
      HeaderFromElementV2( &SavedState, &(SavedRtqElement.VHeader) );
      SavedState.lSendHeader = TRUE;
    }

  /* Force the header if the time has hopped unreasonably */
  {
    SINT4 iTimeOffset =
      iymds_subtract( &SavedRtqElement.RHeader.Ymds, &SavedState.VHeader.ing_time );

    /* If the time offset overflows reason, then zero it and force a new
     * header transmission.
     */
    if ((iTimeOffset<0)||(iTimeOffset>0x0000ffff))
      {
	SavedState.VHeader.ing_time = SavedRtqElement.RHeader.Ymds;
	SavedState.lSendHeader = TRUE;
      }
  }

  /* Send the header if requested above */
  if (SavedState.lSendHeader)
    {
      lFirstCall = FALSE;
      /* Convert the header byte order */
      HeaderHostToNet( &NetVHeader, &(SavedState.VHeader) );
      
      SavedState.iRaysSinceHeader=0;
      *pBuffer_a = (union rtd_v2_packet*)(void*)&NetVHeader;
      return sizeof( struct rtd_v2_vol_header );
    }
  
  /* Convert the ray */
  iRaySize = RayFromElementV2( &(SavedPacket.RHeader), &SavedState, &SavedRtqElement );

  /* Finally send a normal ray */
  *pBuffer_a = &SavedPacket;
  return iRaySize;
  
}

/* ===============================================
 * Make a new real time display header.  Also determine if we will make a display,
 * and if so, set static variables to define it.
 */
static void HeaderFromElementV2
( struct rtd_v2_state      *pState_a,            /* Fill in internal state here */
  const struct rtq_volume_header *pRtqHeader_a )
{
  SINT4 iMaxBytesPerPacket, iMoment;
  
  memset( pState_a, 0, sizeof(*pState_a) );

  /* Save the last input header */
  pState_a->LastInVHeader = *pRtqHeader_a;

  pState_a->iInDataRes = idata_resolution( pRtqHeader_a->iDTypes[0]);

  /* Fill in the rest of the structure
   */
  pState_a->VHeader.iCode      = RTD_PACKET_TYPE_HEADER | sizeof(struct rtd_v2_vol_header);
  pState_a->VHeader.ipolar        = pRtqHeader_a->ipolar ;
  strncpy( pState_a->VHeader.sSiteName16, pRtqHeader_a->sSiteName16, 16 );
  strncpy( pState_a->VHeader.sSiteName3, pRtqHeader_a->sSiteName3, 4 );
  pState_a->VHeader.sSiteName3[3] = 0;
  pState_a->VHeader.ilat          = pRtqHeader_a->ilat;
  pState_a->VHeader.ilon          = pRtqHeader_a->ilon;
  pState_a->VHeader.ing_time      = pRtqHeader_a->ing_time;
  pState_a->VHeader.ibin_first    = pRtqHeader_a->ibin_first;
  pState_a->VHeader.ibin_step     = pRtqHeader_a->ibin_step;
  pState_a->VHeader.iNominalBinCount = pRtqHeader_a->iNominalBinCount;

  /* Calculate the total number of range bins to process.  First
   * network hardware limit (subtract the ray header).
   */
  iMaxBytesPerPacket = UDP_DATA_SIZE-sizeof(struct rtd_v2_ray_header);
  if (pState_a->iInDataRes==16)
    pState_a->VHeader.iBinsPerPacket = MIN( (SINT4)pRtqHeader_a->iNominalBinCount, iMaxBytesPerPacket/2 );
  else
    pState_a->VHeader.iBinsPerPacket = MIN( (SINT4)pRtqHeader_a->iNominalBinCount, iMaxBytesPerPacket );

  pState_a->VHeader.iMomentCount  = pRtqHeader_a->iMomentCount;
  for (iMoment=0; iMoment<RTQ_MAX_MOMENTS; iMoment++)
    pState_a->VHeader.idata[iMoment] = pRtqHeader_a->iDTypes[iMoment];
  pState_a->VHeader.iprf          = pRtqHeader_a->iprf;
  pState_a->VHeader.itrig         = pRtqHeader_a->itrig;
  pState_a->VHeader.ilambda       = pRtqHeader_a->ilambda;
  strncpy( pState_a->VHeader.stname, pRtqHeader_a->stname, 16 );
  pState_a->VHeader.iId           = 0;

}
/* ===============================================
 * Test to see if a significant change to the volume header changed.
 * This can be used to force a new header to be sent.
 */
static UINT1 lVHeaderChanged
( const struct rtq_volume_header *pLastRtqHeader_a,  /* Header last converted */
  const struct rtq_volume_header *pThisRtqHeader_a )     /* Header of this ray */
{
  struct rtq_volume_header LastHeader, ThisHeader;
  
  /* We are going to do a full memcmp of the two structures, 
   * leaving out fields which we know are not important for a
   * change in the middle of the volume scan.
   */

  LastHeader = *pLastRtqHeader_a;
  ThisHeader = *pThisRtqHeader_a;

  LastHeader.iFlags = 0;          ThisHeader.iFlags = 0;  /* Unused */
  /* Only notice motion between volumes*/
  LastHeader.ilat   = 0;          ThisHeader.ilat   = 0;  
  LastHeader.ilon   = 0;          ThisHeader.ilon   = 0;
  LastHeader.iAntStatus = 0;      ThisHeader.iAntStatus   = 0;  /* Unused */
  LastHeader.iAntStatusMask = 0;  ThisHeader.iAntStatusMask = 0; /* Unused */
  LastHeader.ipad_69x1 = 0;       ThisHeader.ipad_69x1 = 0;   /* Unused */

  return (0 != memcmp( &LastHeader, &ThisHeader, sizeof(LastHeader) ));

}

/* ==============================================================
 * Function to convert part of a ray from RTQ format to a RTD V2 packet.  Returns
 * the number of bytes in the packet.  Also updates internal state to
 * track future packet sends.
 */
static UINT2 RayFromElementV2
( struct rtd_v2_ray_header  *pRay_a,
  struct rtd_v2_state *pState_a,
  const struct RtqQueueElement *pElement_a )
{
  UINT2 iSize;
  SINT4 iBinsSent, iBinsRemaining;
  const UINT1 *pInRay;

  iSize = 0;
  pInRay         = pElement_a->iData[pState_a->iDataIndex];
  iBinsSent      = pState_a->iRangeIndex*pState_a->VHeader.iBinsPerPacket;
  iBinsRemaining = pElement_a->RHeader.iBinCount - iBinsSent;
  pState_a->iBinsToSend = MIN(iBinsRemaining, pState_a->VHeader.iBinsPerPacket);

  if (16==pState_a->iInDataRes)
    {
      iSize = 2*pState_a->iBinsToSend;
      if (0x1234 != htons(0x1234))
	Swap2cpy( (SINT2*)pRay_a->iData, (SINT2*)&(pInRay[2*iBinsSent]),
		  pState_a->iBinsToSend );
      else
	memmove( pRay_a->iData, &(pInRay[2*iBinsSent]), iSize );
    }
  else
    {
      iSize = pState_a->iBinsToSend;
      memmove( pRay_a->iData, &(pInRay[iBinsSent]), iSize );
    }
  RayHeaderFromElementV2( pRay_a, pState_a, &(pElement_a->RHeader), &iSize );

  /* Increment data position state */
  pState_a->iRangeIndex++;
  if ((pState_a->iRangeIndex*pState_a->VHeader.iBinsPerPacket) >= 
      (SINT4)pElement_a->RHeader.iBinCount)
    {
      pState_a->iDataIndex++;
      pState_a->iRangeIndex=0;
      if (pState_a->iDataIndex >= pState_a->VHeader.iMomentCount)
	{
	  pState_a->iDataIndex=0;
	  pState_a->iRaysSinceHeader++;
	  pState_a->lRayReadyToSend=FALSE;
	}
    }

  return iSize;
}

/* ====================================================
 * Convert just the ray header
 */
static void RayHeaderFromElementV2
(struct rtd_v2_ray_header *pRtdHead_a,
 const struct rtd_v2_state *pState_a,
 const struct rtq_ray_header *pRtqHead_a,   /* Input ray header */
 UINT2 *pisize_a )                   /* Increment for size used */
{
  SINT4 iTimeOffset = 
    iymds_subtract( &pRtqHead_a->Ymds, &pState_a->VHeader.ing_time );

#if 0
  printf( "Az: %5.2f, El: %5.2f, bins: %d, time: %d\n",
	  fPDegFromBin2(pray->iaz_start), fElDegFromBin2(pray->iel_start),
	  pray->ibincount, iTimeOffset );
#endif

  /* Copying everything into network order */
  pRtdHead_a->iSequenceNum  = htons((UINT2)(0xFFFF&iLastSequenceNum_c));
  memcpy( pRtdHead_a->sSiteName3, pState_a->VHeader.sSiteName3, 4 );
  pRtdHead_a->azimuth_start = htons(pRtqHead_a->azimuth_start);
  pRtdHead_a->azimuth       = htons(pRtqHead_a->azimuth);
  pRtdHead_a->elevation     = htons(pRtqHead_a->elevation);
  pRtdHead_a->itime         = htons((UINT2)iTimeOffset);
  pRtdHead_a->iDataIndex    = pState_a->iDataIndex;
  pRtdHead_a->iRangeIndex   = pState_a->iRangeIndex;
  pRtdHead_a->iId           = 0;
  pRtdHead_a->iBinCount     = htons( pState_a->iBinsToSend );
  pRtdHead_a->iDataSize     = htons( *pisize_a );
  *pisize_a                += sizeof( struct rtd_v2_ray_header );
  pRtdHead_a->iCode         = htons( RTD_PACKET_TYPE_RAY|*pisize_a );

}

/* ==============================================================
 * Convert a RTD V2 Volume header from host byte order to net byte order.
 */
static void HeaderHostToNet
( struct rtd_v2_vol_header *pNetVHeader_a,
  const struct rtd_v2_vol_header *pHostVHeader_a )
{
  int iMoment;
  pNetVHeader_a->iCode          = htons( pHostVHeader_a->iCode );
  pNetVHeader_a->ipolar         = htons( pHostVHeader_a->ipolar );
  memcpy( pNetVHeader_a->sSiteName16, (char*)pHostVHeader_a->sSiteName16, 20 );
  memcpy( pNetVHeader_a->sSiteName3, (char*)pHostVHeader_a->sSiteName3, 4 );
  pNetVHeader_a->ilat           = htonl( pHostVHeader_a->ilat );
  pNetVHeader_a->ilon           = htonl( pHostVHeader_a->ilon );
  pNetVHeader_a->ing_time.isec  = htonl( pHostVHeader_a->ing_time.isec );
  pNetVHeader_a->ing_time.imills= htons( pHostVHeader_a->ing_time.imills );
  pNetVHeader_a->ing_time.iyear = htons( pHostVHeader_a->ing_time.iyear );
  pNetVHeader_a->ing_time.imon  = htons( pHostVHeader_a->ing_time.imon );
  pNetVHeader_a->ing_time.iday  = htons( pHostVHeader_a->ing_time.iday );
  pNetVHeader_a->ibin_first     = htonl( pHostVHeader_a->ibin_first );
  pNetVHeader_a->ibin_step      = htonl( pHostVHeader_a->ibin_step );
  pNetVHeader_a->iNominalBinCount = htonl( pHostVHeader_a->iNominalBinCount );
  pNetVHeader_a->iBinsPerPacket = htons( pHostVHeader_a->iBinsPerPacket);
  pNetVHeader_a->iMomentCount   = htons( pHostVHeader_a->iMomentCount );
  for (iMoment=0; iMoment<RTQ_MAX_MOMENTS; iMoment++ )
    pNetVHeader_a->idata[iMoment] = htons( pHostVHeader_a->idata[iMoment] );
  pNetVHeader_a->iprf           = htonl( pHostVHeader_a->iprf );
  pNetVHeader_a->itrig          = htonl( pHostVHeader_a->itrig );
  pNetVHeader_a->ilambda        = htonl( pHostVHeader_a->ilambda );
  memcpy( pNetVHeader_a->stname, (char*)pHostVHeader_a->stname, 16 );
  pNetVHeader_a->iId            = pHostVHeader_a->iId;

}

/* ===========================================
 * Print string to log and terminal, if desired.
 */
void Lprintf
( const char *sString_a )
{
  struct ymds_time Ymds;
  char sDateBuf[TIMENAME_SIZE], sTimeBuf[TIMENAME_SIZE] ;

  if (iLogFile_c)
    {

      GetTimeYmds( &Ymds, TIME_FLG_LOCAL );
      
      sddmonyyyy_r( Ymds.iyear, Ymds.imon, Ymds.iday, sDateBuf );
      shhmmss_r( Ymds.isec, sTimeBuf );

      fprintf( iLogFile_c, "%s %s.%03d: ", sDateBuf, sTimeBuf, MS_FROM_MILLS(Ymds.imills) );
      fputs( sString_a, iLogFile_c );
      fflush( iLogFile_c );
    }

  return;
}
