#include <ncbi.h>
#include <../asnlib/asnbuild.h>
#include <gishlib.h>

size_t AsnDeBinReadBufOctets PROTO((AsnIoPtr aip, AsnTypePtr atp, CharPtr PNTR bufp, CharPtr (*falloc)PROTO((Nlm_VoidPtr,CharPtr,size_t,size_t)), size_t PNTR bufmax, VoidPtr ctx));
size_t AsnDeBinReadBufOctets PROTO((AsnIoPtr aip, AsnTypePtr atp, CharPtr PNTR bufp, CharPtr (*falloc)PROTO((Nlm_VoidPtr,CharPtr,size_t,size_t)), size_t PNTR bufmax, VoidPtr ctx));

#define ASNDEBIN_EOF -2
/*****************************************************************************
*
*   ssize_t AsnBinReadBuf(aip, atp, bufp, bufmax, falloc, ctx)
*   	read the value pointed at by atp
*         this should be the same as aip->typestack[aip->type_indent].type
*   	returns START_STRUCT
*               END_STRUCT
*   			1 if ok
*   			0 if an error occured
*   	if (buf == NULL)
*   		just checks that value is of proper type
*
*****************************************************************************/
size_t LIBCALL AsnBinReadBuf(AsnIoPtr aip, AsnTypePtr atp, CharPtr PNTR bufp, size_t PNTR bufmax, CharPtr (*falloc)PROTO((Nlm_VoidPtr,Nlm_CharPtr,size_t,size_t)), VoidPtr ctx)
{
	Int4	used;
	int	isa;
	size_t	retval;
	AsnTypePtr atp2;
	DataVal fake_value;
	AsnTypePtr base_type, curr_type;
	int next_type;
    PstackPtr currpsp, prevpsp;

	currpsp = &aip->typestack[aip->type_indent];
	curr_type = currpsp->type;

	base_type = AsnFindBaseType(atp);   
	if (base_type == NULL) {   /* not found */
		base_type = atp;     
		while (base_type->type != NULL)
			base_type = base_type->type;
        if (base_type->imported) {
            AsnIoErrorMsg(aip,9, atp->name, base_type->name);
			return 0;
		}
		else {
            AsnIoErrorMsg(aip,10, atp->name, base_type->name);
			return 0;
		}
    }
	isa = base_type->type->isa;

	if (ISA_STRINGTYPE(isa))
		isa = GENERALSTRING_TYPE;

	switch (isa) {
	case GENERALSTRING_TYPE:
	case OCTETS_TYPE:
	case STRSTORE_TYPE:
		base_type = AsnFindBaseType(atp);   
		base_type = base_type->type;        /* point at the type itself */

		while ((aip->tagclass != base_type->tagclass) ||
				(aip->tagnumber != base_type->tagnumber)) { /* read to element */
			used = AsnDeBinScanTag(aip);
			if (! AsnDeBinDecr(used, aip))
				return 0;
		}
			
		if (aip->bytes == aip->offset)
			if (! AsnIoReadBlock(aip)) {
				AsnIoErrorMsg(aip, 17);
				return 0;
			}

		switch (isa) {
		case GENERALSTRING_TYPE:
			if (bufp != NULL) {
				retval = AsnDeBinReadBufString(aip, atp, bufp, bufmax, falloc, ctx);
				if (retval != aip->length)
					return 0;
			}
			else
				AsnDeBinSkipString(aip);
			break;
		case OCTETS_TYPE:
		case STRSTORE_TYPE:
			if (bufp != NULL) {
				retval = AsnDeBinReadBufOctets(aip, bufp, bufmax, falloc, ctx);
				if (retval != aip->length)
					return 0;
			}
			else
				AsnDeBinSkipOctets(aip);
			break;
		}
		break;
	default:
		AsnIoErrorMsg(aip, 11, atp->name);
		return 0;
	}

	if (aip->io_failure)
		retval = 0;

  	next_type = aip->type_indent - 1;   /* end any choices */
	if ((next_type >= 0) &&
			(AsnFindBaseIsa(aip->typestack[next_type].type) == CHOICE_TYPE)) {
		while ((next_type >= 0) &&
				(AsnFindBaseIsa(aip->typestack[next_type].type) == CHOICE_TYPE)) {
			currpsp = & aip->typestack[next_type];
			atp2 = currpsp->type;
			if ((curr_type->tagclass != TAG_NONE) &&
					(currpsp->tag_indef)) {
				used = AsnDeBinScanTag(aip);   /* should be 00 for tag */
				if (! AsnDeBinDecr(used, aip))
					return 0;
				if ((aip->length == 0) && (aip->tagclass == 0) &&
						(aip->tagnumber == 0))
					currpsp->tag_indef = FALSE;
				else {
					AsnIoErrorMsg(aip, 3, atp2->name);
					return 0;
				}
			}
			next_type--;
		}
		AsnTypeSetIndent(FALSE, aip, curr_type);
	}

	return retval;
}

size_t
AsnDeBinReadBufString(AsnIoPtr aip, AsnTypePtr atp, CharPtr PNTR bufp, CharPtr (*falloc)PROTO((Nlm_VoidPtr,CharPtr,size_t,size_t)), size_t PNTR bufmax, VoidPtr ctx)
{
	CharPtr	to, from;
	size_t	len, retval, amount;
	int		bytes;

	aip->tagsaved = FALSE;
	len = aip->length;
	AsnDeBinDecr(len, aip);

	if (bufmax == NULL || *bufmax < len + 1) {
		if (falloc == NULL) {
			AsnIoErrorMsg(aip, 14, len, atp->name);
			return 0;
		}
		*bufp = (*falloc)(ctx, *bufp, 0, len + 1);
		if (*bufp == NULL) {
			AsnIoErrorMsg(aip, 14, len + 1, atp->name);
			return 0;
		}
		if (bufmax != NULL)
			*bufmax = len + 1;
	}

	to = *bufp;
	to[retval = len] = NULLB;
	from = (CharPtr)(aip->buf + aip->offset);
	bytes = aip->bytes - aip->offset;
	while (len > 0) {
		if (bytes >= len)
			amount = len;
		else
			amount = bytes;
		Nlm_MemCpy(to, from, amount);
		to += amount;
		len -= amount;
		bytes -= amount;
		if (bytes == 0) {
			bytes = AsnIoReadBlock(aip);
			if (bytes == 0) {
				AsnIoErrorMsg(aip, 17);
				return 0;
			}
			from = (CharPtr)aip->buf;
		}
	}
	aip->offset = aip->bytes - bytes;
	return retval;
}

size_t
AsnDeBinReadBufOctets(AsnIoPtr aip, AsnTypePtr atp, CharPtr PNTR bufp, CharPtr (*falloc)PROTO((Nlm_VoidPtr,CharPtr,size_t,size_t)), size_t PNTR bufmax, VoidPtr ctx)
{
	CharPtr	to, from;
	size_t	len, retval, amount;
	int		bytes;

	aip->tagsaved = FALSE;
	retval = len = aip->length;
	AsnDeBinDecr(len, aip);

	if (bufmax == NULL || *bufmax < len) {
		if (falloc == NULL) {
			AsnIoErrorMsg(aip, 14, len, atp->name);
			return 0;
		}
		*bufp = (*falloc)(ctx, *bufp, 0, len);
		if (*bufp == NULL) {
			AsnIoErrorMsg(aip, 14, len, atp->name);
			return 0;
		}
		if (bufmax != NULL)
			*bufmax = len;
	}

	to = *bufp;
	from = (CharPtr)(aip->buf + aip->offset);
	bytes = aip->bytes - aip->offset;
	while (len > 0) {
		if (bytes >= len)
			amount = len;
		else
			amount = bytes;
		Nlm_MemCpy(to, from, amount);
		to += amount;
		len -= amount;
		bytes -= amount;
		if (bytes == 0) {
			bytes = AsnIoReadBlock(aip);
			if (bytes == 0) {
				AsnIoErrorMsg(aip, 17);
				return 0;
			}
			from = (CharPtr)aip->buf;
		}
	}
	aip->offset = aip->bytes - bytes;
	return retval;
}
