/*
 * treg.cc
 *
 * Ian Soboroff, NIST
 * June, 1994
 *
 * This test uses a registry, instance manager, and STEPfile.  The
 * registry keeps dictionary information on the schema, such as lists and
 * reference counts of defined types, entity types, etc.  The instance
 * manager is used to keep track of instantiated entities.  This program may
 * serve as a basic tutorial... for more information, see NISTIR 4937,
 * Validation Testing System: Reusable Software Component Design, by Morris,
 * Sauder, and Ressler.
 *
 * treg can take an optional argument, a filename.  If given, the STEPfile
 * is written to this filename at program completion, for importing by
 * another program.
 *
 */

/* A switch for tests.h, because we don't need to schema header file */
#define DONT_NEED_HEADER
#include "tests.h"


// This function, given a pointer to an entity, will populate it, that is,
// put data values in its attributes.  PopulateEntity doesn't care what
// attributes your entity has.  It goes through them, one at a time, checks
// their type, and puts an appropriate random value in.
void PopulateEntity( STEPentity * ent ) {
    int attrCount = ent->AttributeCount();
    cout << "Populating " << ent->EntityName() << " which has ";
    cout << attrCount << " attributes." << endl;

    ent->ResetAttributes();    // start us walking at the top of the list

    STEPattribute * attr = ent->NextAttribute();
    while( attr != 0 ) {
        const AttrDescriptor * attrDesc = attr->aDesc;
        cout << "  attribute " << attrDesc->Name();
        cout << " [" << attrDesc->TypeName() << "] = ";
        int needOutput = 1;  // true if we need to output the value
        // that is, if it's anything but 'none'

        // Here's how we do this... set up a string stream to put the value
        // into, because STEPattribute has this StrToVal() function to put
        // a string value as the value of the attribute.  Then, depending on
        // the type of the attribute, put something nearly appropriate in.
        ostringstream valstr;
        switch( attrDesc->NonRefType() ) {
            case INTEGER_TYPE: // for these types, just put in a random number
            case REAL_TYPE:    // from 0-99.
            case NUMBER_TYPE:
                cout << "(int/real/num) ";
                valstr << rand() % 100;
                break;
            case STRING_TYPE:  // for strings, put in the name of the entity,
                cout << "(string) ";    // followed by a number from 1 to 10.
                valstr << ent->EntityName() << rand() % 10 + 1;
                break;
            case ENUM_TYPE:    // for enumerations, put a random legal value.
            case BOOLEAN_TYPE: // the trick here is that the value needs to be
            case LOGICAL_TYPE: { // the word, not the int value, because of StrToVal
                cout << "(enum/bool/logi) ";
                STEPenumeration * se = attr->ptr.e; // grab the enumeration...
                valstr << se->element_at( rand() % se->no_elements() );
            }
            break;
            default:   // for other stuff like aggregates and selects, just leave
                cout << "none (" << attrDesc->NonRefType();  // 'em blank...
                cout << ")" << endl;
                needOutput = 0;
        }
        valstr << ends;  // flush and null-terminate the stream
        /*** char *val = valstr.str(); ***/  // fix stream into char* string
        char * val = &( valstr.str()[0] );
        if( needOutput ) {
            cout << val << endl;
        }
        attr->StrToVal( val ); // and assign

        attr = ent->NextAttribute();
    }
}


int main( int argc, char * argv[] ) {
    int using_outfile = 0;

    if( argc > 2 ) {
        cout << "Syntax:   treg [filename]" << endl;
        exit( 1 );
    } else if( argc > 1 ) {
        using_outfile = 1;    // output filename is in argc[1]
    }

    // This has to be done before anything else.  This initializes
    // all of the registry information for the schema you are using.
    // The SchemaInit() function is generated by exp2cxx... see
    // extern statement above.

    Registry * registry = new Registry( SchemaInit );

    // The nifty thing about the Registry is that it basically keeps a list
    // of everything in your schema.  What this means is that we can go
    // through the Registry and instantiate, say, one of everything, without
    // knowing at coding-time what entities there are to instantiate.  So,
    // this test could be linked with other class libraries produced from
    // other schema, rather than the example, and run happily.

    InstMgr instance_list;
    STEPfile * sfile = new STEPfile( *registry, instance_list );

    // The STEPfile is actually an object that manages the relationship
    // between what's instantiated in the instance manager, and how that
    // information gets passed to the outside, e.g., a file on disk.

    // Here's what's going to happen below: we're going to figure out
    // how many different entities there are, instantiate one of each and
    // keep an array of pointers to each.  We'll stick some random data in
    // them as we go.  When we're done, we'll print out everything by walking
    // the array, and then write out the STEPfile information to the screen.

    // Figure outhow many entities there are, then allocate an array
    // to store a pointer to one of each.

    int num_ents = registry->GetEntityCnt();
    STEPentity ** SEarray = new STEPentity*[num_ents];

    // "Reset" the Schema and Entity hash tables... this sets things up
    // so we can walk through the table using registry->NextEntity()

    registry->ResetSchemas();
    registry->ResetEntities();

    // Print out what schema we're running through.

    const SchemaDescriptor * schema = registry->NextSchema();
    cout << "Building entities in schema " << schema->Name() << endl;

    // "Loop" through the schema, building one of each entity type.

    const EntityDescriptor * ent;  // needs to be declared const...
    for( int i = 0; i < num_ents; i++ ) {
        ent = registry->NextEntity();
        cout << "  Building entity " << ent->Name() << endl;

        // Build object, using its name, through the registry
        SEarray[i] = registry->ObjCreate( ent->Name() );

        // Add each realized entity to the instance list
        instance_list.Append( SEarray[i], completeSE );

        // Put some data into each instance
        PopulateEntity( SEarray[i] );
    }

    // Print out all entities

    SEarrIterator SEitr( ( const STEPentity ** ) SEarray, num_ents );
    // the above cast is needed because the SEarrIterator
    // constructor takes a const entity array pointer argument

    cout << endl << "Here are the entities instantiated, via the SEarray:";
    cout << endl;
    for( SEitr = 0; !SEitr; ++SEitr ) {
        SEitr()->STEPwrite( cout );
    }

    cout << endl << "Here are all the entities via the STEPfile:" << endl;
    sfile->WriteExchangeFile( cout );

    if( using_outfile ) {
        cout << "\nWriting STEPfile to output file " << argv[1] << endl;
        ofstream step_out( argv[1] );
        sfile->WriteExchangeFile( step_out );
    }
    exit( 0 );
}

