#ifndef GFUNC_H
#pragma interface
#define GFUNC_H

#include "types.h"
/* Needed because g++ doesn't allow forward ref to enum RealToIntMode. */
#include "gennum.h"

inline Root*
ApplyRest(Root* value, ArgDesc& args, int leftUsed, int rightUsed)
{
    if (args.lCount+args.rCount+args.nCount == leftUsed+rightUsed)
	return value;
    else {
	ArgDesc xargs(args, leftUsed, rightUsed);
	return value->apply(xargs);
    }
}
inline void
ApplyRest(void* dst, Type* dstType, Root* value,
	  ArgDesc& args, int leftUsed, int rightUsed)
{
    if (args.lCount+args.rCount+args.nCount == leftUsed+rightUsed)
	dstType->coerceFromRoot(dst, value);
    else {
	ArgDesc xargs(args, leftUsed, rightUsed);
	value->xapply(dst, dstType, xargs);
    }
}


class GFunction : public Functional {
    DECLARE_MEMBERS(GFunction)
  public:
    struct Function *func;
    Root *env;
    GFunction(Symbol *name, struct Function &f, Root *e = 0) {
	func = &f; env = e;
	name->sym_function(this);
    }
    GFunction(struct Function *f, Root *e = 0) { func = f; env = e; }
    GFunction(struct Function *f) { func = f;}
    GFunction() { func = NULL; }
    virtual void printon(ostream& outs) const;
    virtual void xapply(void* dst, Type* dstType, ArgDesc& args);
    virtual void dumpPtr(CFile *cf) const;
};

class MFunction : public GFunction { // A macro
    DECLARE_MEMBERS(MFunction)
    MFunction(Symbol *name, struct Function &f, Root *e = 0)
	: GFunction(name, f, e) { }
    MFunction(Function *f, Root *e = 0) : GFunction(f, e) { }
    MFunction(Function *f) : GFunction(f) { }
    virtual void dumpPtr(CFile *cf) const;
};

class CurriedFunction : public Functional {
    friend Functional * DoCurry(Functional *, ArgDesc& args);
  protected:
    Functional *saved_Function;
    ArgDesc saved; // saved (curried) arguments
  public:
    CurriedFunction(Functional *function, ArgDesc& args);	
    virtual void printon(ostream& outs) const;
    virtual void xapply(void* dst, Type* dstType, ArgDesc& args);
};

class UpdatingFunction : public Functional {
  public:
    Functional* worker;
    virtual void xapply(void* dst, Type* dstType, ArgDesc& args);
    UpdatingFunction(Functional* w) : worker(w) { }
};

class BinOp : public Functional {
  public:
    char *name;
    int assignable_ok(int argno) const { return 0; } // for now
//  int sequence_ok(int argno); // 0: no; 1: non-series ok; 2: series also ok
    virtual void printon(ostream& outs) const;
    virtual Root *prefix(Root*);
    virtual Root *postfix(Root*);
    virtual Root *infix(Root*, Root*);
    virtual Root *doit(Root*, Root*) const; // return NULL if bad args
    virtual void xapply(void* dst, Type* dstType, ArgDesc& args);
    virtual const Root *rightIdentity() const;
    Functional * reduce() const;
    virtual const StringC *asString(int format=0) const;
    inline Root * operator()(Root *x, Root *y) {return infix(x, y);}
    inline Root * operator()(const Root *x, const Root *y)
      {return infix((Root*)x, (Root*)y);}
};

class Reduction : public Functional {
    int left_to_right;
    Functional *func;
  public:
    Reduction(Functional *f, int lr) { func = f; left_to_right = lr; }
    virtual void printon(ostream& outs) const;
    virtual void xapply(void* dst, Type* dstType, ArgDesc& args);
};

#if 0
class ReducedOp : public BinOp {
    BinOp *baseOp;
  public:
    ReducedOp(BinOp *base) { baseOp = base; }
    virtual void printon(ostream& outs) const;
    virtual Root *infix(Root *larg, Root *rarg);
    virtual Root *postfix(Root *arg);
    virtual const Root *rightIdentity() const;
};
#endif

class InverseOp : public Functional {
    Root *base;
  public:
    InverseOp(Root *b) { base = b; }
    virtual void printon(ostream& outs) const;
    virtual void xapply(void* dst, Type* dstType, ArgDesc& args);
};

class PlusOp : public BinOp {
 public:
    PlusOp(char *n) { name = n; }
    virtual Root *doit(Root*, Root*) const;
    virtual Root *prefix(Root *arg);
    virtual const Root *rightIdentity() const;
};

class TimesOp : public BinOp {
 public:
    TimesOp(char *n) { name = n; }
    virtual Root *doit(Root *, Root *) const;
    virtual const Root *rightIdentity() const;
};

#if 0
class GcdOp : public BinOp {
 public:
    GcdOp(char *n) { name = n; }
    virtual Root *doit(Root *arg1, Root *arg2) const;
    virtual const Root *rightIdentity() const;
    virtual Functional *reduce() const;
};

class ReducedGcdOp : public ReducedOp {
 public:
    ReducedGcdOp(BinOp*);
    virtual Root *postfix(Root *arg);
};
#endif

#if 0
class ConcatOp : public ReducedOp {
  public:
    ConcatOp(BinOp* base)  : ReducedOp(base) { }
    virtual Root *postfix(Root *arg);
};
#endif

class IotaOp : public BinOp {
  public:
    IotaOp(char *n) { name = n;}
    virtual Root *doit(Root *arg1, Root *arg2) const;
    virtual Root *postfix(Root *arg);
};

class BoolOp : public BinOp {
  public:
    int op;
    BoolOp(char *n, int i) { name = n; op = i; }
    virtual Root *doit(Root *arg1, Root *arg2) const;
    virtual void printon(ostream& outs) const;
};

class BitOp : public BinOp {
  public:
    BitOp(char *n) { name = n; }
    virtual Root *prefix(Root *arg);
};

class UnionOp : public BinOp {
  public:
    UnionOp(char *n) { name = n; }
    virtual Root *infix(Root *arg1, Root *arg2);
};

class AssignOp : public BinOp {
  public:
    AssignOp(char *n) { name = n;}
    virtual void xapply(void* dst, Type* dstType, ArgDesc& args);
};

class ShiftAssignOp : public GFunction {
  public:
    ShiftAssignOp() { }
    virtual void printon(ostream& outs) const;
    virtual void xapply(void* dst, Type* dstType, ArgDesc& args);
};

class AllocLogicalOp : public Functional {
  public:
    AllocLogicalOp() { }
    virtual Root *prefix(Root *);
};

//extern MkComplexOp MkComplex;
extern PlusOp Plus;
extern TimesOp Times;
//extern ConcatOp Concatenate;
//extern GcdOp GCD;
//extern ReducedGcdOp ReducedGCD;
extern IotaOp Iota;

extern BitOp Bit;
extern BoolOp BitClr;
extern BoolOp BitAnd;
extern BoolOp BitAndC2;
extern BoolOp Bit1;
extern BoolOp BitAndC1;
extern BoolOp Bit2;
extern BoolOp BitXor;
extern BoolOp BitIor;
extern BoolOp BitNor;
extern BoolOp BitEqv;
extern BoolOp BitC2;
extern BoolOp BitOrC2;
extern BoolOp BitC1;
extern BoolOp BitOrC1;
extern BoolOp BitNand;
extern BoolOp BitSet;

extern ShiftAssignOp ShiftAssignTo;
extern UnionOp Union;
extern AssignOp AssignTo;
#if 0
class CloseOp : public BinOp {
  public:
    CloseOp(char *n) { name = n;}
    virtual Root *postfix(Root *arg);
};
extern CloseOp CloseCmd;
#endif

struct Selector : public Root {
    DECLARE_MEMBERS(Selector)
    virtual void xapply(void*, Type*, ArgDesc&);
    virtual Root *get_it(Root *collection);
    virtual Root *set_it(Root *collection, Root *new_value);
    // also need: get_locative(collection)
};

struct ByteSpec : public Selector {
    DECLARE_MEMBERS(ByteSpec)
    fix_int _pos;
    fix_int _len;
  public:
    fix_int position() const { return _pos; };
    fix_int size() const { return _len; };
    ByteSpec(fix_int l, fix_int p) { _len = l; _pos = p; }
    virtual void printon(ostream& outs) const;
    virtual Root *get_it(Root *collection);
    virtual Root *set_it(Root *collection, Root *new_value);
};

struct BitSpec : public ByteSpec {
    BitSpec(fix_int p) : ByteSpec(1, p) { }
};

struct Binding : public Root {
    Root* _key;
    Root* _val;
    Root* key() const { return _key; }
    Root* val() const { return _val; }
    Binding(Root* k, Root* v) { _key = k; _val = v; }
    virtual void printon(ostream& outs) const;
    virtual void xapply(void* dst, Type* dstType, ArgDesc& args);
};

extern AllocLogicalOp AllocLogicalCmd;
struct Clause;
void ApplyClause(void*, Type*, Clause*, Function*, Root*, ArgDesc&);
extern void CallClause(void* dst, Type* dstType, Clause *clause,
			 struct DisplayEnv *dEnv, void *params);

extern void InsertSpecial(Symbol& name, const Root& value);
extern Root *DoCons(Root *arg1, Root *arg2);
extern Root *DoMinus(Numeric* x, Numeric* y);
extern Root *DoDivide(Numeric* x, Numeric* y);

class Predefined {
  public:
    inline Predefined(Symbol& name, const Root& value)
	{InsertSpecial(name, value);name._flags|=(int)Symbol::is_constant;}
    inline Predefined(Symbol* name, const Root* value)
      {InsertSpecial(*name, *value);name->_flags|=(int)Symbol::is_constant;}
};
#define INSERT_NAMED_BUILTIN(CNAME, QNAME, VALUE) \
	static Predefined pre_##CNAME(QNAME, VALUE);
#define INSERT_BUILTIN(NAME, VALUE) \
	extern Symbol NAME##_Qsym asm("__QB" #NAME);\
	static Predefined pre_##NAME(NAME##_Qsym, VALUE);
//extern Root *SearchSpecial(Symbol * name);
#endif /* GFUNC_H */
