/*
    		 Crown Copyright (c) 1997
    
    This TenDRA(r) Computer Program is subject to Copyright
    owned by the United Kingdom Secretary of State for Defence
    acting through the Defence Evaluation and Research Agency
    (DERA).  It is made available to Recipients with a
    royalty-free licence for its use, reproduction, transfer
    to other parties and amendment for any purpose not excluding
    product development provided that any such use et cetera
    shall be deemed to be acceptance of the following conditions:-
    
        (1) Its Recipients shall ensure that this Notice is
        reproduced upon any copies or amended versions of it;
    
        (2) Any amended version of it shall be clearly marked to
        show both the nature of and the organisation responsible
        for the relevant amendment or amendments;
    
        (3) Its onward transfer from a recipient to another
        party shall be deemed to be that party's acceptance of
        these conditions;
    
        (4) DERA gives no warranty or assurance as to its
        quality or suitability for any purpose and DERA accepts
        no liability whatsoever in relation to any use to which
        it may be put.
*/


#include "implement.h"
#include "interface.h"
#include <cstdio>
#include <cstring>
#include <typeinfo>
using namespace std ;


/*
    CHECK FOR EQUAL TYPES

    This routine returns true if the types represented by t and s are
    equal.
*/

static int is_equal_type ( TYPE_INFO *t, TYPE_INFO *s )
{
    if ( t == s ) return ( 1 ) ;
    int c = t->code ;
    if ( c == s->code ) {
	// Type categories match
	switch ( c ) {
	    case RTTI_ptr :
	    case RTTI_ref :
	    case RTTI_ptr_mem :
	    case RTTI_array :
	    case RTTI_bitfield :
	    case RTTI_func :
	    case RTTI_c_func : {
		// Examine sub-types
		BASE_INFO *bt = t->base ;
		BASE_INFO *bs = s->base ;
		while ( bt && bs ) {
		    if ( bt->access != bs->access ) return ( 0 ) ;
		    if ( bt->virt != bs->virt ) return ( 0 ) ;
		    t = bt->rtti ;
		    s = bs->rtti ;
		    if ( !is_equal_type ( t, s ) ) return ( 0 ) ;
		    bt = bt->next ;
		    bs = bs->next ;
		}
		if ( bt || bs ) return ( 0 ) ;
		return ( 1 ) ;
	    }
	    case RTTI_class :
	    case RTTI_union :
	    case RTTI_enum : {
		// Examine type names
		if ( strcmp ( t->name, s->name ) == 0 ) return ( 1 ) ;
		break ;
	    }
	    default : {
		// Built-in types have unique representative
		break ;
	    }
	}
    }
    return ( 0 ) ;
}


/*
    CHECK FOR A BASE CLASS

    This routine returns 1 if the type represented by s is a base class
    of the type represented by t and 2 if the two types are actually
    equal.
*/

static int is_base_class ( TYPE_INFO *t, TYPE_INFO *s )
{
    if ( is_equal_type ( t, s ) ) return ( 2 ) ;
    if ( t->code == RTTI_class ) {
	for ( BASE_INFO *b = t->base ; b ; b = b->next ) {
	    // Check base classes
	    if ( is_base_class ( b->rtti, s ) ) return ( 1 ) ;
	}
    }
    return ( 0 ) ;
}


/*
    UNIQUE POINTERS

    The following unique pointers are used in do_base_cast.
*/

static void *error_ptr = ( void * ) &error_ptr ;
static void *null_ptr = ( void * ) &null_ptr ;


/*
    PERFORM A BASE CLASS CONVERSION

    This routine converts the pointer p from the type represented by t
    to the type represented by s.  The error pointer is returned for
    ambiguous conversions while a keeps track of the base class access.
*/

static void *do_base_cast ( void *p, TYPE_INFO *t, TYPE_INFO *s, int &a )
{
    void *r = NULL ;
    int acc = 0 ;
    if ( is_equal_type ( t, s ) ) {
	r = p ;
    } else {
	// Check base classes
	for ( BASE_INFO *b = t->base ; b ; b = b->next ) {
	    int c = b->access ;
	    void *q = p ;
	    if ( q != null_ptr ) {
		q = ( void * ) ( ( char * ) p + b->off ) ;
		if ( b->virt ) q = *( ( void ** ) q ) ;
	    }
	    q = do_base_cast ( q, b->rtti, s, c ) ;
	    if ( q ) {
		if ( r == NULL ) {
		    // First successful conversion
		    r = q ;
		    acc = c ;
		} else if ( r == q ) {
		    // Select most accessible successful conversion
		    if ( c < acc ) acc = c ;
		} else {
		    // Ambiguous conversion
		    r = error_ptr ;
		    return ( r ) ;
		}
	    }
	}
    }
    a += acc ;
    return ( r ) ;
}


/*
    PERFORM AN UNAMBIGUOUS BASE CLASS CONVERSION

    This routine is identical to do_base_cast except that it returns the
    null pointer for ambiguous or inaccessible conversions.
*/

static void *do_valid_base_cast ( void *p, TYPE_INFO *t, TYPE_INFO *s )
{
    int acc = INFO_public ;
    p = do_base_cast ( p, t, s, acc ) ;
    if ( p == error_ptr || acc != INFO_public ) p = NULL ;
    return ( p ) ;
}


/*
    PERFORM A DYNAMIC CAST

    This routine is used to implement the dynamic cast construct.  p gives
    a pointer to the virtual function table pointer for the construct
    operand and s gives the type information for the target type.
*/

void *__TCPPLUS_dynamic_cast ( VTABLE **p, TYPE_INFO *s )
{
    void *r = NULL ;
    VTABLE *vptr = *p ;
    TYPE_INFO *t = vptr->extra.rtti ;
    int c = is_base_class ( t, s ) ;
    if ( c ) {
	// Perform the base class conversion
	r = ( void * ) p ;
	r = ( void * ) ( ( ( char * ) r ) + vptr->extra.off ) ;
	if ( c == 1 ) r = do_valid_base_cast ( r, t, s ) ;
    }
    return ( r ) ;
}


/*
    PERFORM AN EXCEPTION CATCH CONVERSION

    This routine checks whether an exception of type t can be caught
    by a handler of type s.  If so it applies any necessary conversions
    to the exception value p and returns the value.  Otherwise the
    null pointer is returned.  Note that any reference components will
    have been removed from s.  cn is true if all the qualifiers in
    a qualification conversion contain 'const'.  dpt keeps track of
    the depth of pointer conversions.
*/

void *__TCPPLUS_catch_cast
    ( void *p, TYPE_INFO *t, TYPE_INFO *s, int cn, int dpt )
{
    if ( p ) {
	if ( is_equal_type ( t, s ) ) {
	    // Exact match
	    return ( p ) ;
	}
	int c = t->code ;
	if ( c == s->code ) {
	    if ( c == RTTI_ptr || c == RTTI_ref ) {
		// Check for pointer conversions
		BASE_INFO *bt = t->base ;
		BASE_INFO *bs = s->base ;
		int at = bt->access ;
		int as = bs->access ;
		if ( at == as || ( cn && !( at & ~as ) ) ) {
		    // Qualifiers are alright
		    t = bt->rtti ;
		    s = bs->rtti ;
		    if ( dpt == 0 ) {
			if ( s->code == RTTI_void && c == RTTI_ptr ) {
			    switch ( t->code ) {
				case RTTI_func :
				case RTTI_c_func : {
				    // Can't convert function to 'void *'
				    break ;
				}
				default : {
				    // Can convert 't *' to 'void *'
				    return ( p ) ;
				}
			    }
			}
			int b = is_base_class ( t, s ) ;
			if ( b == 2 ) {
			    // Exact base class conversion
			    return ( p ) ;
			}
			if ( b == 1 ) {
			    // Base class conversion
			    void *q = *( ( void ** ) p ) ;
			    if ( q == NULL ) q = null_ptr ;
			    void *r = do_valid_base_cast ( q, t, s ) ;
			    if ( r ) {
				static void *buf = NULL ;
				if ( q == r ) return ( p ) ;
				buf = r ;
				return ( ( void * ) &buf ) ;
			    }
			}
		    }
		    if ( !( at & INFO_const ) ) cn = 0 ;
		    p = __TCPPLUS_catch_cast ( p, t, s, cn, dpt + 1 ) ;
		    return ( p ) ;
		}

	    } else if ( c == RTTI_ptr_mem ) {
		// Check for pointer to member conversion
		BASE_INFO *bt = t->base ;
		BASE_INFO *bs = s->base ;
		if ( is_equal_type ( bt->rtti, bs->rtti ) ) {
		    // Classes match
		    bt = bt->next ;
		    bs = bs->next ;
		    int at = bt->access ;
		    int as = bs->access ;
		    if ( at == as || ( cn && !( at & ~as ) ) ) {
			// Qualifiers are alright
			t = bt->rtti ;
			s = bs->rtti ;
			if ( !( at & INFO_const ) ) cn = 0 ;
			p = __TCPPLUS_catch_cast ( p, t, s, cn, dpt + 1 ) ;
			return ( p ) ;
		    }
		}

	    } else if ( c == RTTI_class ) {
		// Check for base class conversions
		if ( dpt == 0 ) {
		    int b = is_base_class ( t, s ) ;
		    if ( b == 2 ) {
			return ( p ) ;
		    }
		    if ( b == 1 ) {
			p = do_valid_base_cast ( p, t, s ) ;
			return ( p ) ;
		    }
		}
	    }
	}
    }
    return ( NULL ) ;
}


/*
    PRINT RUN-TIME TYPE INFORMATION

    This routine prints the run-time type information t with access a
    and virtual qualifier v at an indentation of i.
*/

static void print_type_info ( TYPE_INFO *t, char *a, char *v, int i )
{
    for ( int j = 0 ; j < i ; j++ ) fputc ( ' ', stdout ) ;
    printf ( "%s%s%s", a, v, t->name ) ;
    BASE_INFO *b = t->base ;
    if ( b && t->code == RTTI_class ) {
	printf ( " = {\n" ) ;
	while ( b ) {
	    switch ( b->access ) {
		case INFO_public : a = "public " ; break ;
		case INFO_protected : a = "protected " ; break ;
		case INFO_private : a = "private " ; break ;
	    }
	    v = const_cast < char * > ( b->virt ? "virtual " : "" ) ;
	    print_type_info ( b->rtti, a, v, i + 4 ) ;
	    b = b->next ;
	}
	for ( int j = 0 ; j < i ; j++ ) fputc ( ' ', stdout ) ;
	fputc ( '}', stdout ) ;
    }
    printf ( " ;\n" ) ;
    return ;
}


/*
    PRINT RUN-TIME TYPE INFORMATION FOR AN OBJECT

    This routine prints the run-time type information for an object given
    a pointer to its virtual function table.
*/

void __TCPPLUS_type_info ( VTABLE *p )
{
    if ( p ) {
	TYPE_INFO *t = p->extra.rtti ;
	print_type_info ( t, "", "", 0 ) ;
    }
    return ;
}


/*
    MEMBER FUNCTIONS FOR TYPE_INFO CLASS

    These routines give the member functions for the type_info class.  They
    exploit the layout equivalent of a type_info and a TYPE_INFO.
*/

type_info::type_info ( const type_info &rhs )
{
    __code = rhs.__code ;
    __name = rhs.__name ;
    __base = rhs.__base ;
    return ;
}

type_info &type_info::operator= ( const type_info &rhs )
{
    __code = rhs.__code ;
    __name = rhs.__name ;
    __base = rhs.__base ;
    return ( *this ) ;
}

type_info::~type_info ()
{
    return ;
}

bool type_info::operator== ( const type_info &rhs ) const
{
    return ( is_equal_type ( ( TYPE_INFO * ) this, ( TYPE_INFO * ) &rhs ) ) ;
}

bool type_info::operator!= ( const type_info &rhs ) const
{
    return ( !( *this == rhs ) ) ;
}

bool type_info::before ( const type_info &rhs ) const
{
    return ( strcmp ( __name, rhs.__name ) < 0 ) ;
}

const char *type_info::name () const
{
    return ( __name ) ;
}
