/* 
This sample encodes a message and demonstrates how a user-defined callback
can be used to intercept output that would otherwise go to stdout.  To
achieve this functionality the objects must be generated with the
-prttostrm qualfiier to the asn1c command.  

In this particular sample the message is a CSTA answer call request
message, but the callback mechanisms work for any object generated
with the -prttostrm qualifier.
*/

#include <stdio.h>
#include <stdlib.h>

#include "CSTA-ROSE-PDU-types.h"
#include "CSTA-answer-call.h"

#define MAXMSGLEN 1024

static int encodeAnswerCallRequest (ASN1BEREncodeBuffer& encodeBuffer, 
                                    ASN1T_AnswerCallArgument& answerCallArg, 
                                    ASN1C_AnswerCallArgument& answerCallArgC,
                                    ASN1T_DeviceID& staticID,
                                    ASN1T_ConnectionID_both& connectionID);

static int encodeROSERequestHeader (ASN1BEREncodeBuffer& encodeBuffer, 
                                    ASN1T_CSTA_ROSE_PDU& pdu,
                                    ASN1C_CSTA_ROSE_PDU& pduC,
                                    ASN1T_CSTA_ROSE_PDU_invoke& invoke,
                                    int msglen);

/* User callback function to intercept output.  Note that the first
   argument can be anything (see the callback registration below).  */

void writeToFile(void* pPrntStrmInfo, const char* fmtspec, 
                 va_list arglist);

int main (int argc, char** argv)
{

   /* Run-time support variables */

   const OSOCTET* msgptr;
   OSOCTET msgbuf[MAXMSGLEN];
   int i, len;
   FILE *fp, *fpLog;
   const char* filename = "message.dat";
   OSBOOL trace = TRUE;

   /* Process command line arguments */

   if (argc > 1) {
      for (i = 1; i < argc; i++) {
         if (!strcmp (argv[i], "-v")) trace = TRUE;
         else if (!strcmp (argv[i], "-o")) filename = argv[++i];
         else if (!strcmp (argv[i], "-notrace")) trace = FALSE;
         else {
            printf ("usage: writer [ -v ] [ -o <filename>\n");
            printf ("   -v  verbose mode: print trace info\n");
            printf ("   -o <filename>  write encoded msg to <filename>\n");
            printf ("   -notrace  do not display trace info\n");
            return 0;
         }
      }
   }

   /* Initialize the encoding context */

   ASN1BEREncodeBuffer encodeBuffer (msgbuf, sizeof(msgbuf));

   /* Create the objects for the CSTA operation argument */

   ASN1T_AnswerCallArgument answerCallArg;
   ASN1C_AnswerCallArgument answerCallArgC(encodeBuffer, answerCallArg);
   ASN1T_DeviceID staticID;
   ASN1T_ConnectionID_both connectionID;
   ASN1T_CSTA_ROSE_PDU_invoke invoke;

   /* Set up logging */

   fpLog = fopen("writer.log", "w");
   if(fpLog)
   {
      /* What we're doing here is registering a callback function
         for the context that's associated with the control object
         for the CSTA answer call argument (answerCallArgC).

         We will also be creating a control object for the ROSE
         header, but since that object will have the same context
         as the control object for the actual CSTA message, we
         only need to do this registering once.

         Remember we said above that the first argument to this
         callback function could be anything?  Here we're
         declaring that the first argument will be the pointer to
         the FILE structure for our log file, cast to void *.
      */
      answerCallArgC.setPrintStream(&writeToFile, (void *) fpLog);
   }
   else
   {
      printf("Failed to setup logging\n");
      return -1;
   }

   /* Now encode the actual CSTA message.  */

   if ((len = encodeAnswerCallRequest (encodeBuffer, answerCallArg, 
      answerCallArgC, staticID, connectionID)) < 0) return len;

   if (trace) {
      printf ("Encoding of answer call request was successful. See writer.log for details.\n");
      encodeBuffer.hexDump (len);
      encodeBuffer.binDump ();
   }
   answerCallArgC.toStream("AnswerCallArg");

   /* Encode ROSE header on top of encoded argument */

   ASN1T_CSTA_ROSE_PDU pdu;
   ASN1C_CSTA_ROSE_PDU pduC (encodeBuffer, pdu);
   if ((len = encodeROSERequestHeader (encodeBuffer, pdu, pduC, 
      invoke, len)) < 0) return len;
 
   if (trace) {
      printf ("Encoding of ROSE header was successful. See writer.log for details.\n");
      encodeBuffer.hexDump (len);
      encodeBuffer.binDump ();
   }
   pduC.toStream("ROSEHeader_AnswerCallArg");
   msgptr = encodeBuffer.getMsgPtr();
  
   /* Write the encoded message out to the output file */

   /* Note that g++ on Linux suggests the inner parentheses around the
      "assignment used as truth value".  I disagree, but here they are to
      eliminate the warning message.  */
   if ((fp = fopen (filename, "wb"))) {
      fwrite (msgptr, 1, len, fp);
      fclose (fp);
   }
   else {
      printf ("Error opening %s for write access\n", filename);
      return -1;
   }

   fclose (fpLog);

   return 0;

}

/* Encode "answerCall" operation Request */

static int encodeAnswerCallRequest (ASN1BEREncodeBuffer& encodeBuffer, 
                                    ASN1T_AnswerCallArgument& answerCallArg, 
                                    ASN1C_AnswerCallArgument& answerCallArgC,
                                    ASN1T_DeviceID& staticID,
                                    ASN1T_ConnectionID_both& both)
{
   int len;

   /* To make it simple, we will omit all of the optional fields */

   both.callID.numocts = 1;
   OSOCTET data = 0x10;
   both.callID.data = &data;

   staticID.deviceIdentifier.t = T_DeviceID_deviceIdentifier_dialingNumber;
   staticID.deviceIdentifier.u.dialingNumber = "22343";
   both.deviceID.t = T_LocalDeviceID_staticID;
   both.deviceID.u.staticID = &staticID;

   answerCallArg.callToBeAnswered.t = T_ConnectionID_both;
   answerCallArg.callToBeAnswered.u.both = &both;

   /* Encode */

   len = answerCallArgC.Encode();

   if (len < 0) {
      printf ("Encode of AnswerCallArgument failed.\n");
      encodeBuffer.printErrorInfo();
   }

   return len;
}

/* Encode ROSE header */

static int encodeROSERequestHeader (ASN1BEREncodeBuffer& encodeBuffer, 
                                    ASN1T_CSTA_ROSE_PDU& pdu,
                                    ASN1C_CSTA_ROSE_PDU& pduC,
                                    ASN1T_CSTA_ROSE_PDU_invoke& invoke,
                                    int msglen)
{
   int len;

   /* Populate header structure */

   invoke.m.argumentPresent = 1;
   invoke.invokeId.t = T_InvokeId_present;
   invoke.invokeId.u.present = 1; /* arbitrary number: should be unique */
   invoke.opcode.t = T_Code_local;
   invoke.opcode.u.local = 2;   /* opeartion code for "answerCall" operation from Table 1 */

   /* This is where we get the previously encoded message component..   */
   invoke.argument.numocts = msglen;
   invoke.argument.data = (OSOCTET*) encodeBuffer.getMsgPtr();

   pdu.t = T_CSTA_ROSE_PDU_invoke;
   pdu.u.invoke = &invoke;

   /* Encode */

   len = pduC.Encode();

   if (len < 0) {
      printf ("Encode ROSE PDU failed.\n");
      encodeBuffer.printErrorInfo();
   }

   return len;
}

