#ifndef COMPLEXSUP_H
#define COMPLEXSUP_H

#include <iostream>
#include <fstream>
using namespace std;

extern "C"
{
#include <string.h>
#include "classes.h"
#include "../express/express.h"
#include "../exppp/exppp.h"
#include "../express/dict.h"
}

#define FALSE      0
#define TRUE       1
#define DONT_KNOW -1
#define LISTEND  999
// LISTEND signifies that an OrList has gone beyond its last viable choice
// among its children.

enum MarkType {
    NOMARK, ORMARK, MARK
};
// MARK is the usual value we'd mark with.  If we mark with ORMARK, it means
// an OrList marked this node, so we'll know it may become unmarked later if
// we try another choice.

enum MatchType {
    UNKNOWN, UNSATISFIED, SATISFIED, MATCHSOME, MATCHALL, NEWCHOICE, NOMORE
};
// These are the conditions the EntList::match() functions may return.  They
// are also the assigned values to EntList::viable.
//
//    UNKNOWN     - The default EntList::viable value - before any matching
//                  has been attempted.
//    UNSATISFIED - EntList had conditions that EntNode did not satisfy (e.g.,
//                  EntList specified A AND B while EntNode only contained an
//                  A).
//    SATISFIED   - EntList had no conditions EntNode did not meet.  But,
//                  EntList did not match (mark) any nodes of EntNode which
//                  were not already matched.
//    MATCHSOME   - EntList matched some of the nodes in EntNode.
//    MATCHALL    - EntList matched all the nodes in EntNode (complex entity
//                  can be instantiated).
//    MORECHOICES - Special case - when trying alternate OR paths, this return
//                  value signifies that we have found another choice.
//    NOMORE      - Special case - when trying alternate OR paths, this return
//                  value signifies that no more alternates within this path.

enum JoinType {
    SIMPLE, AND, OR, ANDOR
};

class SimpleList;
class MultList;
class JoinList;
class AndOrList;
class AndList;
class OrList;
class ComplexList;
class ComplexCollect;

class EntNode {
        friend class SimpleList;
        friend class AndOrList;
        friend class AndList;
        friend class OrList;
        friend class ComplexList;

    public:
        EntNode( const char * nm = "" ) : next( 0 ), mark( NOMARK ),
            multSupers( 0 ) {
            strcpy( name, nm );
        }
        EntNode( char *[] );  // given a list, create a linked list of EntNodes
        ~EntNode() {
            if( next ) {
                delete next;
            }
        }
        operator const char * () {
            return name;
        }
        int operator== ( EntNode & ent ) {
            return ( strcmp( name, ent.name ) == 0 );
        }
        int operator< ( EntNode & ent ) {
            return ( strcmp( name, ent.name ) < 0 );
        }
        int operator> ( EntNode & ent ) {
            return ( strcmp( name, ent.name ) > 0 );
        }
        void setmark( MarkType stamp = MARK ) {
            mark = stamp;
        }
        void markAll( MarkType = MARK );
        void unmarkAll() {
            markAll( NOMARK );
        }
        int marked( MarkType base = ORMARK ) {
            return ( mark >= base );
        }
        int allMarked();  // returns TRUE if all nodes in list are marked
        int unmarkedCount();
        int multSuprs() {
            return multSupers;
        }
        void multSuprs( int j ) {
            multSupers = j;
        }

        EntNode * next;

    private:
        MarkType mark;
        char name[BUFSIZ+1];
        int multSupers;  // do I correspond to an entity with >1 supertype?
};

class EntList {
        friend class MultList;
        friend class JoinList;
        friend class OrList;
        friend class ComplexList;
        friend class ComplexCollect;
        friend ostream & operator<< ( ostream &, EntList & );
        friend ostream & operator<< ( ostream &, MultList & );

    public:
        EntList( JoinType j ) : join( j ), prev( 0 ), next( 0 ), viable( UNKNOWN ),
            level( 0 ) {}
        virtual ~EntList() {}
        MatchType viableVal() {
            return viable;
        }
        virtual void setLevel( int l ) {
            level = l;
        }
        virtual int getMaxLevel() {
            return level;
        }
        virtual int contains( const char * ) = 0;
        virtual int hit( const char * ) = 0;
        virtual int isDependent( const char * ) = 0;
        virtual MatchType matchNonORs( EntNode * ) {
            return UNKNOWN;
        }
        virtual int acceptChoice( EntNode * ) = 0;
        virtual void unmarkAll( EntNode * ) = 0;
        virtual void reset() {
            viable = UNKNOWN;
        }
        int siblings();
        virtual void write( ostream & ) = 0;
        // write out my contents to stream

        // List access functions.  They access desired children based on their
        // join or viable values.  Below is an incomplete list of possible fns,
        // but all we need.
        EntList * firstNot( JoinType );
        EntList * nextNot( JoinType j ) {
            return next->firstNot( j );
        }
        EntList * firstWanted( MatchType );
        EntList * nextWanted( MatchType mat ) {
            return next->firstWanted( mat );
        }
        EntList * lastNot( JoinType );
        EntList * prevNot( JoinType j ) {
            return prev->lastNot( j );
        }
        EntList * lastWanted( MatchType );
        EntList * prevWanted( MatchType mat ) {
            return prev->lastWanted( mat );
        }

        JoinType join;
        int multiple() {
            return ( join != SIMPLE );
        }
        EntList * prev, * next;

    protected:
        MatchType viable;
        // How does this EntList match the complex type.  Used especially if Ent-
        // List's parent is an OrList or AndOrList to record if this child is an
        // acceptable choice or not.  For an AndOr, viable children are accepted
        // right away.  For Or, only one is accepted, but we keep track of the
        // other possible solutions in case we'll want to try them.
        int level;  // How many levels deep are we (main use for printing).
};

class SimpleList : public EntList {
        friend class ComplexList;
        friend ostream & operator<< ( ostream &, SimpleList & );

    public:
        SimpleList( const char * n ) : EntList( SIMPLE ), I_marked( NOMARK ) {
            strcpy( name, n );
        }
        ~SimpleList() {}
        int operator== ( const char * nm ) {
            return ( strcmp( name, nm ) == 0 );
        }
        const char * Name() {
            return name;
        }
        int contains( const char * nm ) {
            return *this == nm;
        }
        int hit( const char * nm ) {
            return *this == nm;
        }
        int isDependent( const char * );
        MatchType matchNonORs( EntNode * );
        int acceptChoice( EntNode * );
        void unmarkAll( EntNode * );
        void reset() {
            viable = UNKNOWN;
            I_marked = NOMARK;
        }
        void write( ostream & );

    private:
        char name[BUFSIZ+1];    // Name of entity we correspond to.
        MarkType I_marked; // Did I mark, and with what type of mark.
};

class MultList : public EntList {
        // Supports concepts and functionality common to all the compound list
        // types, especially AND and ANDOR.

        friend class ComplexList;
        friend class ComplexCollect;
        friend ostream & operator<< ( ostream &, MultList & );

    public:
        MultList( JoinType j ) : EntList( j ), numchildren( 0 ), childList( 0 ) {}
        ~MultList();
        void setLevel( int );
        int getMaxLevel();
        int contains( const char * );
        int hit( const char * );
        int isDependent( const char * );
        void appendList( EntList * );
        EntList * copyList( EntList * );
        void processSubExp( Expression, Entity, ComplexCollect * );
        void addSimpleAndSubs( Entity, ComplexCollect * );
        virtual MatchType matchORs( EntNode * ) = 0;
        virtual MatchType tryNext( EntNode * );

        int childCount() {
            return numchildren;
        }
//  EntList *operator[]( int );
        EntList * getChild( int );
        EntList * getLast() {
            return ( getChild( numchildren - 1 ) );
        }
        void unmarkAll( EntNode * );
        int prevKnown( EntList * );
        void reset();
        void write( ostream & );

    protected:
        int numchildren;
        EntList * childList;
        // Points to a list of "children" of this EntList.  E.g., if join =
        // AND, it would point to a list of the entity types we are AND'ing.
        // The children may be SIMPLE EntLists (contain entity names) or may
        // themselves be And-, Or-, or AndOrLists.
};

class JoinList : public MultList {
        // A specialized MultList, super for subtypes AndOrList and AndList, or
        // ones which join their multiple children.
    public:
        JoinList( JoinType j ) : MultList( j ) {}
        ~JoinList() {}
        void setViableVal( EntNode * );
        int acceptChoice( EntNode * );
};

class AndOrList : public JoinList {
        friend class ComplexList;

    public:
        AndOrList() : JoinList( ANDOR ) {}
        ~AndOrList() {}
        MatchType matchNonORs( EntNode * );
        MatchType matchORs( EntNode * );
};

class AndList : public JoinList {
        friend class MultList;
        friend class ComplexList;
        friend ostream & operator<< ( ostream &, ComplexList & );

    public:
        AndList() : JoinList( AND ), supertype( 0 ) {}
        ~AndList() {}
        int isDependent( const char * );
        MatchType matchNonORs( EntNode * );
        MatchType matchORs( EntNode * );

    private:
        int supertype;  // do I represent a supertype?
};

class OrList : public MultList {
    public:
        OrList() : MultList( OR ), choice( -1 ), choice1( -2 ), choiceCount( 0 ) {}
        ~OrList() {}
        int hit( const char * );
        MatchType matchORs( EntNode * );
        MatchType tryNext( EntNode * );
        void unmarkAll( EntNode * );
        int acceptChoice( EntNode * );
        int acceptNextChoice( EntNode * ents ) {
            choice++;
            return ( acceptChoice( ents ) );
        }
        void reset() {
            choice = -1;
            choice1 = -2;
            choiceCount = 0;
            MultList::reset();
        }

    private:
        int choice, choice1, choiceCount;
        // Which choice of our childList did we select from this OrList; what's
        // the first viable choice; and how many choices are there entirely.
};

class ComplexList {
        // Contains the entire list of EntLists which describe the set of
        // instantiable complex entities defined by an EXPRESS expression.

        friend class MultList;
        friend class ComplexCollect;
        friend ostream & operator<< ( ostream &, ComplexList & );

    public:
        ComplexList( AndList * alist = NULL ) : list( 0 ), head( alist ), next( 0 ),
            abstract( 0 ), dependent( 0 ),
            multSupers( 0 ) {}
        ComplexList( Entity, ComplexCollect * );
        ~ComplexList();
        void buildList();
        void remove();
        int operator< ( ComplexList & c ) {
            return ( strcmp( supertype(), c.supertype() ) < 0 );
        }
        int operator< ( char * name ) {
            return ( strcmp( supertype(), name ) < 0 );
        }
        int operator== ( char * name ) {
            return ( strcmp( supertype(), name ) == 0 );
        }
        const char * supertype() {
            return ( ( SimpleList * )head->childList )->name;
        }
        // Based on knowledge that ComplexList always created by ANDing supertype
        // with subtypes.
        int toplevel( const char * );
        int contains( EntNode * );
        int matches( EntNode * );
        int isDependent( const char * );

        EntNode * list; // List of all entities contained in this complex type,
        // regardless of how.  (Used as a quick way of determining
        // if this List *may* contain a certain complex type.)
        AndList * head;
        ComplexList * next;
        int Dependent() {
            return dependent;
        }
        void write( ostream & );
        int getEntListMaxLevel() {
            return head->getMaxLevel();
        }

    private:
        void addSuper( Entity );
        void addSubExp( Expression );
        void addImplicitSubs( Linked_List, ComplexCollect * );
        void addChildren( EntList * );
        int hitMultNodes( EntNode * );
        int abstract;   // is our supertype abstract?
        int dependent;  // is our supertype also a subtype of other supertype(s)?
        int multSupers; // am I a combo-CList created to test a subtype which has
        int maxlevel;
};                  // >1 supertypes?

class ComplexCollect {
        // The collection of all the ComplexLists defined by the current schema.
    public:
        ComplexCollect( ComplexList * c = NULL ) : clists( c ) {
            count = ( c ? 1 : 0 );
        }
        ComplexCollect( Express );
        ~ComplexCollect() {
            delete clists;
        }
        void insert( ComplexList * );
        void remove( ComplexList * );
        // Remove this list but don't delete its hierarchy structure, because
        // it's used elsewhere.
        ComplexList * find( char * );
        int supports( EntNode * );
        int externMapping( const char * ent ) {
            return ( clists ? clists->isDependent( ent ) : 0 );
        }
        // One of our clists shows that ent will have to be instantiated
        // using external mapping (see Part 21, sect 11.2.5.1).
        void write( const char * );

        ComplexList * clists;

    private:
        int count;  // # of clist children
};

// Standalone function which can be used to print out the complex info in an
// express file (prints out CCollect, CList & EntList instant. statements):
void print_complex( ComplexCollect &, const char * );

#endif
