#ifndef GVARS_H
#define GVARS_H
#pragma interface
#include "genob.h"

#define LVarPlain 1
#define LVarChain 0

#ifndef DO_BACKTRACK
struct OrContextLink {
    struct OrContext *context;
    struct OrContextLink *next;
    int variant();
    int contextId();
    OrContextLink() { }
    OrContextLink(OrContextLink& old) { context = old.context; }
};

struct OrContext : public OrContextLink {
    int _variant;
    int _contextId;
    OrContext();
    OrContext *parent() { return next->context; } // Or: (OrContext*)next
};

inline int OrContextLink::variant() { return context->_variant; }
inline int OrContextLink::contextId() { return context->_contextId; }

extern OrContext *CurrentOrContext;
//extern LEnvironment *CurrentEnvironment;
//inline LEnvironment::LEnvironment() { parent = CurrentEnvironment; }
#endif

class LDependency; class GenFile; class Combination; class StackIterator;
extern void UndoBindings(union _Undo_ *limit) asm("_UndoBindings");

class LVariable : public Root { // Logic variables - abstract class
    DECLARE_MEMBERS(LVariable)
  public:
#ifndef DO_BACKTRACK
    OrContext *__env;
    OrContext*& lenviron() { return __env; }
    LVariable() { __env = CurrentOrContext; }
#endif
    virtual varflags() { return 0; }
    virtual void dumpPtr(CFile *cf) const;
    virtual Root *value(); // NULL if unbound, value otherwise
    virtual void xapply(void* dst, Type* dstType, ArgDesc& args);
    virtual LVariable * lvariable() { return this; }
    virtual Assignable * assignable() const;
    virtual Numeric* numeric();
    inline const Numeric *numeric() const /* Is this needed for g++? */
	{ return (const Numeric*)((Root*)this)->numeric(); }
    virtual GenMap *mapping();
    virtual GenSeq *sequence() const;
    virtual Functional * functional();
    virtual GenFile *file();
    inline int plainLinked() { return varflags() & LVarPlain; }
    inline int chainLinked() { return !(varflags() & LVarPlain); }
    virtual const StringC *asString(int format=0) const;
    virtual long magic() const;
    virtual void assign(Root *new_value);
};

class PVariable : public LVariable { // Prolog-like pointer implementation
  public:
    Root *val;
    virtual Root *value();
    virtual void unify(Root&);
    virtual void printon(ostream&) const;
    PVariable();
};

class NamedPVariable : public PVariable {
  public:
    Symbol * name;
    NamedPVariable(Symbol * n);
    virtual void printon(ostream&) const;
};

class CVariable : public LVariable { // Circular Chain implementation
    DECLARE_MEMBERS(CVariable)
#define UnknownVarMask 1
#define LastVarMask 0x80000000 // marks the "representative"
#define LinkMask ~(UnknownVarMask+LastVarMask)
  public:
    virtual varflags() { return LVarChain; }

    union {
        Root *vl;
	CVariable *lnk;
	long ilink;
    };
    CVariable *getRepresentative();
    virtual Root *value();
    virtual void unify(Root&);
    virtual void printon(ostream&) const;
    CVariable();
    virtual void notify();

    inline Root *val() const { return vl; }
  protected:
    inline int unknown() const { return ilink & UnknownVarMask; }
    inline CVariable *link() const { return (CVariable*)(ilink & LinkMask); }
    inline void setvar(Root *v) { vl = v; }
    void Set(Root *);

    friend void UndoBindings(union _Undo_ *limit);
};

class NamedCVariable : public CVariable {
  public:
    Symbol * name;
    NamedCVariable(Symbol * n);
    virtual void printon(ostream&) const;
};

class InverseVariable : public CVariable {
  public:
    Root *function;
    ArgDesc args;
    virtual void notify();
    InverseVariable(Root *func, ArgDesc& _args) {
	_args.copy_to(args); function = func; }
};

class Combination : public CVariable {
    DECLARE_MEMBERS(Combination)
  public:
    int ndeps;
    LDependency *dep;
    Combination() : CVariable() {}
    virtual void recalculate(LDependency*);
};

class LDependency : public CVariable {
    Combination *_combination;
  public:
    LDependency() { }
    LDependency(Combination *, Root *);
    virtual void notify();
    inline Combination *combination() { return _combination; }
};

class Choice : public Combination {
    DECLARE_MEMBERS(Choice)
  public:
    int nchoices() const { return ndeps; }
    virtual void printon(ostream&) const;
    virtual void recalculate(LDependency*);
    virtual void notify();
#ifdef DO_BACKTRACK
    Choice(Root **vals, int nchoices);
#else
    Choice(Root **vals, OrContext *envs, int nchoices);
    Choice(Root **vals, OrContext **envs, int nchoices);
#endif
    Choice(int nchoices ...);
    virtual void xapply(void* dst, Type* dstType, ArgDesc& args);
    virtual ~Choice() {}
};

class Sum : public Combination {
  public:
    int naddends() const { return ndeps; }
    const Numeric *constant;
    virtual void printon(ostream&) const;
    virtual void recalculate(LDependency*);
    virtual void notify();
    Sum(const Numeric *constant, int naddends ...);
    virtual ~Sum() {}
};

class Product : public Combination {
  public:
    int nfactors() const { return ndeps; }
    const Numeric *constant;
    virtual void printon(ostream&) const;
    virtual void recalculate(LDependency*);
    virtual void notify();
    Product(const Numeric *constant, int nfactors ...);
    virtual ~Product() {}
};

class Concatenation : public Combination {
    void notifyRest(StackIterator&, int, int);
  public:
    int nSegs;
    int nVars() { return ndeps; }
    Root **segments;
    int knownLength, knownVars; // do not reorder without fixing recalculate()
    virtual void printon(ostream&) const;
    Concatenation();
    Concatenation(int, ...);
    virtual void recalculate(LDependency*);
    virtual void notify();
};

class UnknownTuple : public Combination {
  public:
    LVariable *lmap;
    UnknownTuple(LVariable * v) { lmap = v; }
    virtual void printon(ostream&) const;
};

extern "C" LVariable *AllocVariable(Symbol * name);

extern Root *MakeTuple(Root *val);

#define MAX_TUPLE_INLINE 2
class Tuple : public Root {
    DECLARE_MEMBERS(Tuple)
  public:
    size_t size;
    Root *head[MAX_TUPLE_INLINE]; // First two values.
    const GenMap *val;
    Tuple(size_t count, const GenMap *v=NULL) : val(v) { size = count; }
    Tuple(Root *val1, Root *val2) : val(NULL) {
	size = 2; head[0] = val1; head[1] = val2; }
    Tuple(GenSeq *v);
    Tuple(size_t count, Root **vals);
    virtual void printon(ostream &) const;
    virtual void xapply(void* dst, Type* dstType, ArgDesc& args);
//    virtual Root *value(); // First value.
};

extern Tuple EmptyTuple;

#endif /*GVARS_H*/
