/*******************************************************************
** FedEx parser output module for generating C++  class definitions
** December  5, 1989
** release 2 17-Feb-1992
** release 3 March 1993
** release 4 December 1993
** K. C. Morris
**
** Development of FedEx was funded by the United States Government,
** and is not subject to copyright.

*******************************************************************
The conventions used in this binding follow the proposed specification
for the STEP Standard Data Access Interface as defined in document
N350 ( August 31, 1993 ) of ISO 10303 TC184/SC4/WG7.
*******************************************************************/

extern int multiple_inheritance;

/**************************************************************************
********    The functions in this file generate C++ code for representing
********    EXPRESS SELECT types.
**************************************************************************/
#include <stdlib.h>
#include "classes.h"
#include <sc_stdbool.h>

bool is_python_keyword( char * word );
int isAggregateType( const Type t );
char * generate_attribute_name( Variable a, char * out );
void ATTRsign_access_methods( Variable a, FILE * file );
char * generate_attribute_func_name( Variable a, char * out );
void ATTRprint_access_methods_get_head( const char * classnm, Variable a, FILE * file );
void ATTRprint_access_methods_put_head( const char * entnm, Variable a, FILE * file );

#define BASE_SELECT "SCLP23(Select)"

#define TYPEis_primitive(t) ( !( TYPEis_entity(t)  || \
                                 TYPEis_select (t) || \
                                 TYPEis_aggregate(t) ) )

#define TYPEis_numeric(t)   ( ((t)->u.type->body->type == real_)    || \
                  ((t)->u.type->body->type == integer_) || \
                  ((t)->u.type->body->type == number_) )
#define PRINT_BUG_REPORT  \
     fprintf( f, "   std::cerr << __FILE__ << \":\" << __LINE__ <<  \":  ERROR" \
              " in schema library:  \\n\" \n\t<< _POC_ << \"\\n\\n\";\n");

#define PRINT_SELECTBUG_WARNING(f) \
     fprintf( (f), "\n   severity( SEVERITY_WARNING );\n" ); \
     fprintf( (f), "   std::cerr << __FILE__ << \":\" << __LINE__ <<  \":  "); \
     fprintf( (f), "WARNING:  possible misuse of\"\n        << \" SELECT "); \
     fprintf( (f), "TYPE from schema library.\\n\";\n"); \
     fprintf( (f), "   Error( \"Mismatch in underlying type.\" );\n" );

#define print_error_msg() \
      fprintf( f, "\n  severity( SEVERITY_BUG );\n" ); \
      PRINT_BUG_REPORT  \
      fprintf( f, "  Error( \"This 'argument' is of the incorrect type\" );\n" );

#define TRUE    1
#define FALSE   0

const char *
SEL_ITEMget_enumtype( Type t ) {
    return StrToUpper( TYPEget_name( t ) );
}


/** FIXME implement for python or remove
 ** \returns type used to represent the underlying type in a select class
 */
const char * TYPEget_utype( Type t )  {
    (void) t; /* unused */
    return NULL;
}

/*******************
LISTmember

determines if the given entity is a member of the list.
RETURNS the member if it is a member; otherwise 0 is returned.
*******************/
Generic
LISTmember( const Linked_List list, Generic e ) {
    Link node;
    for( node = list->mark->next; node != list->mark; node = node->next )
        if( e == node -> data ) {
            return e;
        }
    return ( 0 );
}

/*******************
 compareOrigTypes

 Specialized function to catch if two enumerations, two selects, or two aggrs
 of either, are of the same type.  The issue is that e.g. select B may be a
 rename of sel A (i.e., TYPE B = A;).  Such renamed types are implemented by
 exp2python with typedefs, so that they are in fact the same type.  TYPEget_-
 ctype() is used for most type comparisons and does not consider renamed types
 equivalent.  This function is called in instances when they should be consi-
 dered equivalent.  One such case is the generation of duplicate lists.
 *******************/
static int
compareOrigTypes( Type a, Type b ) {
    Type t, u;

    if( ( TYPEis_select( a ) && TYPEis_select( b ) )
            || ( TYPEis_enumeration( a ) && TYPEis_enumeration( b ) ) ) {
        t = a;
        u = b;
    } else if( TYPEis_aggregate( a ) && TYPEis_aggregate( b ) ) {
        t = TYPEget_base_type( a );
        u = TYPEget_base_type( b );
        if( !( ( TYPEis_select( t ) && TYPEis_select( u ) )
                || ( TYPEis_enumeration( t ) && TYPEis_enumeration( u ) ) ) ) {
            return FALSE;
            /* Only go further with 1D aggregates of sels or enums.  Note that
            // for 2D aggrs and higher we do not continue.  These are all recog-
            // nized to be the same type ("GenericAggregate") by TYPEget_ctype(),
            // and do not have to be handled specially here. */
        }
    } else {
        return FALSE;
    }

    if( TYPEget_head( t ) ) {
        t = TYPEget_ancestor( t );
    }
    if( TYPEget_head( u ) ) {
        u = TYPEget_ancestor( u );
    }
    return ( !strcmp( TYPEget_name( t ), TYPEget_name( u ) ) );
}

/*******************
 utype_member

 determines if the given "link's" underlying type is a member of the list.
        RETURNS the underlying type if it is a member; otherwise 0 is returned.

 If "rename" is TRUE, we also consider check to match in certain cases where
 list already has an item that check is a renaming of (see header comments to
 compareOrigTypes() above).
 *******************/
const char *
utype_member( const Linked_List list, const Type check, int rename ) {
    static char r [BUFSIZ];

    LISTdo( list, t, Type )
    strncpy( r, TYPEget_utype( t ), BUFSIZ );
    if( strcmp( r, TYPEget_utype( check ) ) == 0 ) {
        return r;
    }
    if( rename && compareOrigTypes( check, t ) ) {
        return r;
    }
    LISTod;
    return 0;
}

/**
***  SELgetnew_dmlist (const Type type)
***  Returns a list of types which have unique underlying types
***  The returned list includes all the types which have a data members
***  in the select type.
***
***  The list that is returned needs to be freed by the caller.
***/


Linked_List
SELgetnew_dmlist( const Type type ) {
    Linked_List complete = SEL_TYPEget_items( type );
    Linked_List newlist = LISTcreate();

    LISTdo( complete, t, Type )

    /*     if t\'s underlying type is not already in newlist, */
    if( ! utype_member( newlist, t, 0 ) ) {
        LISTadd_last( newlist, t );
    }

    LISTod;

    return newlist;

}

const char *
SEL_ITEMget_dmtype( Type t, const Linked_List l ) {
    const char * r = utype_member( l, t, 0 );
    return StrToLower( r ? r : TYPEget_utype( t ) );

}


/*******************
duplicate_in_express_list

determines if the given "link's" underlying type is a multiple member
of the list.
    RETURNS 1 if true, else 0.
*******************/
int
duplicate_in_express_list( const Linked_List list, const Type check ) {
    if( TYPEis_entity( check ) ) {
        return FALSE;
    }
    /*  entities are never the same  */

    LISTdo( list, t, Type )
    if( t == check ) {
        ;    /*  don\'t compare check to itself  */
    } else {
        return TRUE;    /* other things in the list conflict  */
    }
    LISTod;
    return FALSE;
}

/*******************
unique_types ( const Linked_List list )

determines if any of the types in a select type resolve to the same
underlying Express type.
RETURNS 1 if true, else 0.
*******************/
int
unique_types( const Linked_List list ) {
    LISTdo( list, t, Type )
    if( duplicate_in_express_list( list, t ) ) {
        return FALSE;
    }
    LISTod;
    return TRUE;
}


/*******************
duplicate_utype_member

determines if the given "link's" C++ representation is used again in the list.
    RETURNS 1 if true, else 0.
*******************/
int
duplicate_utype_member( const Linked_List list, const Type check ) {
    char b [BUFSIZ];

    if( TYPEis_entity( check ) ) {
        return FALSE;
    }
    /*  entities are never the same  */

    LISTdo( list, t, Type )
    if( t == check ) {
        ;
    }
    /*  don\'t compare check to itself  */
    else {   /*  continue looking  */
        strncpy( b, TYPEget_utype( t ), BUFSIZ );
        if( ( !strcmp( b, TYPEget_utype( check ) ) )
                || ( compareOrigTypes( t, check ) ) )
            /*  if the underlying types are the same  */
        {
            return TRUE;
        }
        if( ! strcmp( b, "SCLP23(Integer)" ) &&
                ( ! strcmp( TYPEget_utype( check ), "SCLP23(Real)" ) ) )
            /*  integer\'s and real\'s are not unique  */
        {
            return TRUE;
        }
    }
    LISTod;
    return FALSE;
}

/*******************
any_duplicates_in_select

determines if any of the types in a select type resolve to the same
C++ representation for the underlying Express type.
RETURNS 1 if true, else 0.
*******************/
int
any_duplicates_in_select( const Linked_List list ) {
    LISTdo( list, t, Type )
    if( duplicate_utype_member( list, t ) ) {
        return TRUE;
    }
    LISTod;
    return FALSE;
}

/*******************
find_duplicate_list

finds an instance of each kind of duplicate type found in the given list.
This list is returned as dup_list.  If a duplicate exists, the function
returns TRUE, else FALSE.
list should be unbound before calling, and freed afterwards.
*******************/
int
find_duplicate_list( const Type type, Linked_List * duplicate_list ) {
    Linked_List temp; /** temporary list for comparison **/

    *duplicate_list = LISTcreate();
    if( any_duplicates_in_select( SEL_TYPEget_items( type ) ) ) {
        /**  if there is a dup somewhere  **/
        temp = LISTcreate();
        LISTdo( SEL_TYPEget_items( type ), u, Type )
        if( !utype_member( *duplicate_list, u, 1 ) ) {
            /**  if not already a duplicate  **/
            if( utype_member( temp, u, 1 ) ) {
                LISTadd_first( *duplicate_list, u );
            } else {
                LISTadd_first( temp, u );
            }
        }
        LISTod;
        LISTfree( temp );
        return TRUE;
    }
    return FALSE;
}

/*******************
non_unique_types_string ( const Type type )

returns a string containing the non-unique EXPRESS types deriveable
from a select.  the returned string is in the form (TYPE | TYPE |...)
*******************/
/* In the functions below, we use a vector of ints to count paths in the
   select-graph to base types.  The names in this enum correspond to the
   indices in the vector, i.e., tvec[treal] == tvec[1], and contains the
   number of paths to REAL in the graph
*/

enum __types {
    tint = 0,  /* INTEGER */
    treal,     /* REAL */
    tstring,   /* STRING */
    tbinary,   /* BINARY */
    tenum,     /* ENUMERATION, also LOGICAL, BOOLEAN */
    tselect,   /* SELECT */
    tentity,   /* ENTITY */
    taggr,     /* AGGREGATE, also ARRAY, BAG, SET, LIST */
    tnumber    /* NUMBER */
};

/* This function gets called recursively, to follow a select-graph to its
   leaves.  It passes around the vector described above, to track paths to
   the leaf nodes.
*/
void
non_unique_types_vector( const Type type, int * tvec ) {
    LISTdo( SEL_TYPEget_items( type ), t, Type )
    switch( TYPEget_body( t )->type ) {
        case integer_:
            tvec[tint]++;
            break;
        case real_:
            tvec[treal]++;
            break;
        case string_:
            tvec[tstring]++;
            break;
        case binary_:
            tvec[tbinary]++;
            break;
        case enumeration_:
        case logical_:
        case boolean_:
            tvec[tenum]++;
            break;
        case select_:
            /* SELECT, ergo recurse! */
            non_unique_types_vector( t, tvec );
            break;
        case entity_:
            tvec[tentity]++;
            break;
        case aggregate_:
        case array_:
        case list_:
        case bag_:
        case set_:
            tvec[taggr]++;
            break;
        case number_:
            tvec[tnumber]++;
            break;
        default:
            fprintf( stderr, "Error at %s:%d - type %d not handled by switch statement.", __FILE__, __LINE__, TYPEget_body( t )->type );
            abort();
    }
    LISTod;
}

/* Uses non_unique_types_vector on the select to get a vector of base-type
   reference counts, then uses that to make a string of types, of the form
   (FOO_TYPE | BAR_TYPE | BAZ_TYPE), where FOO, BAR, and BAZ are EXPRESS
   types.  If all types are unique, the string (0) is generated.
*/
char *
non_unique_types_string( const Type type ) {
    int tvec[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
    char * typestr;
    int first = 1;
    int i;

    non_unique_types_vector( type, tvec );

    /* build type string from vector */
    typestr = ( char * )malloc( BUFSIZ );
    typestr[0] = '\0';
    strcat( typestr, ( char * )"(" );
    for( i = 0; i <= tnumber; i++ ) {
        if( tvec[i] < 2 ) {
            continue;    /* skip, this one is unique */
        }

        if( !first ) {
            strcat( typestr, ( char * )" | " );
        } else {
            first = 0;
        }
        switch( i ) {
            case tint   :
                strcat( typestr, ( char * )"sdaiINTEGER" );
                break;
            case treal  :
                strcat( typestr, ( char * )"sdaiREAL" );
                break;
            case tstring:
                strcat( typestr, ( char * )"sdaiSTRING" );
                break;
            case tbinary:
                strcat( typestr, ( char * )"sdaiBINARY" );
                break;
            case tenum  :
                strcat( typestr, ( char * )"sdaiENUMERATION" );
                break;
            case tentity:
                strcat( typestr, ( char * )"sdaiINSTANCE" );
                break;
            case taggr  :
                strcat( typestr, ( char * )"sdaiAGGR" );
                break;
            case tnumber:
                strcat( typestr, ( char * )"sdaiNUMBER" );
                break;
        }
    }
    if( first ) {
        strcat( typestr, ( char * )"0" );
    }
    strcat( typestr, ( char * )")" );
    return typestr;
}



/******************************************************************
 ** Procedure:  ATTR_LISTmember
 ** Parameters:  Linked_List l, Variable check
 ** Returns:  the attribute if an attribute with the same name as "check"
 **  is on the list, 0 otherwise
 ** Description:  checks to see if an attribute is a member of the list
 ** Side Effects:
 ** Status:  26-Oct-1993 done
 ******************************************************************/

Variable
ATTR_LISTmember( Linked_List l, Variable check ) {
    char nm [BUFSIZ];
    char cur [BUFSIZ];

    generate_attribute_name( check, nm );
    LISTdo( l, a, Variable )
    generate_attribute_name( a, cur );
    if( ! strcmp( nm, cur ) ) {
        return check;
    }
    LISTod;
    return ( 0 );
}


/******************************************************************
 ** Procedure:  SEL_TYPEgetnew_attribute_list
 ** Parameters:  const Type type
 ** Returns:  Returns a list of all the attributes for a select type.
 **   The list is the union of all the attributes of the underlying types.
 ** Description:
 ** Side Effects:
***  The list that is returned needs to be freed by the caller.
 ** Status:
 ******************************************************************/

Linked_List
SEL_TYPEgetnew_attribute_list( const Type type ) {
    Linked_List complete = SEL_TYPEget_items( type );
    Linked_List newlist = LISTcreate();
    Linked_List attrs;
    Entity cur;

    LISTdo( complete, t, Type )
    if( TYPEis_entity( t ) ) {
        cur = ENT_TYPEget_entity( t );
        attrs = ENTITYget_all_attributes( cur );
        LISTdo_n( attrs, a, Variable, b )
        if( ! ATTR_LISTmember( newlist, a ) ) {
            LISTadd_first( newlist, a );
        }
        LISTod;
    }
    LISTod;
    return newlist;
}


Linked_List
ENTITYget_expanded_entities( Entity e, Linked_List l ) {
    Linked_List supers;
    Entity super;

    if( ! LISTmember( l, e ) ) {
        LISTadd_first( l, e );
    }

    if( multiple_inheritance ) {
        int super_cnt = 0;
        supers = ENTITYget_supertypes( e );
        LISTdo( supers, s, Entity )
        /* ignore the more than one supertype
           since multiple inheritance isn\'t implemented  */
        if( super_cnt == 0 ) {
            ENTITYget_expanded_entities( s, l );
        }
        ++ super_cnt;
        LISTod;
    } else {
        /* ignore the more than one supertype
        since multiple inheritance isn\'t implemented  */
        super = ENTITYget_superclass( e );
        ENTITYget_expanded_entities( super, l );
    }
    return l;
}

Linked_List
SELget_entity_itemlist( const Type type ) {
    Linked_List complete = SEL_TYPEget_items( type );
    Linked_List newlist = LISTcreate();
    Entity cur;

    LISTdo( complete, t, Type )
    if( TYPEis_entity( t ) ) {
        cur = ENT_TYPEget_entity( t );
        ENTITYget_expanded_entities( cur, newlist );
    }
    LISTod;
    return newlist;

}


/*******************
TYPEselect_lib_print prints the member functions (definitions) of a select
class.
*******************/
void
TYPEselect_lib_print( const Type type, FILE * f ) {
    int nbr_select = 0;
    int num = 0;

    fprintf( f, "# SELECT TYPE %s\n", TYPEget_name( type ) );
    /* create the SELECT */
    if( is_python_keyword( TYPEget_name( type ) ) ) {
        fprintf( f, "%s_ = SELECT(", TYPEget_name( type ) );
    } else {
        fprintf( f, "%s = SELECT(", TYPEget_name( type ) );
    }

    /* first compute the number of types (necessary to insert commas) */
    nbr_select = 0;
    LISTdo( SEL_TYPEget_items( type ), t, Type )
    (void) t; /* unused */
    nbr_select++;
    LISTod;
    /* then write types */
    num = 0;
    LISTdo( SEL_TYPEget_items( type ), t, Type )
    if( is_python_keyword( TYPEget_name( t ) ) ) {
        fprintf( f, "\n\t'%s_'", TYPEget_name( t ) );
    } else {
        fprintf( f, "\n\t'%s'", TYPEget_name( t ) );
    }
    if( num < nbr_select - 1 ) {
        fprintf( f, "," );
    }
    num++;
    LISTod;
    fprintf( f, ",\n\tscope = schema_scope)\n" );
}

#undef BASE_SELECT


/**************************************************************************
********        END  of SELECT TYPE
**************************************************************************/
