#ifndef GASSIGN_H
#pragma interface
#define GASSIGN_H

#include "genob.h"
#include "editbuf.h"

class ObFieldLocative : public Assignable {
  public:
    Root * _object;
    size_t _offset;
    ObFieldLocative(Root *ob, size_t off) : _object(ob), _offset(off) { }
    virtual Root *value() { return *(Root**)((char*)_object + _offset); }
    virtual void assign(Root *new_value);
};

class SeqElementLocative : public Assignable {
  public:
    GenSeq *seq;
    index_t indx;
    SeqElementLocative(GenSeq *q, index_t i) : seq(q), indx(i) { }
    virtual Root *value();
    virtual void assign(Root *new_value) { seq->set_at(indx, new_value); }
};

class AssignableMap;
class Environment;
class EnvElementLocative : public Assignable {
  public:
    Environment *env;
    const StringC *key;
    EnvElementLocative(Environment *e, const StringC *k) : env(e), key(k) { }
    virtual Root *value();
    virtual void assign(Root *new_value);
};
class AssignableElement : public Assignable {
  public:
    AssignableMap& map;
    Root *key;
    AssignableElement(AssignableMap& m, Root *k) : map(m) { key = k; }
    virtual Root *value();
    virtual void assign(Root *new_value);
};

extern Root * Dereference(Root *val);

// After :V = (f X1 ... Xn), then V is an assignable which depends
// on X1 ... Xn.
// V contains n ADependency elements arg[0:n]. Each arg[i].dependent==V.
// arg[i].independent==Xi.
// arg[i].next is used to link ADependencies of Xi in a list
// headed by Xi.dependents.

struct ADependency {
  public:
    Root * independent; // an Assignable, unless dependent==NULL
    inline Root *value() { return Dereference(independent); }
    ADependency(Assignable *var, Root *arg);
    ~ADependency();
};

class SimpleAssignable : public Assignable {
  public:
    const Root *current;
    SimpleAssignable(Root *init);
    virtual void assign(Root *new_value);
    virtual Root *value();
};

typedef void (*CoerceAssigner)(class CoercedAssignable *, Root *new_val);
class CoercedAssignable : public Symbol {
  public:
    void *pointer;
    CoerceAssigner coercer;
    CoercedAssignable(const StringC *str, Package *pack,
		      void *p, CoerceAssigner c, Root *init);
    virtual void assign(Root *new_value);
};

class IntAssignable : public Assignable {
    long *pointer; // pointer to current value
  public:
    IntAssignable(long *ptr) : Assignable() { pointer = ptr; }
    virtual void assign(Root *new_value);
    virtual Root *value();
    long int_val() { return *pointer; }
};

class DependentAssignable : public Assignable {
  public:
    DependentAssignable() { }
    struct ADependency *lDeps;
    struct ADependency *rDeps;
    struct ADependency *nDeps;
    Symbol **names;
    int lCount;
    int rCount;
    int nCount;
    const Root *function;
    const Root *current;
    virtual Root *value();
    virtual void update();
};

#if 0
class TextString;
// Use this to simulate Multiple Inheritance
class TextStringSeq : public GenSeq {
    TextString& this_string;
  public:
    TextStringSeq(TextString& str) : this_string(str) { }
    virtual Assignable * assignable() const;
    virtual void printon(ostream&) const;
};
#endif

class TextString : public GenSeq {
    edit_string bstr;
//    TextStringSeq seq;
//    friend TextStringSeq;
  public:
//    virtual GenSeq *sequence() const { return (GenSeq*)this; }
    TextString(edit_buffer*b, edit_mark*ms,edit_mark*es)
	: bstr(b, ms, es) { };
    virtual void assign(Root *new_value);
    virtual Root *value();
    virtual Root * index(index_t index);
    virtual void set_at(index_t index, Root *new_value);
    virtual size_t length();
    virtual GenSeq *subseq(index_t start, index_t end);
    virtual void printon(ostream&) const;
    virtual int sizeof_file() const;
    virtual void open(GenFile*, OpenFlags flags=0);
};

class TextBuffer : public TextString {
    edit_buffer buf;
  public:
    TextBuffer() : buf(), TextString(&buf, &buf.start_mark, &buf.end_mark) { };
};

#if 0
class MapAndAssignable : public GenMap, public Assignable {
  public:
    virtual Assignable * assignable() const { reurn this; }
    virtual GenMap *mapping() { return this; }
    virtual Functional * functional() { return this; }
    virtual Root * prefix(Root *);
};
#endif

class MapAssignable : public GenMap {
    AssignableMap& this_map;
  public:
    MapAssignable(AssignableMap& map) : this_map(map) { }
    virtual Assignable * assignable() const;
    virtual void printon(ostream&) const;
    virtual Root * prefix(Root *);
    virtual size_t length();
    //virtual GenSeq *sequence() const;
};
class AssignableMap : public Assignable {
    MapAssignable map;
    friend MapAssignable;
  public:
    AssignableMap() : map(*this) { }
    virtual GenMap *mapping() { return &map; }
    virtual void xapply(void* dst, Type* dstType, ArgDesc& args);
    virtual Root * get(Root* key) = 0;
    virtual void put(Root* key, Root *val) = 0;
    //virtual GenSeq *sequence() const { return this; }
    // Note: the following are not inherited,
    // since Assignable is not derived from Functional (or GenMap)
    virtual size_t length() = 0;
};

extern void Assign(Root *var, Root *val);
extern Root *ShiftAssign(Root *val, Root **vars, int nVars);
extern IntAssignable PrintBase;
extern IntAssignable PrintReadable;

struct DynamicBind {
    Root *var;
    Root *old_value;
    DynamicBind(Root *v) { var = v; old_value = v->value(); }
    ~DynamicBind() { var->assign(old_value); }
};

struct DynamicBindMany {
    int count;
    Root **vars;
    Root **old_values;
    DynamicBindMany(int c, Root **vv, Root **old_buffer) {
	count = c; vars = vv; old_values = old_buffer;
	for (int i = 0; i < count; i++ ) old_values[i] = vv[i]->value(); }
    void undo() {
	for (int i = 0; i < count; i++) vars[i]->assign(old_values[i]);
	count = 0; // To avoid doing multiple times.
    }
    ~DynamicBindMany() { undo(); }
};

#if 0
struct DoDefVar {
    DoDefVar(Symbol *sym, Root *init);
    DoDefVar(Symbol *sym, Root *(*init_form)());
};
#endif
#endif /* !GASSIGN_H */
