#ifndef COMPLEXSUP_H
#define COMPLEXSUP_H

/*****************************************************************************
 * complexSupport.h                                                          *
 *                                                                           *
 * Description: Slightly simplified version of fedex_plus's complexSupport.h.*
 *              This version works with the SCL and is used to validate      *
 *              complex entities (user requests to build them).  It does not *
 *              contain info for building these structures from a fedex      *
 *              Express object, as the fedex_plus version.                   *
 *                                                                           *
 * Created by:  David Rosenfeld                                              *
 * Date:        5/9/97                                                       *
 *****************************************************************************/

#include <scl_export.h>
#include <iostream>
#include <fstream>
using namespace std;
#include "Str.h"

#define LISTEND 999
/** \def LISTEND
 * signifies that an OrList has gone beyond its last viable choice
 * among its children. FIXME is 999 high enough? Maybe use -1?
 */

enum MarkType {
    NOMARK, ORMARK, MARK
};
/** \enum MarkType
// 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
};
/** \enum MatchType
// 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 SCL_CORE_EXPORT 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 ) {
            StrToLower( nm, name );
        }
        EntNode( const char ** );                ///< given a list, create a linked list of EntNodes
        ~EntNode() {
            if( next ) {
                delete next;
            }
        }
        operator const char * () {
            return name;
        }
        bool operator== ( EntNode & ent ) {
            return ( strcmp( name, ent.name ) == 0 );
        }
        bool operator< ( EntNode & ent ) {
            return ( strcmp( name, ent.name ) < 0 );
        }
        bool operator> ( EntNode & ent ) {
            return ( strcmp( name, ent.name ) > 0 );
        }
        EntNode & operator= ( EntNode & ent );
        void Name( const char * nm ) {
            strncpy( name, nm, BUFSIZ - 1 );
        }
        const char * Name() {
            return name;
        }
        void setmark( MarkType stamp = MARK ) {
            mark = stamp;
        }
        void markAll( MarkType = MARK );
        void unmarkAll() {
            markAll( NOMARK );
        }
        bool  marked( MarkType base = ORMARK ) {
            return ( mark >= base );
        }
        bool allMarked();  ///< returns true if all nodes in list are marked
        int  unmarkedCount();
        bool  multSuprs() {
            return multSupers;
        }
        void multSuprs( bool j ) {
            multSupers = j;
        }
        void sort( EntNode ** );

        EntNode * next;

    private:
        MarkType mark;
        char name[BUFSIZ];
        bool multSupers;  ///< do I correspond to an entity with >1 supertype?
        EntNode * lastSmaller( EntNode * ); ///< used by ::sort()
};

class SCL_CORE_EXPORT 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 ), next( 0 ), prev( 0 ), viable( UNKNOWN ),
            level( 0 ) {}
        virtual ~EntList() {}
        MatchType viableVal() {
            return viable;
        }
        virtual void setLevel( int l ) {
            level = l;
        }
        virtual bool contains( char * ) = 0;
        virtual bool hit( char * ) = 0;
        virtual MatchType matchNonORs( EntNode * ) {
            return UNKNOWN;
        }
        virtual bool acceptChoice( EntNode * ) = 0;
        virtual void unmarkAll( EntNode * ) = 0;
        virtual void reset() {
            viable = UNKNOWN;
        }
        int siblings();

        // 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 * next, *prev;

    protected:
        MatchType viable;
        /** \var 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 SCL_CORE_EXPORT 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;
        }
        bool contains( char * nm ) {
            if( *this == nm ) {
                return true;
            }
            return false;
        }
        bool hit( char * nm ) {
            if( *this == nm ) {
                return true;
            }
            return false;
        }
        MatchType matchNonORs( EntNode * );
        bool acceptChoice( EntNode * );
        void unmarkAll( EntNode * );
        void reset() {
            viable = UNKNOWN;
            I_marked = NOMARK;
        }

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

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

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

    public:
        MultList( JoinType j ) : EntList( j ), supertype( 0 ), numchildren( 0 ),
            childList( 0 ) {}
        ~MultList();
        void setLevel( int );
        bool contains( char * );
        bool hit( char * );
        void appendList( EntList * );
        EntList * copyList( EntList * );
        virtual MatchType matchORs( EntNode * ) = 0;
        virtual MatchType tryNext( EntNode * );

        int childCount() {
            return numchildren;
        }
        EntList * getChild( int );
        EntList * getLast() {
            return ( getChild( numchildren - 1 ) );
        }
        void unmarkAll( EntNode * );
        bool prevKnown( EntList * desc );
        void reset();

    protected:
        int supertype;  ///< do I represent a supertype?
        int numchildren;
        EntList * childList;
        /** \var 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
 * A specialized MultList, super for subtypes AndOrList and AndList, or
 * ones which join their multiple children.
 */
class SCL_CORE_EXPORT JoinList : public MultList {
    public:
        JoinList( JoinType j ) : MultList( j ) {}
        ~JoinList() {}
        void setViableVal( EntNode * );
        bool acceptChoice( EntNode * ents );
};

class SCL_CORE_EXPORT AndOrList : public JoinList {
        friend class ComplexList;

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

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

    public:
        AndList() : JoinList( AND ) {}
        ~AndList() {}
        MatchType matchNonORs( EntNode * );
        MatchType matchORs( EntNode * );
};

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

    private:
        int choice;      ///< Which choice of our childList did we select from this OrList
        int choice1;     ///< what's the first viable choice
        int choiceCount; ///< 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.
 */
class SCL_CORE_EXPORT ComplexList {
        friend class ultList;
        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();
        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;
        }
        /** \fn supertype
         * Based on knowledge that ComplexList always created by ANDing supertype
         * with subtypes.
         */
        bool toplevel( const char * name );
        bool contains( EntNode * ents );
        bool matches( EntNode * );

        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;
        }

    private:
        void addChildren( EntList * );
        bool hitMultNodes( EntNode * ents );
        int abstract;   ///< is our supertype abstract?
        int dependent;  ///< is our supertype also a subtype of other supertype(s)?
        bool multSupers; ///< am I a combo-CList created to test a subtype which has >1 supertypes?
};

/// The collection of all the ComplexLists defined by the current schema.
class SCL_CORE_EXPORT ComplexCollect {
    public:
        ComplexCollect( ComplexList * c = NULL ) : clists( c ) {
            count = ( c ? 1 : 0 );
        }
        ~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 * );
        bool supports( EntNode * ) const;

        ComplexList * clists;

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

#endif
