#ifdef __cplusplus
extern "C" {
#endif
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#ifdef __cplusplus
}
#endif

static char rcsid[] = "$Id: PCF.xs,v 10.1 1999/11/22 21:11:17 wpm Exp $";

/*
  (c) 1999 Morgan Stanley Dean Witter and Co.
  See ..../src/LICENSE for terms of distribution.
 */

/*
  Copied from DevelPPPort-1.0003/ppport.h
 */
#ifndef PERL_PATCHLEVEL
#       ifndef __PATCHLEVEL_H_INCLUDED__
#               include "patchlevel.h"
#       endif
#endif
#ifndef PERL_PATCHLEVEL
#       define PERL_PATCHLEVEL PATCHLEVEL
#       define PERL_SUBVERSION SUBVERSION
#endif
 
#ifndef ERRSV
#       define ERRSV perl_get_sv("@",FALSE)
#endif
 
#if (PERL_PATCHLEVEL < 4) || ((PERL_PATCHLEVEL == 4) && (PERL_SUBVERSION <= 4))
#       define PL_sv_undef      sv_undef
#       define PL_sv_yes        sv_yes
#       define PL_sv_no         sv_no
#       define PL_na            na
#       define PL_stdingv       stdingv
#       define PL_hints         hints
#       define PL_curcop        curcop
#       define PL_curstash      curstash
#       define PL_copline       copline
#endif
 
#if (PERL_PATCHLEVEL < 5)
#  ifdef WIN32
#       define dTHR extern int Perl___notused
#  else
#       define dTHR extern int errno
#  endif
#endif
 
#ifndef boolSV
#       define boolSV(b) ((b) ? &PL_sv_yes : &PL_sv_no)
#endif

#include "cmqc.h"
#include "cmqcfc.h"

/*#define DEBUGME*/

#ifdef DEBUGME
#define TRACEME(x)	do { PerlIO_stdoutf x; PerlIO_stdoutf("\n"); } while (0)
#else
#define TRACEME(x)
#endif

MODULE = MQSeries::Message::PCF		PACKAGE = MQSeries::Message::PCF

void
MQDecodePCF(pBuffer)
	PMQCHAR pBuffer;

	PPCODE:
	{

	  PMQCHAR pTemp = pBuffer;
	  PMQCHAR pSListTemp;
	  MQCFST *pStringParam;
	  MQCFIN *pIntegerParam;
	  MQCFSL *pStringParamList;
	  MQCFIL *pIntegerParamList;
	  MQLONG StringLength;

	  HV *HeaderHV, *DataHV;
	  AV *DataAV, *ListAV;
	  SV **svp;
	  int count, listcount, foundtype, tmpStringLength;

	  MQCFH Header = *(MQCFH *)pTemp;
	  pTemp += Header.StrucLength;

	  HeaderHV = newHV();	

	  hv_store(HeaderHV,"Type",4,(newSViv(Header.Type)),0);
	  hv_store(HeaderHV,"Version",7,(newSViv(Header.Version)),0);
	  hv_store(HeaderHV,"Command",7,(newSViv(Header.Command)),0);
	  hv_store(HeaderHV,"MsgSeqNumber",12,(newSViv(Header.MsgSeqNumber)),0);
	  hv_store(HeaderHV,"Control",7,(newSViv(Header.Control)),0);
	  hv_store(HeaderHV,"CompCode",8,(newSViv(Header.CompCode)),0);
	  hv_store(HeaderHV,"Reason",6,(newSViv(Header.Reason)),0);
	  hv_store(HeaderHV,"ParameterCount",14,(newSViv(Header.ParameterCount)),0);

	  XPUSHs(sv_2mortal(newRV_noinc((SV*)HeaderHV)));
	  
	  DataAV = newAV();

	  for ( count = 0 ; count < (int)Header.ParameterCount ; count++ ) {

	    pStringParam = (MQCFST *)pTemp;
	    pIntegerParam = (MQCFIN *)pTemp;
	    pStringParamList = (MQCFSL *)pTemp;
	    pIntegerParamList = (MQCFIL *)pTemp;

	    foundtype = 0;

	    if ( pStringParam->Type == MQCFT_STRING ) {

	      DataHV = newHV();
	      
	      hv_store(DataHV,"Type",4,(newSViv(pStringParam->Type)),0);
	      hv_store(DataHV,"Parameter",9,(newSViv(pStringParam->Parameter)),0);
	      hv_store(DataHV,"CodedCharSetId",14,(newSViv(pStringParam->CodedCharSetId)),0);
	      hv_store(DataHV,"String",6,(newSVpv(pStringParam->String,pStringParam->StringLength)),0);
	      
	      av_push(DataAV,newRV_noinc((SV*)DataHV));

	      pTemp += pStringParam->StrucLength;

	      foundtype = 1;
		
	    }

	    if ( pIntegerParam->Type == MQCFT_INTEGER ) {

	      DataHV = newHV();
	      
	      hv_store(DataHV,"Type",4,(newSViv(pIntegerParam->Type)),0);
	      hv_store(DataHV,"Parameter",9,(newSViv(pIntegerParam->Parameter)),0);
	      hv_store(DataHV,"Value",5,(newSViv(pIntegerParam->Value)),0);

	      av_push(DataAV,newRV_noinc((SV*)DataHV));

	      pTemp += pIntegerParam->StrucLength;

	      foundtype = 1;

	    }	

	    if ( pStringParamList->Type == MQCFT_STRING_LIST ) {

	      DataHV = newHV();
	      
	      hv_store(DataHV,"Type",4,(newSViv(pStringParamList->Type)),0);
	      hv_store(DataHV,"Parameter",9,(newSViv(pStringParamList->Parameter)),0);
	      hv_store(DataHV,"CodedCharSetId",14,(newSViv(pStringParamList->CodedCharSetId)),0);

	      ListAV = newAV();
	      pSListTemp = pStringParamList->Strings;

	      for ( listcount = 0 ; listcount < (int)pStringParamList->Count ; listcount++ ) {

		tmpStringLength = 0;
		
		while ( 
		       tmpStringLength < pStringParamList->StringLength &&
		       pSListTemp[tmpStringLength] != '\0'
		       ) {
		  tmpStringLength++;
		}

		av_push(ListAV,newSVpv(pSListTemp,tmpStringLength));
		pSListTemp += pStringParamList->StringLength;

	      }

	      hv_store(DataHV,"Strings",7,(newRV_noinc((SV*)ListAV)),0);

	      av_push(DataAV,newRV_noinc((SV*)DataHV));

	      pTemp += pStringParamList->StrucLength;

	      foundtype = 1;
	      
	    }
	    
	    if ( pIntegerParamList->Type == MQCFT_INTEGER_LIST ) {

	      DataHV = newHV();

	      hv_store(DataHV,"Type",4,(newSViv(pIntegerParamList->Type)),0);
	      hv_store(DataHV,"Parameter",9,(newSViv(pIntegerParamList->Parameter)),0);

	      ListAV = newAV();
	      
	      for ( listcount = 0 ; listcount < (int)pIntegerParamList->Count ; listcount++ ) {
		av_push(ListAV,newSViv(pIntegerParamList->Values[listcount]));
	      }

	      hv_store(DataHV,"Values",6,(newRV_noinc((SV*)ListAV)),0);
	      
	      av_push(DataAV,newRV_noinc((SV*)DataHV));

	      pTemp += pIntegerParamList->StrucLength;

	      foundtype = 1;

	    }

	    if ( !foundtype ) {
	      warn("MQDecodePCF: Unrecognized parameter in data list.\n");
	      XSRETURN_EMPTY;
	    }

	  }

	  XPUSHs(sv_2mortal(newRV_noinc((SV *)DataAV)));	

	}

void
MQEncodePCF(Header,ParameterList)
     	MQCFH   Header;
	AV*	ParameterList = (AV*)SvRV(ST(1));
	
	PPCODE:
	{
	  
	  IV Type;
	  SV *Result, *ParameterResult, **svp;
	  AV *ValuesAV, *StringsAV;
	  HV *ParameterHV;
	  
	  MQCFST *pStringParam;
	  MQCFIN *pIntegerParam;
	  MQCFSL *pStringParamList;
	  MQCFIL *pIntegerParamList;

	  MQLONG Parameter, Value, CodedCharSetId, StrucLength;

	  STRLEN StringLength;
	  
	  int index, subindex, MaxStringLength, typecount;
	  char *String, *pString;
	  
	  ParameterResult = newSVpv("",0);

	  Header.ParameterCount = av_len(ParameterList) + 1;

	  for ( index = 0 ; index <= av_len(ParameterList) ; index++ ) {

	    /*
	      Get the next element in the array, and verify that it is
	      a HASH reference.
	     */
	    svp = av_fetch(ParameterList,index,0);
	    
	    if ( !SvROK(*svp) || ( SvROK(*svp) && SvTYPE(SvRV(*svp)) != SVt_PVHV ) ) {
	      warn("MQEncodePCF: ParameterList entry '%d' is not a HASH reference.\n",index);
	      XSRETURN_UNDEF;
	    }

	    ParameterHV = (HV*)SvRV(*svp);

	    /*
	      Look for the following keys: String, Value, Strings,
	      Values.  Only one of these is allowed to exist, and its
	      existence determines the type of data structure (MQCF*)
	      we are encoding.  
	     */
	    
	    typecount = 0;
	    
	    if ( hv_exists(ParameterHV,"Value",5) ) {
	      typecount++;
	      Type = MQCFT_INTEGER;
	    }

	    if ( hv_exists(ParameterHV,"String",6) ) {
	      typecount++;
	      Type = MQCFT_STRING;
	    }

	    if ( hv_exists(ParameterHV,"Values",6) ) {
	      typecount++;
	      Type = MQCFT_INTEGER_LIST;
	    }

	    if ( hv_exists(ParameterHV,"Strings",7) ) {
	      typecount++;
	      Type = MQCFT_STRING_LIST;
	    }

	    if ( typecount == 0 ) {
	      warn("MQEncodePCF: ParameterList entry '%d' has none of the following keys:\n",index);
	      warn("\tString Value Strings Values\n");
	      XSRETURN_UNDEF;
	    }
	    
	    if ( typecount > 1 ) {
	      warn("MQEncodePCF: ParameterList entry '%d' has more than one of the following keys:\n",index);
	      warn("\tString Value Strings Values\n");
	      XSRETURN_UNDEF;
	    }

	    /*
	      Get the Parameter key from the HASH, which all 4 types
	      must have, and verify that it is an integer.
	     */
	    svp = hv_fetch(ParameterHV,"Parameter",9,0);
	    if ( svp == NULL ) {
	      warn("MQEncodePCF: ParameterList entry '%d' has no 'Parameter' key.\n",index);
	      XSRETURN_UNDEF;
	    }

	    Parameter = SvIV(*svp);

	    /*
	      Extract the CodedCharSetId key, if it exists, and
	      verify that it is an integer (notmuch else we can, or
	      want, to do here).
	    */
	    if ( Type == MQCFT_STRING || Type == MQCFT_STRING_LIST ) {

	      svp = hv_fetch(ParameterHV,"CodedCharSetId",14,0);
	      if ( svp != NULL ) {
		CodedCharSetId = SvIV(*svp);
	      }
	      else {
		CodedCharSetId = MQCCSI_DEFAULT;
	      }
	      
	    }

	    /*
	      The rest of the processing of the HASH depends on which
	      Type we have.
	     */

	    if ( Type == MQCFT_INTEGER ) {

	      /*
		Extract the Value key, and verify that it is an
		integer
	      */
	      svp = hv_fetch(ParameterHV,"Value",5,0);
	      if ( svp == NULL ) {
		warn("MQEncodePCF: ParameterList entry '%d' has no 'Value' key.\n",index);
		XSRETURN_UNDEF;
	      }

	      Value = SvIV(*svp);

	      /*
		We can now create the MQCFIN structure, and
		concatenate it to the ParameterResult.
	       */
	      if ( (pIntegerParam = (MQCFIN *)malloc(MQCFIN_STRUC_LENGTH)) == NULL ) {
		perror("Unable to allocate memory");
		XSRETURN_UNDEF;
	      }

	      pIntegerParam->Type = MQCFT_INTEGER;
	      pIntegerParam->StrucLength = MQCFIN_STRUC_LENGTH;
	      pIntegerParam->Parameter = Parameter;
	      pIntegerParam->Value = Value;

	      sv_catpvn(ParameterResult,(char *)pIntegerParam,MQCFIN_STRUC_LENGTH);
	      
	      free(pIntegerParam);

	    }

	    if ( Type == MQCFT_STRING ) {
	      
	      /*
		Extract the String, which must obviously be a string
	       */
	      svp = hv_fetch(ParameterHV,"String",6,0);
	      if ( svp == NULL ) {
		warn("MQEncodePCF: ParameterList entry '%d' has no 'String' key.\n",index);
		XSRETURN_UNDEF;
	      }

	      if ( !SvPOK(*svp) ) {
		warn("MQEncodePCF: ParameterList entry '%d' String key is not a string.\n",index);
		XSRETURN_UNDEF;
	      }

	      String = SvPV(*svp,StringLength);

	      /*
		The total size of the structure needs to be a multiple
		of 4 (or some buggy IBM code will vomit somewhere, I'm
		sure...)
	       */
	      StrucLength = MQCFST_STRUC_LENGTH_FIXED + StringLength;
	      if ( StrucLength % 4 )
		StrucLength += 4 - (StrucLength%4);

	      /*
		Create the MQCFST structure, and concatenate it to the
		ParameterResult
	      */
	      if ( (pStringParam = (MQCFST *)malloc(StrucLength)) == NULL ) {
		perror("Unable to allocate memory");
		XSRETURN_UNDEF;
	      }

	      pStringParam->Type 		= MQCFT_STRING;
	      pStringParam->Parameter 		= Parameter;
	      pStringParam->CodedCharSetId 	= CodedCharSetId;
	      pStringParam->StringLength 	= StringLength;
	      pStringParam->StrucLength 	= StrucLength;
	      
	      memset(pStringParam->String,'\0',StrucLength - MQCFST_STRUC_LENGTH_FIXED);
	      strncpy(pStringParam->String,String,StringLength);
	      
	      sv_catpvn(ParameterResult, (char *)pStringParam, pStringParam->StrucLength);
	      
	      free(pStringParam);

	    }

	    if ( Type == MQCFT_INTEGER_LIST ) {

	      /*
		Extract the Values key, and verify that it is an ARRAY
		reference
	      */
	      svp = hv_fetch(ParameterHV,"Values",6,0);
	      if ( svp == NULL ) {
		warn("MQEncodePCF: ParameterList entry '%d' has no 'Values' key.\n",index);
		XSRETURN_UNDEF;
	      }

	      if ( !SvROK(*svp) || ( SvROK(*svp) && SvTYPE(SvRV(*svp)) != SVt_PVAV ) ) {
		warn("MQEncodePCF: ParameterList entry '%d' Values key is not an ARRAY reference.\n",index);
		XSRETURN_UNDEF;
	      }
	      
	      ValuesAV = (AV*)SvRV(*svp);
	      
	      if ( av_len(ValuesAV) == -1 ) {
		warn("MQEncodePCF: ParameterList entry '%d' Values ARRAY is empty.\n",index);
		XSRETURN_UNDEF;
	      }

	      /*
		Calculate the length of the structure
	       */
	      StrucLength = (MQLONG)(MQCFIL_STRUC_LENGTH_FIXED + 
				     ( sizeof(MQLONG) * (av_len(ValuesAV) + 1)));

	      /*
		Create the MQCFIL structure...
	      */
	      if ( ( pIntegerParamList = (MQCFIL *)malloc(StrucLength) ) == NULL ) {
		perror("Unable to allocate memory");
		XSRETURN_UNDEF;
	      }

	      pIntegerParamList->Type = MQCFT_INTEGER_LIST;
	      pIntegerParamList->StrucLength = StrucLength;
	      pIntegerParamList->Parameter = Parameter;
	      pIntegerParamList->Count = av_len(ValuesAV) + 1;
	      
	      for ( subindex = 0 ; subindex <= av_len(ValuesAV) ; subindex++ ) {
		svp = av_fetch(ValuesAV,subindex,0);
		if ( svp == NULL ) {
		  warn("MQEncodePCF: Unable to retreive ParameterList entry '%d' Values entry '%d'.\n",
		       index,subindex);
		  free(pIntegerParamList);
		  XSRETURN_UNDEF;
		}
		pIntegerParamList->Values[subindex] = SvIV(*svp);	
	      }

	      sv_catpvn(ParameterResult, (char *)pIntegerParamList, StrucLength);
	      free(pIntegerParamList);

	    }

	    if ( Type == MQCFT_STRING_LIST ) {
	      
	      /*
		Extract the Strings key and verify that it is an ARRAY
		reference
	      */
	      svp = hv_fetch(ParameterHV,"Strings",7,0);
	      if ( svp == NULL ) {
		warn("MQEncodePCF: ParameterList entry '%d' has no 'Strings' key.\n",index);
		XSRETURN_UNDEF;
	      }

	      if ( !SvROK(*svp) || ( SvROK(*svp) && SvTYPE(SvRV(*svp)) != SVt_PVAV ) ) {
		warn("MQEncodePCF: ParameterList entry '%d' Strings key is not an ARRAY reference.\n",index);
		XSRETURN_UNDEF;
	      }
	      
	      StringsAV = (AV*)SvRV(*svp);
	      
	      if ( av_len(StringsAV) == -1 ) {
		warn("MQEncodePCF: ParameterList entry '%d' Strings ARRAY is empty.\n",index);
		XSRETURN_UNDEF;
	      }

	      /*
		Calculate the length of the structure, which means
		figuring out the length of the longest string.
	       */
	      MaxStringLength = 0;
	      
	      for ( subindex = 0 ; subindex <= av_len(StringsAV) ; subindex++ ) {
		svp = av_fetch(StringsAV,subindex,0);
		if ( svp == NULL || !SvPOK(*svp) ) {
		  warn("MQEncodePCF: ParameterList entry '%d' Strings entry '%d' is not a string.\n",
		       index,subindex);
		  XSRETURN_UNDEF;
		}
		String = SvPV(*svp,StringLength);
		if ( StringLength > MaxStringLength ) 
		  MaxStringLength = StringLength;
	      }

	      StrucLength = MQCFSL_STRUC_LENGTH_FIXED +
		(MaxStringLength * (av_len(StringsAV) + 1));
	      if ( StrucLength % 4 )
		StrucLength += 4 - (StrucLength%4);

	      /*
		Now allocate the memory for the MQCFSL, and populate
		it with data
	      */
	      if ( ( pStringParamList = (MQCFSL *)malloc(StrucLength) ) == NULL ) {
		perror("Unable to allocate memory");
		XSRETURN_UNDEF;
	      }

	      pStringParamList->Type = MQCFT_STRING_LIST;
	      pStringParamList->StrucLength = StrucLength;
	      pStringParamList->Parameter = Parameter;
	      pStringParamList->CodedCharSetId = CodedCharSetId;
	      pStringParamList->Count = av_len(StringsAV) + 1;
	      pStringParamList->StringLength = MaxStringLength;

	      pString = (char *)pStringParamList->Strings;
	      memset(pString,'\0',StrucLength - MQCFSL_STRUC_LENGTH_FIXED);

	      for ( subindex = 0 ; subindex <= av_len(StringsAV) ; subindex++ ) {
		svp = av_fetch(StringsAV,subindex,0);
		String = SvPV(*svp,StringLength);
		strncpy(pString,String,StringLength);
		pString += MaxStringLength;
	      }

	      sv_catpvn(ParameterResult, (char *)pStringParamList, StrucLength);
	      free(pStringParamList);
	    
	    }
	    
	  }
	  
	  Result = newSVpv((char *)&Header, Header.StrucLength);
	  sv_catsv(Result,ParameterResult);
	  XPUSHs(sv_2mortal(Result));
	  
	}
