/* CoreFlat.c */
	
#include "CoreBasics.h"
#include "Core.h"
#include "CoreFlat.h"

/* Types */
	
	FlatCellTypeRec rootCellType;
		
	FlatCellTypeRec allFlatCells;
		
	WirePathRec rootWirePath;
		
/* Flat Instance */
	
	Bool InstancePathEqualRec(one, other)
		InstancePathRec one;
		InstancePathRec other;
		{
		Nat i;
		if (one.length!=other.length) return(false);
		for (i=0; i<CellTypePathSize; i++) if (one.bits[i]!=other.bits[i]) return(false);
		return(true);
		};
		
	Nat InstancePathHashRec(path)
		InstancePathRec path;
		{
		Nat hash=0;
		Nat i;
		for (i=0; i<=path.length; i++) hash=hash↑(path.bits[i]<<(i%16));
		return(hash);
		};
		
	void ExtendPath(newPath, currentPath, index, rct)
		InstancePath newPath;
		InstancePathRec currentPath;
		Nat index;
		RecordCellType rct;
		{
		Nat bit;
		Nat pathBits=NBits(rct->size);
		*newPath=currentPath;
		for (bit=0; bit<pathBits; bit++) newPath->bits[newPath->length+bit]=EBFN(index, bit, pathBits);
		newPath->length+=pathBits;
		};
		
	typedef struct BindPublicToActualContextRec {
		HashTable bindings;
		HashTable newBindings;
		FlatCellTypeRec parent;
		} BindPublicToActualContextRec;
		
	typedef BindPublicToActualContextRec *BindPublicToActualContext;
		
	Bool BindPublicToActual(actualWire, publicWire, context, subWires)
		Wire actualWire;
		Wire publicWire;
		Ref context;
		Bool *subWires;
		{
		BindPublicToActualContext c=(BindPublicToActualContext)context;
		FlatWire bind=nil;
		*subWires=true;
		if (c->bindings!=nil) bind=(FlatWire)HashTableGet(c->bindings, actualWire);
		if (bind==nil) bind=CreateFlatWire(c->parent, 0, internal, false, rootWirePath, actualWire);
		HashTablePut(c->newBindings, publicWire, bind);
		return(false);
		};
		
	HashTable BindInstance(parent, actual, public, bindings)
		FlatCellTypeRec parent;
		WireSeq actual;
		WireSeq public;
		HashTable bindings;
		{
		BindPublicToActualContextRec context;
		context.bindings=bindings;
		context.newBindings=HashTableCreate(17, nil, nil);
		context.parent=parent;
		if (VisitBindingSeq(actual, public, BindPublicToActual, &context)) Die;
		return(context.newBindings);
		};
		
/* Flat Cell Type */
	
	Bool FlatCellTypeEqualRec(one, other)
		FlatCellTypeRec one;
		FlatCellTypeRec other;
		{
		return(InstancePathEqualRec(one.path, other.path) && one.recastCount==other.recastCount);
		};
		
	Nat FlatCellTypeHashRec(flatCell)
		FlatCellTypeRec flatCell;
		{
		Nat hash=InstancePathHashRec(flatCell.path);
		hash=hash↑flatCell.recastCount;
		return(hash);
		};
		
/* Flat Wire */
	
	FlatWire CreateFlatWire(flatCell, instanceIndex, wireRoot, validPath, path, wire)
		FlatCellTypeRec flatCell;
		Nat instanceIndex;
		WireRoot wireRoot;
		Bool validPath;
		WirePathRec path;
		Wire wire;
		{
		FlatWire flatWire=New(FlatWire, FlatWireRec);
		flatWire->flatCell=flatCell;
		flatWire->instanceIndex=instanceIndex;
		flatWire->wireRoot=wireRoot;
		flatWire->validPath=validPath;
		flatWire->path=path;
		flatWire->wire=wire;
		return(flatWire);
		};
		
	Bool FlatWireEqual(key1, key2)
		Ref key1;
		Ref key2;
		{
		FlatWire wire1=(FlatWire)key1;
		FlatWire wire2=(FlatWire)key2;
		return(wire1->wire==wire2->wire && FlatCellTypeEqualRec(wire1->flatCell, wire2->flatCell));
		};
		
	Nat FlatWireHash(key)
		Ref key;
		{
		FlatWire flatWire=(FlatWire)key;
		Nat hash=FlatCellTypeHashRec(flatWire->flatCell);
		hash=hash↑(((Nat)flatWire->wire)&0x0FFFFL)↑(((Nat)flatWire->wire)>>16);
		return(hash);
		};
		
/* Enumeration */
	
	void NextUnboundCellType(cell, target, flatCell, instance, index, parent, flatParent, data, proc)
		CellType cell;
		FlatCellTypeRec target;
		FlatCellTypeRec flatCell;
		CellInstance instance;
		Nat index;
		CellType parent;
		FlatCellTypeRec flatParent;
		Ref data;
		UnboundFlatCellProc proc;
		{
		if (FlatCellTypeEqualRec(target, flatCell)) return;
		if (cell->class==recordCellClass) {
			RecordCellType rct=(RecordCellType)cell->data;
			Nat pathBits=NBits(rct->size);
			CellInstance instance;
			FlatCellTypeRec childFlatCell;
			childFlatCell.path=flatCell.path;
			childFlatCell.recastCount=0;
			if (FlatCellTypeEqualRec(target, allFlatCells)) {
				Nat inst;
				for (inst=0; inst<rct->size; inst++) {
					instance=rct->cellInstances[inst];
					ExtendPath(&childFlatCell.path, flatCell.path, inst, rct);
					proc(instance->type, target, childFlatCell, instance, inst, cell, flatCell, data);
					};
				}
			else Die;
			}
		else Die;
		};
		
	typedef struct BindCellTypeContextRec {
		CellType previousCell;
		CellInstance previousInstance;
		Ref data;
		HashTable bindings;
		BoundFlatCellProc proc;
		} BindCellTypeContextRec;
		
	typedef BindCellTypeContextRec *BindCellTypeContext;
		
	void BindCellType(cell, target, flatCell, instance, index, parent, flatParent, data)
		CellType cell;
		FlatCellTypeRec target;
		FlatCellTypeRec flatCell;
		CellInstance instance;
		Nat index;
		CellType parent;
		FlatCellTypeRec flatParent;
		Ref data;
		{
		BindCellTypeContext context=(BindCellTypeContext)data;
		HashTable newBindings;
		if (instance==context->previousInstance && cell==context->previousCell) context->proc(cell, target, flatCell, instance, index, parent, flatParent, context->data, context->bindings);
		else {
			Wire actual;
			if (instance==nil || instance->type!=cell) actual=context->previousCell->public;
			else actual=instance->actual;
			newBindings=BindInstance(flatParent, actual, cell->public, context->bindings);
			context->proc(cell, target, flatCell, instance, index, parent, flatParent, context->data, newBindings);
			/* really should delete the flat wires which are no longer needed */
			HashTableFree(newBindings);
			};
		};
		
	void NextBoundCellType(cell, target, flatCell, instance, index, parent, flatParent, data, bindings, proc)
		CellType cell;
		FlatCellTypeRec target;
		FlatCellTypeRec flatCell;
		CellInstance instance;
		Nat index;
		CellType parent;
		FlatCellTypeRec flatParent;
		Ref data;
		HashTable bindings;
		UnboundFlatCellProc proc;
		{
		BindCellTypeContextRec context;
		context.previousCell=cell;
		context.previousInstance=instance;
		context.data=data;
		context.bindings=bindings;
		context.proc=proc;
		NextUnboundCellType(cell, target, flatCell, instance, index, parent, flatParent, &context, BindCellType);
		};
		
	HashTable InitialBindingTable(root)
		CellType root;
		{
		HashTable bindings;
		Die;
		return(bindings);
		};
		
/* Initialization */
	
	void CoreFlatInitialize()
		{
		Nat i;
		rootCellType.path.length=0;
		for (i=0; i<CellTypePathSize; i++) rootCellType.path.bits[i]=false;
		rootCellType.recastCount=0;
		allFlatCells.path.length=0;
		for (i=0; i<CellTypePathSize; i++) allFlatCells.path.bits[i]=false;
		allFlatCells.recastCount=NatLast;
		rootWirePath.length=0;
		for (i=0; i<WirePathSize; i++) rootWirePath.bits[i]=false;
		};