#include "stdafx.h"
#include "constdef.h"
#include "cpiccmc.h"

#include "winapdoc.h"
#include "docpic.h"                   // put this one after winapdoc.h

//----------------------------------------------------------------------------
// This does the allocation to partner
//----------------------------------------------------------------------------
long DoAllocate(CWinapingDoc* pDoc) {

  static CString DEFALUT_SYM_DEST_NAME = _T("APINGD  ");
  static CString   BLANK_SYM_DEST_NAME = _T("        ");
  static CString       BLANK_MODE_NAME = _T("        ");

  CM_INT32         cm_rc;               // return code for CPIC calls
  CM_INT32         length;              // length field for general use

  //--------------------------------------------------------------------------
  // decide destination is parter lu name, symbolic name, or neither (NULL)
  BOOL plnProvided = FALSE;
  BOOL symProvided = FALSE;
  CM_INT32 dest_len = (pDoc->pingInfo.destination).GetLength();
  if (dest_len > 0) {
    if ((dest_len <= MAX_SYM_DEST_NAME) &&
        ((pDoc->pingInfo.destination).Find(_T(".")) == -1)) symProvided = TRUE;
    else plnProvided = TRUE;
        }

  //--------------------------------------------------------------------------
  // cminit time
  if (symProvided) {                  // cminit with given symbolic name
    for (int i = dest_len; i < MAX_SYM_DEST_NAME; i++) {
      pDoc->pingInfo.destination += _T(" " );        // pad with blank
      }
    cminit(pDoc->cm_conv_id,
           (unsigned char*) (LPCTSTR)(pDoc->pingInfo.destination), &cm_rc);
    if (cm_rc != CM_OK) {
      pDoc->cmcall  = _T("CMINIT with ") + pDoc->pingInfo.destination;
      return cm_rc;
      }
    }
  else {                              // cminit with default names
    cminit(pDoc->cm_conv_id,
           (unsigned char*) (LPCTSTR)DEFALUT_SYM_DEST_NAME, &cm_rc);
    if (cm_rc != CM_OK) {
      cminit(pDoc->cm_conv_id,
             (unsigned char*) (LPCTSTR)BLANK_SYM_DEST_NAME, &cm_rc);
      if (cm_rc != CM_OK) {
        pDoc->cmcall  = _T("CMINIT with default destinations");
        return cm_rc;
        }
      }
    }
  if (pDoc->stoprunning) return 0;

  //--------------------------------------------------------------------------
  // If partner lu name given then set it, else make sure there is a good one
  if (plnProvided) {
    cmspln(pDoc->cm_conv_id,
           (unsigned char*) (LPCTSTR)(pDoc->pingInfo.destination),
           &dest_len, &cm_rc);
    if (cm_rc != CM_OK) { pDoc->cmcall  = _T("CMSPLN"); return cm_rc; }
    }
  else {
    unsigned char pln[MAX_DESTINATION + 1];
    cmepln(pDoc->cm_conv_id, pln, &length, &cm_rc);
    if (cm_rc != CM_OK) { pDoc->cmcall  = _T("CMEPLN"); return cm_rc; }
    if ((length == 1) && (pln[0] == ' ')) {
      pDoc->cmcall  = _T("CMEPLN"); return LAST_MESSAGE_NUM-1; } // special rc
    }
  if (pDoc->stoprunning) return 0;

  //--------------------------------------------------------------------------
  // Set tp name if given
  length = (pDoc->pingInfo.tpName).GetLength();
  if (length > 0) {
    cmstpn(pDoc->cm_conv_id, (unsigned char*) (LPCTSTR)(pDoc->pingInfo.tpName),
           &length, &cm_rc);
    if (cm_rc != CM_OK) { pDoc->cmcall  = _T("CMSTPN"); return cm_rc; }
    }
  if (pDoc->stoprunning) return 0;

  //--------------------------------------------------------------------------
  // Set mode name if given
  length = (pDoc->pingInfo.mode).GetLength();
  if ((length == 0) && plnProvided) {         // when partner lu name given &
    pDoc->pingInfo.mode = BLANK_MODE_NAME;   // no mode name given, use blanks
    length = 8;
    }
  if (length > 0) {
    cmsmn(pDoc->cm_conv_id, (unsigned char*) (LPCTSTR)(pDoc->pingInfo.mode),
          &length, &cm_rc);
    if (cm_rc != CM_OK) { pDoc->cmcall  = _T("CMSMN"); return cm_rc; }
    }
  if (pDoc->stoprunning) return 0;

  //--------------------------------------------------------------------------
  // Security (no override when symbolic name & send security not checked)
  if (plnProvided || pDoc->pingInfo.security) {
    CM_INT32 security_type = XC_SECURITY_NONE;
    if (pDoc->pingInfo.security) security_type = XC_SECURITY_PROGRAM;
    cmscst(pDoc->cm_conv_id, &security_type, &cm_rc);
    if (cm_rc != CM_OK) { pDoc->cmcall  = _T("CMSCST"); return cm_rc; }
    if (pDoc->stoprunning) return 0;

    if (pDoc->pingInfo.security) {
      length = (pDoc->pingInfo.userID).GetLength();
      if (length > 0) {
        cmscsu(pDoc->cm_conv_id,
               (unsigned char*) (LPCTSTR)(pDoc->pingInfo.userID),
               &length, &cm_rc); if (cm_rc != CM_OK)
        if (cm_rc != CM_OK) { pDoc->cmcall  = _T("CMSCSU"); return cm_rc; }
        if (pDoc->stoprunning) return 0;
        }
      length = (pDoc->pingInfo.password).GetLength();
      if (length > 0) {
        cmscsp(pDoc->cm_conv_id,
               (unsigned char*) (LPCTSTR)(pDoc->pingInfo.password),
               &length, &cm_rc);
        if (cm_rc != CM_OK) { pDoc->cmcall  = _T("CMSCSP"); return cm_rc; }
        if (pDoc->stoprunning) return 0;
        }
      }
    }

  //--------------------------------------------------------------------------
  // Set sync level
  CM_SYNC_LEVEL sync_level = CM_CONFIRM;
  cmssl(pDoc->cm_conv_id, &sync_level, &cm_rc);
  if (cm_rc != CM_OK) { pDoc->cmcall  = _T("CMSSL"); return cm_rc; }
  if (pDoc->stoprunning) return 0;

  //--------------------------------------------------------------------------
  // Set prepare to receive type
  CM_PREPARE_TO_RECEIVE_TYPE prep_to_receive = CM_PREP_TO_RECEIVE_FLUSH;
  cmsptr(pDoc->cm_conv_id, &prep_to_receive, &cm_rc);
  if (cm_rc != CM_OK) { pDoc->cmcall  = _T("CMSPTR"); return cm_rc; }
  if (pDoc->stoprunning) return 0;

  //--------------------------------------------------------------------------
  // Do allocation
  DWORD time_start = GetCurrentTime();
  cmallc(pDoc->cm_conv_id, &cm_rc);
  pDoc->alloctime = GetCurrentTime() - time_start;
  if (cm_rc != CM_OK) { pDoc->cmcall  = _T("CMALLC"); return cm_rc; }

  return cm_rc;
}

//----------------------------------------------------------------------------
// This does version/plateform information exchange with partner
//----------------------------------------------------------------------------
long DoExchange(CWinapingDoc* pDoc) {
    CM_PREPARE_TO_RECEIVE_TYPE prep_to_receive; /* CPI-C prepare to receive  */
    CM_SEND_TYPE send_type;                 /* CPI-C send type               */
    CM_INT32    cm_rc;                      /* CPI-C return code             */
    unsigned char buffer[EXCHANGE_BUFFER_SIZE]; /* data buffer               */
    CM_INT32    length;                     /* generic length variable       */
    CM_INT32    rts_received;               /* request to send received      */
    CM_INT32    max_receive_len;            /* Max receive length on CMRCV   */
    CM_INT32    what_received;              /* What received parm from CMRCV */
    CM_INT32    received_len;               /* Amount of data rcvd on CMRCV  */
    CM_INT32    status_received;            /* Status from CMRCV             */

  //--------------------------------------------------------------------------
  // start timer
  pDoc->confirmtime = 0;
  DWORD time_start = GetCurrentTime();
  //--------------------------------------------------------------------------
  prep_to_receive = CM_PREP_TO_RECEIVE_FLUSH;
  cmsptr(pDoc->cm_conv_id, &prep_to_receive, &cm_rc);
  if (cm_rc != CM_OK) { pDoc->cmcall  = _T("CMSPTR"); return cm_rc; }

  send_type = CM_BUFFER_DATA;
  cmsst(pDoc->cm_conv_id, &send_type, &cm_rc);
  if (cm_rc != CM_OK) { pDoc->cmcall  = _T("CMSST"); return cm_rc; }

  buffer[0] = CPICERR_EXCHANGE_VERSION;
  buffer[1] = MAJOR_VERSION;
  buffer[2] = MINOR_VERSION;
  length = 3;
  cmsend(pDoc->cm_conv_id, buffer, &length, &rts_received, &cm_rc);
  if (cm_rc != CM_OK) { pDoc->cmcall  = _T("CMSEND"); return cm_rc; }
  if (pDoc->stoprunning) return 0;

  max_receive_len = sizeof(buffer);
  cmrcv (pDoc->cm_conv_id, buffer, &max_receive_len, &what_received,
         &received_len, &status_received, &rts_received, &cm_rc);
  if (cm_rc != CM_OK) { pDoc->cmcall  = _T("CMRCV"); return cm_rc; }
  if (pDoc->stoprunning) return 0;

  if (what_received != CM_NO_DATA_RECEIVED) {
    if (received_len > 2 && buffer[0]==CPICERR_EXCHANGE_VERSION) {
      if (!pDoc->pingInfo.echo) {                      // we are doing one way
        if ( (buffer[1] >  ONEWAY_MAJOR_VERSION) ||
            ((buffer[1] == ONEWAY_MAJOR_VERSION) &&
             (buffer[2] >= ONEWAY_MINOR_VERSION)) ) { }
        else {            // partner does not support one way, will do two way
          pDoc->pingInfo.echo   = TRUE;
          pDoc->pingInfo.verify = FALSE;
          pDoc->cmcall  = _T("No echo request");
          cm_rc = -1;     // special rc to display 2 way message
          }
        }
    if (received_len > 4 && buffer[3]==CPICERR_EXCHANGE_OPSYS_STRING) {
      LPSTR pWork = pDoc->partner.GetBuffer(received_len-4);
      for (int nCount = 4; nCount < received_len; nCount++)
        *pWork++ = buffer[nCount];
      pDoc->partner.ReleaseBuffer(received_len-4);
      }
    }
  if (status_received == CM_CONFIRM_RECEIVED) {
    cmcfmd(pDoc->cm_conv_id, &cm_rc);
    if (cm_rc != CM_OK) { pDoc->cmcall  = _T("CMCFMD"); return cm_rc; }
    }
  else if (status_received != CM_SEND_RECEIVED) {
         pDoc->cmcall  = _T("Exchange version/platform");
         cm_rc = LAST_MESSAGE_NUM - 2;                  // special rc
         }
  }
  pDoc->confirmtime = GetCurrentTime() - time_start;

  return cm_rc;
}

//----------------------------------------------------------------------------
// This does one iteration
//----------------------------------------------------------------------------
long DoIteration(CWinapingDoc* pDoc, long* pTime, BOOL* pVerifyOK) {

  CM_INT32       cm_rc;
  CM_INT32       length = pDoc->pingInfo.packetSize;
  CM_INT32       rts_received;
  UINT           nIdx;
  unsigned char* px;
  unsigned char* py;
  unsigned char  seed1, seed2;
  DWORD          time_start;
  CM_SEND_TYPE   send_type = CM_BUFFER_DATA;

  seed1 = seed2 = rand() % 26;                 // randomly between 0 & 25
  *pTime = 0;
  *pVerifyOK = TRUE;
  //--------------------------------------------------------------------------
  cmsst(pDoc->cm_conv_id, &send_type, &cm_rc);
  if (cm_rc != CM_OK) { pDoc->cmcall  = _T("CMSST"); return cm_rc; }

  //--------------------------------------------------------------------------
  // Start timer
  time_start = GetCurrentTime();

  //--------------------------------------------------------------------------
  // Send consecutivePackets times
  for (nIdx = 1; nIdx <= pDoc->pingInfo.consecutivePackets; nIdx++) {
    if (pDoc->pingInfo.verify) {                    // fill in buffer if verify
      py = (pDoc->buffer) + (pDoc->pingInfo.packetSize);           // end point
      for (px = pDoc->buffer; px < py; ) {
        *(px++) = 'a'+ seed1++;
        if (seed1 >= 26) seed1 -= 26;
        }
      }
    if (nIdx == pDoc->pingInfo.consecutivePackets) { // this is last one to send
      if (pDoc->pingInfo.echo) send_type = CM_SEND_AND_PREP_TO_RECEIVE;
      else                     send_type = CM_SEND_AND_CONFIRM;
      cmsst(pDoc->cm_conv_id, &send_type, &cm_rc);
      if (cm_rc != CM_OK) { pDoc->cmcall  = _T("CMSST"); return cm_rc; }
      }

    cmsend(pDoc->cm_conv_id, pDoc->buffer, &length, &rts_received, &cm_rc);
    if (cm_rc != CM_OK) { pDoc->cmcall  = _T("CMSEND"); return cm_rc; }
    if (pDoc->stoprunning) return 0;
    }

  *pTime = GetCurrentTime() - time_start;         // in no echo case
  if (pDoc->pingInfo.echo) {
    //--------------------------------------------------------------------------
    // Receive consecutivePackets times

    CM_INT32      rts_received;
    CM_INT32      max_receive_len = pDoc->pingInfo.packetSize;
    CM_INT32      data_received;
    CM_INT32      received_len;
    CM_INT32      status_received;
    unsigned long total_bytes = (pDoc->pingInfo.consecutivePackets) *
                                (pDoc->pingInfo.packetSize);   // total expected

    nIdx = pDoc->pingInfo.consecutivePackets;             // # of cmrcv expected
    do {
      cmrcv (pDoc->cm_conv_id, pDoc->buffer, &max_receive_len, &data_received,
             &received_len, &status_received, &rts_received, &cm_rc);
      if (cm_rc != CM_OK) { pDoc->cmcall  = _T("CMRCV"); return cm_rc; }
      if (pDoc->stoprunning) return 0;
      if (data_received != CM_NO_DATA_RECEIVED) {
        nIdx--;
        total_bytes -= received_len;
        if ((pDoc->pingInfo.verify) && (*pVerifyOK)) {
          if (nIdx) {                     // update seed2 if not last cmrcv
            seed2 += (unsigned char) (received_len % 26);
            if (seed2 >= 26) seed2 -= 26;
            }
          else {                          // verify data for the last cmrcv
            px = pDoc->buffer;
            py = px + received_len;       // end point
            while ((*pVerifyOK) && (px < py)) {
              if (*(px++) != 'a' + seed2++) *pVerifyOK = FALSE;
              if (seed2 >= 26) seed2 -= 26;
              }
            }
          }
        }
      } while ((status_received !=  CM_SEND_RECEIVED));

    *pTime = GetCurrentTime() - time_start;    // time it at echo end
    if ((nIdx) || (total_bytes)) {
//    seed2 = seed1;                                   // reset seed2 as seed1
      pDoc->cmcall  = _T("An iteraion");
      cm_rc = LAST_MESSAGE_NUM - 4;                    // special rc
      }
    }

  return cm_rc;
}

//----------------------------------------------------------------------------
// This does cpic deallocation with partner
//----------------------------------------------------------------------------
long DoDeallocate(CWinapingDoc* pDoc) {

  //--------------------------------------------------------------------------
  CM_INT32 cm_rc;

  CM_CONVERSATION_STATE cm_state;
  cmecs(pDoc->cm_conv_id, &cm_state, &cm_rc);
  if (cm_rc == CM_PROGRAM_PARAMETER_CHECK) return 0;
  if (cm_rc == CM_OK) {
//if (cm_state != CM_INITIALIZE_STATE) {
    CM_DEALLOCATE_TYPE deallocate_type = CM_DEALLOCATE_FLUSH;
    cmsdt(pDoc->cm_conv_id, &deallocate_type, &cm_rc);
    if (cm_rc != CM_OK) pDoc->cmcall  = _T("CMSDT");
    else {
      cmdeal(pDoc->cm_conv_id, &cm_rc);
      if (cm_rc != CM_OK) {   // try again (conversation state could be RECEIVE)
        deallocate_type = CM_DEALLOCATE_ABEND;
        cmsdt(pDoc->cm_conv_id, &deallocate_type, &cm_rc);
        if (cm_rc != CM_OK) pDoc->cmcall  = _T("CMSDT");
        else {
          cmdeal(pDoc->cm_conv_id, &cm_rc);
          if (cm_rc != CM_OK) pDoc->cmcall  = _T("CMSDEAL");
          }
        }
      }
    }

  return cm_rc;
}

//----------------------------------------------------------------------------
// This translate the cpic return code to a message string
//----------------------------------------------------------------------------
void DoTranslate(CWinapingDoc* pDoc, long rc)
{
  typedef struct t_cpicmessage {
    char *Message;
    CM_INT32 Code;
    } CPICMESSAGE;

//{"CM_SYNC_LVL_NOT_SUPPORTED_LU    ", 7},
//{"CM_SYNC_LEVEL_NOT_SUPPORTED_LU  ", 7},
//{"CM_CONV_DEALLOC_AFTER_SYNCPT  ", 136},
//{"CM_INCLUDE_PARTNER_REJECT_BO  ", 137},

  CPICMESSAGE CPICReturnCode[] = {
    {"Will do echo without verify", -1},     // -1 ===> info only, will go on
    {"Unexpected number of data echoed", LAST_MESSAGE_NUM-4},
    {"For Packet Size as requested", LAST_MESSAGE_NUM-3},
    {"CM_SEND_RECEIVED status not received", LAST_MESSAGE_NUM-2},
    {"Extracted partner lu name invalid", LAST_MESSAGE_NUM-1},
    {"CM_OK", 0},
    {"CM_ALLOCATE_FAILURE_NO_RETRY", 1},
    {"CM_ALLOCATE_FAILURE_RETRY", 2},
    {"CM_CONVERSATION_TYPE_MISMATCH", 3},
    {"CM_PIP_NOT_SPECIFIED_CORRECTLY", 5},
    {"CM_SECURITY_NOT_VALID", 6},
    {"CM_SYNC_LVL_NOT_SUPPORTED_SYS", 7},
    {"CM_SYNC_LVL_NOT_SUPPORTED_PGM", 8},
    {"CM_TPN_NOT_RECOGNIZED", 9},
    {"CM_SYNC_LEVEL_NOT_SUPPORTED_PGM", 8},
    {"CM_TPN_NOT_RECOGNISED", 9},
    {"CM_TP_NOT_AVAILABLE_NO_RETRY", 10},
    {"CM_TP_NOT_AVAILABLE_RETRY", 11},
    {"CM_DEALLOCATED_ABEND", 17},
    {"CM_DEALLOCATED_NORMAL", 18},
    {"CM_PARAMETER_ERROR", 19},
    {"CM_PRODUCT_SPECIFIC_ERROR", 20},
    {"CM_PROGRAM_ERROR_NO_TRUNC", 21},
    {"CM_PROGRAM_ERROR_PURGING", 22},
    {"CM_PROGRAM_ERROR_TRUNC", 23},
    {"CM_PROGRAM_PARAMETER_CHECK ", 24},
    {"CM_PROGRAM_STATE_CHECK", 25},
    {"CM_RESOURCE_FAILURE_NO_RETRY", 26},
    {"CM_RESOURCE_FAILURE_RETRY", 27},
    {"CM_UNSUCCESSFUL", 28},
    {"CM_DEALLOCATED_ABEND_SVC", 30},
    {"CM_DEALLOCATED_ABEND_TIMER", 31},
    {"CM_SVC_ERROR_NO_TRUNC", 32},
    {"CM_SVC_ERROR_PURGING", 33},
    {"CM_SVC_ERROR_TRUNC", 34},
    {"CM_OPERATION_INCOMPLETE ", 35},
    {"CM_SYSTEM_EVENT", 36},
    {"CM_OPERATION_NOT_ACCEPTED", 37},
    {"CM_CONVERSATION_ENDING", 38},
    {"CM_SEND_RCV_MODE_NOT_SUPPORTED", 39},
    {"CM_BUFFER_TOO_SMALL", 40},
    {"CM_EXP_DATA_NOT_SUPPORTED", 41},
    {"CM_DEALLOC_CONFIRM_REJECT", 42},
    {"CM_ALLOCATION_ERROR", 43},
    {"CM_RETRY_LIMIT_EXCEEDED", 44},
    {"CM_NO_SECONDARY_INFORMATION", 45},
    {"CM_SECURITY_NOT_SUPPORTED", 46},
    {"CM_SECURITY_MUTUAL_FAILED", 47},
    {"CM_CALL_NOT_SUPPORTED", 48},
    {"CM_PARM_VALUE_NOT_SUPPORTED", 49},
    {"CM_TAKE_BACKOUT", 100},
    {"CM_DEALLOCATED_ABEND_BO", 130},
    {"CM_DEALLOCATED_ABEND_SVC_BO", 131},
    {"CM_DEALLOCATED_ABEND_TIMER_BO", 132},
    {"CM_RESOURCE_FAIL_NO_RETRY_BO", 133},
    {"CM_RESOURCE_FAILURE_RETRY_BO", 134},
    {"CM_DEALLOCATED_NORMAL_BO", 135},
    {"Invalid return code or message number", LAST_MESSAGE_NUM}
    };

  int i;
  for (i = 0; CPICReturnCode[i].Code < LAST_MESSAGE_NUM; i++)
     if (rc == CPICReturnCode[i].Code) break;
  pDoc->cmrcmsg = CPICReturnCode[i].Message;
}
