/*
** Fed-x 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 Fed-x 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.
*******************************************************************/

/**************************************************************//**
 * \file classes.c
***  The functions in this file generate the C++ code for ENTITY **
***  classes, TYPEs, and TypeDescriptors.                       ***
 **                                                             **/


/* this is used to add new dictionary calls */
/* #define NEWDICT */

#include <stdlib.h>
#include <assert.h>
#include "classes.h"
#include <ordered_attrs.h>

#include "./trace_fprintf.h"

int multiple_inheritance = 1;
int print_logging = 0;
int old_accessors = 0;

/**
 * Turn the string into a new string that will be printed the same as the
 * original string. That is, turn backslash into a quoted backslash and
 * turn \n into "\n" (i.e. 2 chars).
 *
 * Mostly replaced by format_for_std_stringout, below. This function is
 * still used in one place in ENTITYincode_print().
 */
char * format_for_stringout( char * orig_buf, char * return_buf ) {
    char * optr  = orig_buf;
    char * rptr  = return_buf;
    while( *optr ) {
        if( *optr == '\n' ) {
            *rptr = '\\';
            rptr++;
            *rptr = 'n';
        } else if( *optr == '\\' ) {
            *rptr = '\\';
            rptr++;
            *rptr = '\\';
        } else {
            *rptr = *optr;
        }
        rptr++;
        optr++;
    }
    *rptr = '\0';
    return return_buf;
}

/**
 * Like format_for_stringout above, but splits the static string up
 * into numerous small ones that are appended to a std::string named
 * 'str'. It is assumed that this string already exists and is empty.
 *
 * This version takes a file pointer and eliminates use of the temp buffer.
 */
void format_for_std_stringout( FILE * f, char * orig_buf ) {
    const char * optr  = orig_buf;
    char * s_end = "\\n\" );\n";
    char * s_begin = "    str.append( \"";
    fprintf( f, "%s", s_begin );
    while( *optr ) {
        if( *optr == '\n' ) {
            if( * ( optr + 1 ) == '\n' ) { /*  skip blank lines */
                optr++;
                continue;
            }
            fprintf( f, "%s", s_end );
            fprintf( f, "%s", s_begin );
        } else if( *optr == '\\' ) {
            fprintf( f, "\\\\" );
        } else {
            fprintf( f, "%c", *optr );
        }
        optr++;
    }
    fprintf( f, "%s", s_end );
    free( orig_buf );
}

void USEREFout( Schema schema, Dictionary refdict, Linked_List reflist, char * type, FILE * file ) {
    Dictionary dict;
    DictionaryEntry de;
    struct Rename * r;
    Linked_List list;
    char td_name[BUFSIZ+1];
    char sch_name[BUFSIZ+1];

    strncpy( sch_name, PrettyTmpName( SCHEMAget_name( schema ) ), BUFSIZ );

    LISTdo( reflist, s, Schema ) {
        fprintf( file, "        // %s FROM %s; (all objects)\n", type, s->symbol.name );
        fprintf( file, "        is = new Interface_spec(\"%s\",\"%s\");\n", sch_name, PrettyTmpName( s->symbol.name ) );
        fprintf( file, "        is->all_objects_(1);\n" );
        if( !strcmp( type, "USE" ) ) {
            fprintf( file, "        %s::schema->use_interface_list_()->Append(is);\n", SCHEMAget_name( schema ) );
        } else {
            fprintf( file, "        %s::schema->ref_interface_list_()->Append(is);\n", SCHEMAget_name( schema ) );
        }
    } LISTod

    if( !refdict ) {
        return;
    }
    dict = DICTcreate( 10 );

    /* sort each list by schema */

    /* step 1: for each entry, store it in a schema-specific list */
    DICTdo_init( refdict, &de );
    while( 0 != ( r = ( struct Rename * )DICTdo( &de ) ) ) {
        Linked_List wlist;

        wlist = ( Linked_List )DICTlookup( dict, r->schema->symbol.name );
        if( !wlist ) {
            wlist = LISTcreate();
            DICTdefine( dict, r->schema->symbol.name, wlist, NULL, OBJ_UNKNOWN );
        }
        LISTadd_last( wlist, r );
    }

    /* step 2: for each list, print out the renames */
    DICTdo_init( dict, &de );
    while( 0 != ( list = ( Linked_List )DICTdo( &de ) ) ) {
        bool first_time = true;
        LISTdo( list, re, struct Rename * ) {

            /* note: SCHEMAget_name(r->schema) equals r->schema->symbol.name) */
            if( first_time ) {
                fprintf( file, "        // %s FROM %s (selected objects)\n", type, re->schema->symbol.name );
                fprintf( file, "        is = new Interface_spec(\"%s\",\"%s\");\n", sch_name, PrettyTmpName( re->schema->symbol.name ) );
                if( !strcmp( type, "USE" ) ) {
                    fprintf( file, "        %s::schema->use_interface_list_()->Append(is);\n", SCHEMAget_name( schema ) );
                } else {
                    fprintf( file, "        %s::schema->ref_interface_list_()->Append(is);\n", SCHEMAget_name( schema ) );
                }
            }

            if( first_time ) {
                first_time = false;
            }
            if( re->type == OBJ_TYPE ) {
                sprintf( td_name, "%s", TYPEtd_name( ( Type )re->object ) );
            } else if( re->type == OBJ_FUNCTION ) {
                sprintf( td_name, "/* Function not implemented */ 0" );
            } else if( re->type == OBJ_PROCEDURE ) {
                sprintf( td_name, "/* Procedure not implemented */ 0" );
            } else if( re->type == OBJ_RULE ) {
                sprintf( td_name, "/* Rule not implemented */ 0" );
            } else if( re->type == OBJ_ENTITY ) {
                sprintf( td_name, "%s%s%s",
                        SCOPEget_name( ( ( Entity )re->object )->superscope ),
                        ENT_PREFIX, ENTITYget_name( ( Entity )re->object ) );
            } else {
                sprintf( td_name, "/* %c from OBJ_? in expbasic.h not implemented */ 0", re->type );
            }
            if( re->old != re->nnew ) {
                fprintf( file, "        // object %s AS %s\n", re->old->name,
                        re->nnew->name );
                if( !strcmp( type, "USE" ) ) {
                    fprintf( file, "        ui = new Used_item(\"%s\", %s, \"%s\", \"%s\");\n", re->schema->symbol.name, td_name, re->old->name, re->nnew->name );
                    fprintf( file, "        is->explicit_items_()->Append(ui);\n" );
                    fprintf( file, "        %s::schema->interface_().explicit_items_()->Append(ui);\n", SCHEMAget_name( schema ) );
                } else {
                    fprintf( file, "        ri = new Referenced_item(\"%s\", %s, \"%s\", \"%s\");\n", re->schema->symbol.name, td_name, re->old->name, re->nnew->name );
                    fprintf( file, "        is->explicit_items_()->Append(ri);\n" );
                    fprintf( file, "        %s::schema->interface_().explicit_items_()->Append(ri);\n", SCHEMAget_name( schema ) );
                }
            } else {
                fprintf( file, "        // object %s\n", re->old->name );
                if( !strcmp( type, "USE" ) ) {
                    fprintf( file, "        ui = new Used_item(\"%s\", %s, \"\", \"%s\");\n", re->schema->symbol.name, td_name, re->nnew->name );
                    fprintf( file, "        is->explicit_items_()->Append(ui);\n" );
                    fprintf( file, "        %s::schema->interface_().explicit_items_()->Append(ui);\n", SCHEMAget_name( schema ) );
                } else {
                    fprintf( file, "        ri = new Referenced_item(\"%s\", %s, \"\", \"%s\");\n", re->schema->symbol.name, td_name, re->nnew->name );
                    fprintf( file, "        is->explicit_items_()->Append(ri);\n" );
                    fprintf( file, "        %s::schema->interface_().explicit_items_()->Append(ri);\n", SCHEMAget_name( schema ) );
                }
            }
        } LISTod
    }
    HASHdestroy( dict );
}

int Handle_FedPlus_Args( int i, char * arg ) {
    (void) arg; /* unused */
    if( ( ( char )i == 's' ) || ( ( char )i == 'S' ) ) {
        multiple_inheritance = 0;
    }
    if( ( ( char )i == 'a' ) || ( ( char )i == 'A' ) ) {
        old_accessors = 1;
    }
    if( ( ( char )i == 'l' ) || ( ( char )i == 'L' ) ) {
        print_logging = 1;
    }
    return 0;
}

void MODELPrintConstructorBody( Entity entity, FILES * files, Schema schema ) {
    const char * n;
    DEBUG( "Entering MODELPrintConstructorBody for %s\n", n );

    n = ENTITYget_classname( entity );

    fprintf( files->lib, "    eep = new SDAI_Entity_extent;\n" );

    fprintf( files->lib, "    eep->definition_(%s::%s%s);\n", SCHEMAget_name( schema ), ENT_PREFIX, ENTITYget_name( entity ) );
    fprintf( files->lib, "    _folders.Append(eep);\n\n" );
}

void MODELPrint( Entity entity, FILES * files, Schema schema, int index ) {

    const char * n;
    DEBUG( "Entering MODELPrint for %s\n", n );

    n = ENTITYget_classname( entity );
    fprintf( files->lib, "\n%s__set_var SdaiModel_contents_%s::%s_get_extents() {\n", n, SCHEMAget_name( schema ), n );
    fprintf( files->lib, "\n    return (%s__set_var)((_folders.retrieve(%d))->instances_());\n}\n", n, index );
    DEBUG( "DONE MODELPrint\n" )    ;
}

void MODELprint_new( Entity entity, FILES * files ) {
    const char * n;

    n = ENTITYget_classname( entity );
    fprintf( files->inc, "\n    %s__set_var %s_get_extents();\n", n, n );
}
