#include "asn1fix_internal.h"

static int asn1f_check_same_children(arg_t *arg, asn1p_expr_t *a, asn1p_expr_t *b);

/*
 * Check that the expressions given are compatible in their type.
 * ORDER DOES MATTER! (See .h).
 */
int
asn1f_check_type_compatibility(arg_t *arg, asn1p_expr_t *a, asn1p_expr_t *b) {
	asn1p_expr_type_e atype, btype;

	atype = a->expr_type;
	btype = b->expr_type;

	DEBUG("(%s:%x@%d, %s:%x@%d)",
		a->Identifier, atype, a->_lineno,
		b->Identifier, btype, b->_lineno);

	/*
	 * Expected terminal type!
	 */
	assert(atype != A1TC_REFERENCE);
	assert(btype != A1TC_REFERENCE);

	if(a == b)
		return 0;	/* Fairly obviously */

	if(atype != btype) {
		/*
		 * Limited cross-compatibility of integer types.
		 */
		if((atype == A1TC_UNIVERVAL && btype == ASN_BASIC_INTEGER)
		|| (atype == A1TC_UNIVERVAL && btype == ASN_BASIC_ENUMERATED)
		)
			return 0;

		/* Limited cross-compatibility of string types */
		if((atype & ASN_STRING_MASK)
		&& (btype & ASN_STRING_MASK)) {
			/* X.680, B.5 */
			int akm = (atype & ASN_STRING_KM_MASK)
				|| atype == ASN_STRING_UTF8String;
			int bkm = (btype & ASN_STRING_KM_MASK)
				|| btype == ASN_STRING_UTF8String;
			return (akm == bkm) ? 0 : -1;
		}

		DEBUG("\t%s and %s are not compatible",
			a->Identifier, b->Identifier);
		return -1;	/* Fairly obviously */
	}

	switch(atype) {
	case ASN_BASIC_INTEGER:
		/* All integers are compatible, X.680, B.4.5 */
		return 0;
	case ASN_BASIC_ENUMERATED:
		/*
		 * Enumerations are not compatible
		 * unless their definitions are the same.
		 */
		if(asn1f_check_same_children(arg, a, b)) {
			DEBUG("\tEnumerations are different %s and %s",
				a->Identifier, b->Identifier);
			return -1;
		}
		return 0;
	default:
		if((atype & ASN_STRING_MASK)
		&& (btype & ASN_STRING_MASK)) {
			/* String type is compatible with the same type */
			return 0;
		}
		/* Compatibility is not defined yet */
		DEBUG("\tCompatibility rule is not defined for %s and %s",
			a->Identifier, b->Identifier);
		return -1;
	}

	return 0;
}

/*
 * Check that the children are exactly same.
 */
static int
asn1f_check_same_children(arg_t *arg, asn1p_expr_t *a, asn1p_expr_t *b) {
	asn1p_expr_t *achild;
	asn1p_expr_t *bchild;

	achild = TQ_FIRST(&(a->members));
	bchild = TQ_FIRST(&(b->members));

	while(1) {
		if(achild->expr_type != bchild->expr_type)
			return -1;

		if(achild->Identifier && bchild->Identifier) {
			if(strcmp(achild->Identifier, bchild->Identifier))
				return -1;
		} else if(!(!achild->Identifier && !bchild->Identifier)) {
			return -1;
		}

		if(achild->value && bchild->value) {
			if(achild->value->type != bchild->value->type)
				return -1;
			switch(achild->value->type) {
			case ATV_INTEGER:
				if(achild->value->value.v_integer
				!= bchild->value->value.v_integer)
					return -1;
				break;
			case ATV_REFERENCED:
			default:
				DEBUG("Value %s at lines %d and "
					"%d cannot be used in "
					"semantical equality check",
					asn1f_printable_value(achild->value),
					achild->value->value.reference->_lineno,
					bchild->value->value.reference->_lineno
				);
				return -1;
			}
		} else if(!(!achild->value && !bchild->value)) {
			/* One of values is defined, and another is not */
			return -1;
		}

		achild = TQ_NEXT(achild, next);
		bchild = TQ_NEXT(bchild, next);

		if(achild && bchild)
			continue;
		else if(!achild && !bchild)
			break;
		else
			return -1;
	}

	DEBUG("\t%s:%x@%d and %s:%x@%d are semantically equivalent",
		a->Identifier, a->expr_type, a->_lineno,
		b->Identifier, b->expr_type, b->_lineno);

	return 0;
}