/* This is part of the Aw:: Perl module.  A Perl interface to the ActiveWorks(tm) 
   libraries.  Copyright (C) 1999-2000 Daniel Yacob.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with this library; if not, write to the Free
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

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


#include <awadapter.h>
#include <aweb.h>

#include <awxs.h>
#include <awxs.def>

#include "exttypes.h"

#include "EventToHash.h"

extern BrokerError gErr;


BrokerError
awxsSetHashFromEvent ( BrokerEvent event, HV * hv )
{
char **Keys;
int i, numKeys;
SV * sv;
BrokerBoolean isSet;


	gErr = awGetFieldNames (event, NULL, &numKeys, &Keys);

	if ( gErr != AW_NO_ERROR )
		return ( gErr );



	for ( i = 0; i < numKeys; i++ ) {
		/*
		 *  Don't even create a key if the field is unset.
		 */
		gErr = awIsEventFieldSet ( event, Keys[i], &isSet );
		if ( gErr != AW_NO_ERROR )
			break;
		if ( isSet == awaFalse )
			continue;

		sv = getSV ( event, Keys[i] );

		if ( gErr != AW_NO_ERROR )
			break;

		hv_store ( hv, Keys[i], strlen ( Keys[i] ), sv, 0 );
	}

	free ( Keys );

	return ( gErr );

}



SV *
getHV ( BrokerEvent event, char * key )
{
HV * hv;
BrokerEvent newEvent;


	hv = newHV();
	gErr = awGetStructFieldAsEvent ( event, key, &newEvent );

	if ( gErr != AW_NO_ERROR )
		return ( Nullsv );

	awxsSetHashFromEvent ( newEvent, hv );

	free ( newEvent );

	return ( newRV_noinc((SV*) hv) );
}



SV *
_getAV ( BrokerEvent event, char * key, int offset, int max_n )
{
AV * av;
int i, numKeys;
short type;
void * seqValue;
SV * sv;


	av = newAV();
	gErr = awGetSequenceField ( event, key, offset, max_n, &type, &numKeys, &seqValue );

	if ( gErr != AW_NO_ERROR ) {
		warn ( "ERROR %s", awErrorToCompleteString ( gErr ) );
		return ( Nullsv );
	}

  	for ( i = 0; i < numKeys; i++ ) {
		sv = getValueI ( type, seqValue, i );
		av_push( av, sv );
	}

	free ( seqValue );

	return ( newRV_noinc((SV*) av) );
}



SV *
getSV ( BrokerEvent event, char * key )
{
SV * sv;
short type;
void * value;


	gErr = awGetEventFieldType ( event, key, &type );

	if ( gErr != AW_NO_ERROR )
		return ( Nullsv );

	if ( type == FIELD_TYPE_SEQUENCE )
		return ( getAV( event, key ) );

	if ( type == FIELD_TYPE_STRUCT )
		return ( getHV( event, key ) );

	gErr = awGetField ( event, key, &type, &value );

	if ( gErr != AW_NO_ERROR )
		return ( Nullsv );

	sv = getValue ( type, value );

	free ( value );

	return ( sv );
}



SV *
_getValue ( short type, void * value, int i, bool array )
{
SV * sv;

	switch ( type )
	  {
		/*
		 *  NOTE:  If there is later a problem using the void *
		 *  then the awGet<type>Field functions can be used in
		 *  these cases.
		 *
		 */
		case FIELD_TYPE_BOOLEAN:
			sv = boolSV ( ((BrokerBoolean *)value)[i] );
			break;

		case FIELD_TYPE_BYTE:
			sv = newSViv ( (IV)((BrokerByte*)value)[i]  );
			// sv = newSViv ( *(IV*)((BrokerByte*)value+i)  );
			break;

		case FIELD_TYPE_CHAR:
	  		sv = newSVpv ( (char*)value+i, 1 );
			break;

		case FIELD_TYPE_INT:
			sv = newSViv ( *(IV*)((int*)value+i)  );
			// sv = newSViv ( *((IV*)(&((int*)value)[i])) ); // indentical
			break;

		case FIELD_TYPE_LONG:
			{
			char blString[24];
	  		sv = ll_from_longlong ( longlong_from_string ( awBrokerLongToString( *((BrokerLong*)value), blString ) ) );
			}
			break;

		case FIELD_TYPE_SHORT:
			sv = sv_newmortal();
	  		sv_setuv ( sv, (UV)(((short *)value)[i]) );
			SvREFCNT_inc(sv);
			break;

		case FIELD_TYPE_DATE:
			sv = sv_newmortal();
			{
			BrokerDate * bd = (BrokerDate *)safemalloc ( sizeof (BrokerDate) );
			memcpy ( bd, ((BrokerDate*)value+i), sizeof(BrokerDate) );
			sv_setref_pv( sv, "Aw::Date", (void*)bd );
			}
			SvREFCNT_inc(sv);
			break;

		case FIELD_TYPE_DOUBLE:
			sv = newSVnv ( *((double*)value+i)  );
			break;

		case FIELD_TYPE_FLOAT:
			sv = newSVnv ( *((float*)value+i)  );
			break;

		case FIELD_TYPE_STRING:
			if (array)
	  			sv = newSVpv ( *(((char**)value)+i), 0 );
	  			// sv = newSVpv ( *(&((char**)value)[i]), 0 ); identical
			else
	  			sv = newSVpv ( (char*)value, 0 );
			break;

		case FIELD_TYPE_UNICODE_STRING:
			{
			char * utf8St;
			if (array)
				utf8St = awUCtoUTF8 ( ((charUC**)value)[i] );
			else
				utf8St = awUCtoUTF8 ( (charUC*)value );
  			sv = newSVpv ( utf8St, 0 );
			}
			break;

		case FIELD_TYPE_STRUCT:
			{
			HV * hv = newHV();
			awxsSetHashFromEvent ( ((BrokerEvent*)value)[i], hv );
			sv = newRV_noinc((SV*) hv);
			}
			break;

		case FIELD_TYPE_UNICODE_CHAR:
			{
			char * utf8Ch;
			charUC ucCh[2];
			ucCh[0] = ((charUC*)value)[i];
			ucCh[1] = (charUC)NULL;
			utf8Ch  = awUCtoUTF8 ( ucCh );
	  		sv = newSVpv ( utf8Ch, 0 );
			}
			break;
	  }


	return ( sv );
}