#pragma implementation
#include <iostream.h>
#include "gassign.h"
#include "gkinds.h"
#include "gennum.h"
#include "exceptions.h"
#include "gfiles.h"
#include "symbol.h"
#include "genmap.h"
#include "genfiles.h"
#include "gfunc.h"
//#include "hash.h"
#include <std.h>
#include "builtin-syms.h"
#include "prop-list.h"
typedef void *Pix;

void ObFieldLocative::assign(Root *new_value)
{
    *(Root**)((char*)_object + _offset) = new_value;
}

Root* SeqElementLocative::value()
{
    Root *val = seq->index(indx);
    if (val == Missing)
	Signal(&MissingElement);
    return val;
}

long Assignable::magic() const
{
#if 0
    Root *val = value();
    if (val != NULL)
	return val->magic() | AssignableKind;
    else
#endif
	return AssignableKind;
}

void Assignable::xapply(void* dst, Type *dstType, ArgDesc& args)
{
  if (args.rCount == 0)
    return value()->xapply(dst, dstType, args);
  else
    return Root::xapply(dst, dstType, args);
}

void Assignable::printon(ostream& outs) const
{
    ((Assignable*)this)->value()->printon(outs);
}

void Assignable::NotifyDependents()
{
#ifndef OLD_DEPEND
    Root *props = _property_list;
    for (;;) {
	ConsPair *pair = FindProp(props, &UPDATE_sym);
	if (pair == NULL)
	    return;
	props = pair->cdr;
	pair->car->update();
    }
#else
    struct ADependency *dep;
    for (dep = dependents; dep != NULL; dep = dep->next) {
	dep->dependent->update();
    }
#endif
}
 
SimpleAssignable::SimpleAssignable(Root*init) : Assignable()
 { current = init; }

void SimpleAssignable::assign(Root *new_value) { current = new_value; }

Root *SimpleAssignable::value() { return current; }

CoercedAssignable::CoercedAssignable(const StringC *str, Package *pack,
				     void *p, CoerceAssigner c, Root *init)
: Symbol(str, pack)
{
    pointer = p;
    coercer = c;
    if (pack) {
	pack->export(this);
    }
    assign(init);
}

void CoercedAssignable::assign(Root *new_value)
{
    (*coercer)(this, new_value);
    Symbol::assign(new_value);
}

Root *IntAssignable::value() { return MakeFixInt(*pointer); }

void IntAssignable::assign(Root *new_value)
{
    long new_int;
    const Numeric *new_num = new_value->numeric();
    if (new_num == NULL || !new_num->getlong(&new_int)) RaiseDomainError(NULL);
    *pointer = new_int;
}

Root * Dereference(Root *val)
{
#if 1
    // This loop is needed e.g. in case of a logical variable
    // that names an assignable variable.
    // I'm not sure this is the right way to do it!
    for (;;) {
	Root *v = val->value();
	if (v == val)
	    return v;
	val = v;
    }
#else
    Assignable *var = val->assignable();
    if (var == NULL) return val;
    return var->value();
#endif
}

void Assign(Root *dest, Root *val)
{
    dest->assign(Dereference(val));
    Assignable *var = dest->assignable();
    if (var != NULL)
	var->NotifyDependents();
}

Root *ShiftAssign(Root *in_val, Root **vars, int nVars)
{
    Root **vals = (Root**)alloca(sizeof(Root*) * nVars);
    int i;
    for (i = 0; i < nVars; i++) {
	Assignable *var = vars[i]->assignable();
	if (var == NULL) RaiseDomainError(NULL);
	vals[i] = var->value();
    }
    Root *val = in_val == NULL ? vals[nVars-1] : Dereference(in_val);
    for (i = 0; i < nVars; i++) {
	Assignable *var = vars[i]->assignable();
	if (var == NULL) RaiseDomainError(NULL);
	var->assign(val);
	var->NotifyDependents();
	val = vals[i];
    }
    return val;
}

ADependency::ADependency(Assignable *dep_var, Root *arg)
{
    independent = arg;
    Assignable *indep = arg->assignable();
#ifndef OLD_DEPEND
    if (indep) {
	indep->_property_list =
	    new ConsPair(&UPDATE_sym,
			 new ConsPair(dep_var,
				      indep->_property_list));
    }
#else
    dependent = dep_var;
    if (indep != NULL) {
	next = indep->dependents;
	indep->dependents = this;
    }
#endif
}

ADependency::~ADependency()
{
#ifndef OLD_DEPEND
#if 0
    independent = arg;
    Assignable *indep = arg->assignable();
    if (!indep)
	return;
    for (;;) {
	register Root **prop_list_ptr = &indep->_property_list;
	register Root *prop_list = *prop_list_ptr;
	if (prop_list == &NilSymbol)
	    return;
	ConsPair *cons = CheckCons(prop_list);
	Root *prop_next = cons->cdr;
	ConsPair *cons_next = CheckCons(prop_next);
	if (cons->car == key && cons_next->car == ??) {
	    Root *result = cons_next->car;
	    *prop_list_ptr = cons_next->cdr;
	}
	prop_list_ptr = &cons_next->cdr;
    }
#endif
#else
    if (dependent != NULL) {
	Assignable *indep = independent->assignable();
	if (indep == NULL) return;
	ADependency **ptr = &indep->dependents;
	for ( ; *ptr != this; ptr = &(*ptr)->next) ;
	*ptr = next;
	indep = NULL;
    }
#endif
}

Root *DependentAssignable::value()
{
    if (current == Missing) ((DependentAssignable*)this)->update();
    return current;
}

void DependentAssignable::update()
{
    const Root **lTemp = (const Root**)alloca(sizeof(Root*) * lCount);
    const Root **rTemp = (const Root**)alloca(sizeof(Root*) * rCount);
    const Root **nTemp = (const Root**)alloca(sizeof(Root*) * nCount);
    int i;
    struct ADependency *dep; 
    for (i = 0, dep = lDeps; i < lCount; i++, dep++) lTemp[i] = dep->value();
    for (i = 0, dep = rDeps; i < rCount; i++, dep++) rTemp[i] = dep->value();
    for (i = 0, dep = nDeps; i < nCount; i++, dep++) nTemp[i] = dep->value();
    current = function->apply(lTemp,rTemp,nTemp, names, lCount,rCount,nCount);
}

void TextString::printon(ostream& outs) const
{
    if (print_readable)
	outs << "BUFFER<";
    edit_streambuf buf_file(&bstr, ios::in);
    for (;;) {
	int ch = buf_file.sbumpc();
	if (ch == EOF) break;
	outs.put(ch);
    }
    if (print_readable)
	outs << '>';
}

Root * TextString::value()
{
    int length;
    buf_char* str = bstr.copy_bytes(&length);
    return new String((char*)str, length);
}

void TextString::assign(Root *new_value)
{
    const GenSeq * seq = new_value->sequence();
    if (seq == NULL) RaiseDomainError(NULL);
    ITERATOR(src_file, seq);
    edit_streambuf dst_file(&bstr, ios::out); // Mode: "r+"
    for (;;) {
	Root *v = src_file.next();
	if (v == Missing) break;
#if 0
	int v_magic = v->magic();
	int curIsSymbol = (v_magic & BasicKindMask) == SymbolKind;
	if (!curIsSymbol || sym length != 1) error();
#endif
	v->printon(&dst_file);	
    }
#if 1
    dst_file.truncate();
#else
    bstr.buffer->delete_range(bstr.buffer->tell((buf_char*)dst_file.pptr()),
			      bstr.buffer->tell(bstr.end));
#endif
}

Root * TextString::index(index_t i)
{
    if (i < 0)
	i += length() + 1;
    edit_buffer *buffer = bstr.buffer;
    buf_char *string_start = bstr.start->ptr(buffer);
    buf_char *gap_start = buffer->gap_start();
    if (string_start <= gap_start) {
	buf_offset size_to_gap = gap_start - string_start;
	if (i < size_to_gap)
	    return &CharTable[string_start[i]];
	else {
	    string_start = buffer->gap_end();
	    i -= size_to_gap;
	}
    }
    buf_char *string_end = bstr.end->ptr(buffer);
    buf_offset size_to_end = string_end - string_start;
    if (i >= size_to_end)
	return Missing;
    else
	return &CharTable[string_start[i]];
}

void TextString::set_at(index_t i, Root *new_value)
{
    if (i < 0)
	i += length() + 1;
    if (!new_value->isKindOf(*Character::desc()))
	SignalBadAssignment(this, new_value);
    unsigned char ch = ((Character*)new_value)->string()[0];
    edit_buffer *buffer = bstr.buffer;
    buf_char *string_start = bstr.start->ptr(buffer);
    buf_char *gap_start = buffer->gap_start();
    if (string_start <= gap_start) {
	buf_offset size_to_gap = gap_start - string_start;
	if (i < size_to_gap)  {
	    string_start[i] = ch;
	    return;
	}
	else {
	    string_start = buffer->gap_end();
	    i -= size_to_gap;
	}
    }
    buf_char *string_end = bstr.end->ptr(buffer);
    buf_offset size_to_end = string_end - string_start;
    if (i >= size_to_end)
	SignalBadAssignment(this, new_value);
    else
	string_start[i] = ch;
}

int TextString::sizeof_file() const { return sizeof(CharFile); }
void TextString::open(GenFile* result, OpenFlags flags)
{
    edit_streambuf *sbuf = new edit_streambuf(&bstr, 0);
    CharFile* cresult = (CharFile*)result;
    cresult->CharFile::CharFile(*this, sbuf);
    cresult->index = 0;
    cresult->state=NULL;
    cresult->stream = sbuf;
}

size_t TextString::length()
{
    return bstr.length();
}

GenSeq* TextString::subseq(index_t start, index_t end)
{
    edit_mark *start_mark =
	start >= 0 ? new edit_mark(&bstr, start)
	    : new edit_mark(&bstr, start+1);
    edit_mark *end_mark =
	end >= 0 ? new edit_mark(&bstr, end)
	    : new edit_mark(&bstr, end+1);
    return new TextString(bstr.buffer, start_mark, end_mark);
}

//Assignable* TextStringSeq::assignable() const { return &this_string; }
//void TextStringSeq::printon(ostream& outs) const {this_string.printon(outs);}

IntAssignable PrintBase(&print_base);
IntAssignable PrintReadable(&print_readable);
IntAssignable PrintLength(&print_length);
IntAssignable PrintLisp(&print_lisp);

void AssignableMap::xapply(void* dst, Type* dstType, ArgDesc& args)
{
  map.xapply(dst, dstType, args);
}

Assignable* MapAssignable::assignable() const { return &this_map; }
void MapAssignable::printon(ostream& outs) const { this_map.printon(outs); }
size_t MapAssignable::length() { return this_map.length(); }
Root* MapAssignable::prefix(Root* arg)
{
    return new AssignableElement(this_map, arg);
}

Root * AssignableElement::value()
{
    return Dereference(map.get(Dereference(key)));
}
void AssignableElement::assign(Root* new_value)
{
    map.put(Dereference(key), new_value);
}

static Predefined pre_print_lisp(print_lisp_sym, PrintLisp);
static Predefined pre_print_base(print_base_sym, PrintBase);
static Predefined pre_print_readable(print_readable_sym, PrintReadable);

void SignalBadAssignment(Root *var, Root *new_val)
{
    Signal(new BadAssignment(var, new_val));
}

#if 0
DoDefVar::DoDefVar(Symbol *sym, Root *init)
{
    if (sym->value() == NULL)
	sym->value(init);
}

DoDefVar::DoDefVar(Symbol *sym, Root *(*init_form)())
{
    if (sym->value() == NULL)
	sym->value((*init_form)());
}
#endif

Root *EnvElementLocative::value()
{
    Root *val = env->lookup_at(key);
    if (val == NULL)
	val = Missing;
    return val;
}

void EnvElementLocative::assign(Root *new_value)
{
    if (new_value == Missing)
	new_value = NULL;
    env->set_at(new_value, key);
}

INSERT_BUILTIN(GLOBAL, UserPackage);

