#include "client.h"

/* Encode ROSE header */

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

   if (trace) {
      printf ("encode Rose Request Header\n" );
   }

   /* Populate header structure */

   invoke.m.argumentPresent = 1;
   invoke.invokeId.t = T_InvokeId_present;
   invoke.invokeId.u.present = 1;
   invoke.opcode.t = T_Code_local;
   invoke.opcode.u.local = opcode; 

   /* 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;
}

int Client::encodeROSEResponseHeader (ASN1BEREncodeBuffer& encodeBuffer, 
                                     int msglen, int opcode)
{
   ASN1T_CSTA_ROSE_PDU pdu;
   ASN1C_CSTA_ROSE_PDU pduC (encodeBuffer, pdu);
   ASN1T_ReturnResult returnResult;
   int len;

   /* Populate header structure */

   returnResult.m.resultPresent = 1;
   returnResult.invokeId.t = T_InvokeId_present;
   returnResult.invokeId.u.present = 1; /* arbitrary number: should be unique */
   returnResult.result.opcode.t = T_Code_local;
   returnResult.result.opcode.u.local = opcode;   /* opeartion code for operation from Table 1 */

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

   pdu.t = T_CSTA_ROSE_PDU_returnResult;
   pdu.u.returnResult = &returnResult;

   /* Encode */

   len = pduC.Encode();

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

   return len;
}

int Client::sendMessage (OSRTSocket& cstaSocket, 
   ASN1BEREncodeBuffer& encodeBuffer, size_t len)
{
   OSOCTET lenbytes[2];
   OSCTXT* pctxt;

   if (mMessageType == Client::ietf ||
       mMessageType == Client::siemens_cap ||
       mMessageType == Client::panasonic) {

      /* add 2 header bytes to message */

      pctxt = encodeBuffer.getCtxtPtr();

      /* Add special 2-byte length wrapper before encoded data */
      /* convert to network byte order */ 
      lenbytes[0] = len / 256;
      lenbytes[1] = len % 256;
      xe_memcpy (pctxt, lenbytes, 2);

      len += 2;

   } 
   else if (mMessageType == Client::siemens_hipath_3000) { 
      /* add 3 header bytes to the message */

      pctxt = encodeBuffer.getCtxtPtr();

      /* Add special 2-byte length wrapper to encoded data and application code */
      /*(note: this is specific to the Siemens PBX).. */

      /* convert to network byte order */
      lenbytes[0] = len / 256;
      lenbytes[1] = len % 256;
      lenbytes[0] |= 0x80;
      xe_memcpy (pctxt, lenbytes, 2);

      /* Add system_application code (CTI3_APPLICATION = 0x26) */

      OSOCTET applcode = 0x26;
      xe_memcpy (pctxt, &applcode, 1);

      len += 3;
   }

   const OSOCTET* msgptr = encodeBuffer.getMsgPtr();

   /* Send message to device socket */

   if (trace) {
      printf ("sending message to PBX, len = %zu\n", len);
      rtxHexDump (msgptr, len);
   }

   cstaSocket.send (msgptr, len);
   printf ("message sent to PBX, len = %zu\n", len);

   return 0;
}

int Client::receiveMessage (OSRTSocket& cstaSocket, ASN1BERDecodeBuffer &decodeBuffer)
{
   int len;
   int stat;

   /* Receive response message */
   if (trace) {
      printf ("receive message from socket\n");
   }

   OSOCTET* msgbuf = 0;
   int msglen = 0;

   if (mMessageType == ietf ||
       mMessageType == siemens_cap ||
       mMessageType == panasonic) { /* with 2 header bytes + message */

      if (trace) {
         printf ("receiving response length from PBX..\n");
      }

      /* receive 2 header bytes for length */

      OSOCTET lenbytes[2];

      lenbytes[0] = lenbytes[1] = 0;
      len = cstaSocket.blockingRead (lenbytes, 2);
      if (len != 2) {
         printf ("error: recv failed, len = %d\n", len);
         return -1;
      }

      msglen = (lenbytes[0] * 256) + lenbytes[1];
      if (trace) {
         printf ("Message length = %d, Length bytes = { %02x, %02x }\n", 
            msglen, lenbytes[0], lenbytes[1]);
      }

      if(msglen <= 0) {
         printf("error: invalid message size\n");
         return -1;
      }

      /* receive ASN.1 BER-encoded data */
      msgbuf = (OSOCTET*)rtxMemAlloc(decodeBuffer.getCtxtPtr(), msglen);

      len = cstaSocket.blockingRead (msgbuf, msglen);
      if (len != msglen) {
         printf ("error: blocking read failed: len = %d\n", len);
         return -1;
      }

    } 
    else if (mMessageType == siemens_hipath_3000) { /* with 3 header bytes + message */
      /* First byte should be system_application. */

      if (trace) {
         printf ("receiving first response byte from PBX..\n");
      }

      OSOCTET b;
      len = cstaSocket.recv (&b, 1);
      if (len == 1) {
      if (b != 0x26) 
         printf ("warning: unexpected first byte = %02x\n", b);
      }
      else {
         printf ("error: recv failed, len = 0\n");
         return -1;
      }

      /* Receive length bytes */

      if (trace) {
         printf ("receiving length bytes from PBX..\n");
      }

      OSOCTET lenbytes[2];

      lenbytes[0] = lenbytes[1] = 0;
      len = cstaSocket.blockingRead (lenbytes, 2);
      if (len != 2) {
         printf ("error: recv failed, len = %d\n", len);
         return -1;
      }

      lenbytes[0] &= 0x7F;   // mask off continuation bit
      msglen = (lenbytes[0] * 256) + lenbytes[1];
      if (trace) {
         printf ("Message length = %d, Length bytes = { %02x, %02x }\n", 
            msglen, lenbytes[0], lenbytes[1]);
      }

      if(msglen <= 0) {
         printf("error: invalid message size\n");
         return -1;
      }

      /* Receive ASN.1 BER-encoded data */
      msgbuf = (OSOCTET*)rtxMemAlloc(decodeBuffer.getCtxtPtr(), msglen);

      len = cstaSocket.blockingRead (msgbuf, msglen);
      if (len != msglen) {
         printf ("error: blocking read failed: len = %d\n", len);
         return -1;
      }
   } 
   else { /* without any header bytes */
      stat = berReadMsgFromSocket (decodeBuffer.getCtxtPtr(), 
         cstaSocket.getSocket(), 0, 0, &msgbuf, &msglen);
      if (stat != 0) {
         printf ("error: socket read failed, status = %d\n", stat);
         return -1;
      }
      if (trace) {
         printf ("incoming message length = %d \n", msglen);
      }
   }

   decodeBuffer.setBuffer (msgbuf, msglen);
   if (trace) {
      printf ("message dump:\n");
      decodeBuffer.hexDump(msglen);
   }

   return 0;
}

