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

/* Miscellaneous */
	
	String Index(name, index)
		String name;
		Nat index;
		{
		String indexString=StringFromNat(index);
		StringCat(StringCat(StringCat(name, "["), indexString), "]");
		return(indexString);
		};
		
/* Wire */
	
	Ref GetWireProp(wire, prop)
		Wire wire;
		Atom prop;
		{
		return(PropertyGet(wire->properties, prop));
		};
		
	void PutWireProp(wire, prop, value)
		Wire wire;
		Atom prop;
		Ref value;
		{
		wire->properties=PropertyPut(wire->properties, prop, value);
		};
		
	Atom nameProp;
		
	String GetShortWireName(wire)
		Wire wire;
		{
		return(GetWireProp(wire, nameProp));
		};
		
	void SetShortWireName(wire, name)
		Wire wire;
		String name;
		{
		PutWireProp(wire, nameProp, name);
		};
		
	Nat CountBits(wire, visitTab)
		Wire wire;
		HashTable visitTab;
		{
		Nat bits=0;
		Nat i;
		if (HashTableGet(visitTab, wire)!=nil) return(0);
		HashTablePut(visitTab, wire, true);
		if (wire->size==0) return(1);
		for (i=0; i<wire->size; i++) bits+=CountBits(wire->elements[i], visitTab);
		return(bits);
		};
		
	Nat WireBits(wire)
		Wire wire;
		{
		HashTable visitTab=HashTableCreate(17, nil, nil);
		Nat bits=CountBits(wire, visitTab);
		HashTableFree(visitTab);
		return(bits);
		};
		
	void SetName(wire, name, wireToNames, nameToWire)
		Wire wire;
		String name;
		HashTable wireToNames;
		HashTable nameToWire;
		{
		ListOfString names=(ListOfString)HashTableGet(wireToNames, wire);
		Wire previousWire=(Wire)HashTableGet(nameToWire, name);
		Nat i;
		if (name!=nil && !ListMemberString(names, name)) {
			names=(ListOfString)ListCons(names, name);
			HashTablePut(wireToNames, wire, names);
			};
		if (name!=nil && HashTableGet(nameToWire, name)!=nil && previousWire!=wire) {
			printf("The name, %s, is applied to two different wires", name);
			Die;
			};
		HashTablePut(nameToWire, name, wire);
		for (i=0; i<wire->size; i++) {
			String shortName=GetShortWireName(wire->elements[i]);
			String subName=nil;
			if (shortName==nil) subName=Index(name, i);
			else if (name==nil) subName=shortName;
			else subName=StringCat(name, StringCat(".", shortName));
			SetName(wire->elements[i], subName, wireToNames, nameToWire);
			};
		};
		
	Atom wireToNamesCacheProp;
	Atom nameToWireCacheProp;
		
	void FillFullWireNameCaches(root)
		WireSeq root;
		{
		Nat rootSize=WireBits(root);
		HashTable wireToNames=HashTableCreate(rootSize, nil, nil);
		HashTable nameToWire=HashTableCreate(rootSize, HashTableHashString, StringEqual);
		/* SetName has GetShortWireName[wire] instead of NIL to make full names also work for wires in their own context. */
		SetName(root, GetShortWireName(root), wireToNames, nameToWire);
		PutWireProp(root, wireToNamesCacheProp, wireToNames);
		PutWireProp(root, nameToWireCacheProp, nameToWire);
		};
		
	ListOfString GetFullWireNames(root, wire)
		WireSeq root;
		Wire wire;
		{
		ListOfString names=nil;
		HashTable wireToNames=(HashTable)GetWireProp(root, wireToNamesCacheProp);
		if (wireToNames==nil) {
			FillFullWireNameCaches(root);
			wireToNames=(HashTable)GetWireProp(root, wireToNamesCacheProp);
			if (wireToNames==nil) Die;
			};
		names=(ListOfString)HashTableGet(wireToNames, wire);
		if (names==nil) Die;
		return(names);
		};
		
	String GetFullWireName(root, wire)
		WireSeq root;
		Wire wire;
		{
		String name=nil;
		ListOfString names=GetFullWireNames(root, wire);
		while (names!=nil) {
			if (name==nil || (name[0]=='[' && names->first[0]!='[') || StringLength(names->first)<StringLength(name) || (StringLength(names->first)==StringLength(name) && StringEqual(names->first, name)<0)) name=names->first;
			names=names->rest;
			};
		return(name);
		};
		
	typedef struct InsertWireContextRec {  
		Wire parent;
		Nat index;
		} InsertWireContextRec;
		
	typedef InsertWireContextRec *InsertWireContext;
		
	Bool InsertWire(item, context)
		Ref item;
		Ref context;
		{
		Wire wire=(Wire)item;
		InsertWireContext insertContext=(InsertWireContext)context;
		insertContext->parent->elements[insertContext->index]=wire;
		insertContext->index+=1;
		return(false);
		};
		
	Wire CreateWire(wires, name, properties)
		List wires;
		String name;
		Properties properties;
		{
		Nat size=ListLength(wires);
		Wire wire=CreateWires(size, name, properties);
		InsertWireContextRec insertContext;
		insertContext.parent=wire;
		insertContext.index=0;
		ListEnumerate(wires, InsertWire, &insertContext);
		return(wire);
		};
		
	Wire CreateWires(size, name, properties)
		Nat size;
		String name;
		Properties properties;
		{
		Wire wire;
		wire=NewSeq(Wire, WireRec, Wire, size);
		wire->properties=properties;
		wire->size=size;
		SetShortWireName(wire, name);
		return(wire);
		};
		
	Wire CopyWireUsingTable(old, oldToNew, copyName)
		Wire old;
		HashTable oldToNew;
		Bool copyName;
		{
		Wire new=(Wire)HashTableGet(oldToNew, old);
		String name=nil;
		Nat i;
		if (new!=nil) return(new);
		if (copyName) name=GetShortWireName(old);
		new=CreateWires(old->size, name, nil);
		HashTablePut(oldToNew, old, new);
		for (i=0; i<old->size; i++) new->elements[i]=CopyWireUsingTable(old->elements[i], oldToNew, copyName);
		return(new);
		};
		
	Wire CopyWire(wire)
		Wire wire;
		{
		Wire new;
		if (wire==nil) new=nil;
		else {
			HashTable oldToNew=HashTableCreate(17, nil, nil);
			new=CopyWireUsingTable(wire, oldToNew, true);
			HashTableFree(oldToNew);
			};
		return(new);
		};
		
	void PrintAWire(wire, indent, level, name, cr, firstWire, recur)
		Wire wire;
		Nat indent;
		Nat level;
		String name;
		Bool cr;
		Bool firstWire;
		Nat recur;
		{
		Nat i;
		StringPrintIndent(indent, (cr || firstWire));
		printf("%s", name);
		/*!! if (recur==1 && Rope.Match["[*]", name] THEN out.PutF["(%g↑)", IO.int[LOOPHOLE[wire]]]; */
		if (wire->size!=0) printf(", %d elements", wire->size);
		PropertyPrint(wire->properties, indent+1, cr, level);
		/*!! if (wire->size!=0 && (level==0 || AllSimpleAtomics(wire))) printf(" [...]") */
		if (wire->size!=0 && level==0) printf(" [...]");
		else for (i=0; i<wire->size; i++) {
			String subName;
			subName=GetShortWireName(wire->elements[i]);
			/*!! if (subName==nil) subName=Index(nil, i); */
			if (subName==nil) subName="indexed";
			PrintAWire(wire->elements[i], indent+1, level-1, subName, cr && (wire->size<=32), cr && i==0, recur+1);
			};
		};
		
	Wire FindWire(root, name)
		WireSeq root;
		String name;
		{
		Wire wire;
		HashTable nameToWire=(HashTable)GetWireProp(root, nameToWireCacheProp);
		if (nameToWire==nil) {
			FillFullWireNameCaches(root);
			nameToWire=(HashTable)GetWireProp(root, nameToWireCacheProp);
			if (nameToWire==nil) Die;
			};
		wire=(Wire)HashTableGet(nameToWire, name);
		return(wire);
		};
		
	void PrintWire(wire, indent, level)
		Wire wire;
		Nat indent;
		Nat level;
		{
		String name;
		name=GetShortWireName(wire);
		if (name==nil) name="<no name>";
		PrintAWire(wire, indent, level, name, true, true, 0);
		};
		
	void VisitAtomicWires(wire, each, context)
		Wire wire;
		EachWirePairProc each;
		Ref context;
		{
		Nat i;
		if (wire->size==0) each(wire, context);
		else for (i=0; i<wire->size; i++) VisitAtomicWires(wire->elements[i], each, context);
		};
		
	void VisitRootAtomics(root, each, context)
		Wire root;
		EachWirePairProc each;
		Ref context;
		{
		Nat i;
		for (i=0; i<root->size; i++) VisitAtomicWires(root->elements[i], each, context);
		};
		
	Bool VisitBinding(actual, public, each, context)
		Wire actual;
		Wire public;
		EachWirePairProc each;
		Ref context;
		{
		Nat i;
		Bool subWires;
		Bool quit;
		if (actual->size!=public->size) return(true);
		quit=each(actual, public, context, &subWires);
		if (quit || !subWires) return(quit);
		for (i=0; i<actual->size; i++) if (VisitBinding(actual->elements[i], public->elements[i], each, context)) return (true);
		return(false);
		};
		
	Bool VisitBindingSeq(actual, public, each, context)
		WireSeq actual;
		WireSeq public;
		EachWirePairProc each;
		Ref context;
		{
		Nat i;
		for (i=0; i<actual->size; i++) if (VisitBinding(actual->elements[i], public->elements[i], each, context)) return (true);
		return(false);
		};
		
/* Cell Class */
	
	Ref GetCellClassProp(cellClass, prop)
		CellClass cellClass;
		Atom prop;
		{
		return(PropertyGet(cellClass->properties, prop));
		};
		
	void PutCellClassProp(cellClass, prop, value)
		CellClass cellClass;
		Atom prop;
		Ref value;
		{
		cellClass->properties=PropertyPut(cellClass->properties, prop, value);
		};
		
	CellClass CreateCellClass(name, recast, properties, layersProps)
		String name;
		RecastProc recast;
		Properties properties;
		Bool layersProps;
		{
		CellClass cellClass;
		cellClass=New(CellClass, CellClassRec);
		cellClass->name=name;
		cellClass->recast=(Ref (*)())recast;
		cellClass->properties=properties;
		cellClass->layersProps=layersProps;
		return(cellClass);
		};
		
	Atom printClassProcProp;
		
	CellClass SetClassPrintProc(cellClass, proc)
		CellClass cellClass;
		PrintClassProc proc;
		{
		PutCellClassProp(cellClass, printClassProcProp, proc);
		return(cellClass);
		};
		
/* Cell Type */
	
	Ref GetCellTypeProp(cellType, prop)
		CellType cellType;
		Atom prop;
		{
		return(PropertyGet(cellType->properties, prop));
		};
		
	void PutCellTypeProp(cellType, prop, value)
		CellType cellType;
		Atom prop;
		Ref value;
		{
		cellType->properties=PropertyPut(cellType->properties, prop, value);
		};
		
	String GetCellTypeName(cellType)
		CellType cellType;
		{
		return(GetCellTypeProp(cellType, nameProp));
		};
		
	CellType SetCellTypeName(cellType, name)
		CellType cellType;
		String name;
		{
		PutCellTypeProp(cellType, nameProp, name);
		return(cellType);
		};
		
	CellType CreateCellType(class, public, data, name, properties)
		CellClass class;
		WireSeq public;
		Ref data;
		String name;
		Properties properties;
		{
		CellType cellType=New(CellType, CellTypeRec);
		cellType->class=class;
		cellType->public=public;
		cellType->data=data;
		cellType->properties=properties;
		if (name!=nil) cellType=SetCellTypeName(cellType, name);
		return(cellType);
		};
		
	void PrintCellType(cellType, indent, level)
		CellType cellType;
		Nat indent;
		Nat level;
		{
		PrintClassProc classProc=nil;
		String name;
		if (cellType->class==nil) name="<nil class pointer>";
		else {
			name=cellType->class->name;
			classProc=(PrintClassProc)GetCellClassProp(cellType->class, printClassProcProp);
			};
		printf("\n\n%s: %s Cell Type", GetCellTypeName(cellType), name);
		if (cellType->public!=nil) {
			printf("\nPublic wire:");
			PrintWire(cellType->public, indent+1, level);
			};
		if (classProc!=nil) classProc(cellType->data, indent, level);
		PropertyPrint(cellType->properties, indent, true, level);
		};
		
/* Record Cell Class */
	
	CellClass recordCellClass;
		
	Ref GetCellInstanceProp(instance, prop)
		CellInstance instance;
		Atom prop;
		{
		return(PropertyGet(instance->properties, prop));
		};
		
	void PutCellInstanceProp(instance, prop, value)
		CellInstance instance;
		Atom prop;
		Ref value;
		{
		instance->properties=PropertyPut(instance->properties, prop, value);
		};
		
	String GetCellInstanceName(instance)
		CellInstance instance;
		{
		return(GetCellInstanceProp(instance, nameProp));
		};
		
	CellInstance SetCellInstanceName(instance, name)
		CellInstance instance;
		String name;
		{
		PutCellInstanceProp(instance, nameProp, name);
		return(instance);
		};
		
	CellInstance CreateCellInstance(actual, type, name, properties)
		WireSeq actual;
		CellType type;
		String name;
		Properties properties;
		{
		CellInstance cellInstance;
		cellInstance=New(CellInstance, CellInstanceRec);
		cellInstance->actual=actual;
		cellInstance->type=type;
		cellInstance->properties=properties;
		if(name!=nil) SetCellInstanceName(cellInstance, name);
		return(cellInstance);
		};
		
	typedef struct InsertInstanceContextRec {
		RecordCellType data;
		Nat elementIndex;
		} InsertInstanceContextRec;
		
	typedef InsertInstanceContextRec *InsertInstanceContext;
		
	Bool InsertInstance (item, context)
		Ref item;
		Ref context;
		{
		InsertInstanceContext insertContext=(InsertInstanceContext)context;
		insertContext->data->cellInstances[insertContext->elementIndex]=(CellInstance)item;
		insertContext->elementIndex+=1;
		return(false);
		};
		
	CellType CreateRecordCell(public, internal, cellInstances, name, properties, giveNames)
		WireSeq public;
		WireSeq internal;
		List cellInstances;
		String name;
		Properties properties;
		Bool giveNames;
		{
		InsertInstanceContextRec insertContext;
		Nat size=ListLength(cellInstances);
		RecordCellType data=NewSeq(RecordCellType, RecordCellTypeRec, CellInstance, size);
		data->internal=internal;
		data->size=size;
		insertContext.data=data;
		insertContext.elementIndex=0;
		ListEnumerate(cellInstances, InsertInstance, &insertContext);
		return(CreateCellType(recordCellClass, public, data, name, properties));
		};
		
	typedef struct PrintInstanceBindingContextRec {
		Wire public;
		Wire internal;
		} PrintInstanceBindingContextRec;
		
	typedef PrintInstanceBindingContextRec *PrintInstanceBindingContext;
		
	Bool PrintInstanceBinding(actualWire, publicWire, context, subWires)
		Wire actualWire;
		Wire publicWire;
		Ref context;
		Bool *subWires;
		{
		PrintInstanceBindingContext printInstanceContext=(PrintInstanceBindingContext)context;
		String publicName=GetFullWireName(printInstanceContext->public, publicWire);
		String actualName=GetFullWireName(printInstanceContext->internal, actualWire);
		*subWires=GetShortWireName(publicWire)==nil && publicWire->size>0;
		/*!! IF NOT subWires THEN IF Rope.Match["[*]", actualName] 
			THEN out.PutF[" %g: %g(%g↑);", IO.rope[publicName], IO.rope[actualName], IO.int[LOOPHOLE[actualWire]]] 
			ELSE out.PutF[" %g: %g;", IO.rope[publicName], IO.rope[actualName]]; */
		if (!*subWires) printf( " %s: %s;", publicName, actualName);
		return(false);
		};
		
	void RecordPrint(data, indent, level)
		Ref data;
		Nat indent;
		Nat level;
		{
		Nat i, j;
		RecordCellType recordCellType=(RecordCellType)data;
		PrintInstanceBindingContextRec printInstanceContext;
		printInstanceContext.internal=recordCellType->internal;
		StringPrintIndent(indent, true);
		printf("Internal wire:");
		PrintWire(recordCellType->internal, indent+1, level);
		StringPrintIndent(indent, true);
		printf("%d instances", recordCellType->size);
		for (i=0; i<recordCellType->size; i++) {
			CellInstance cellInstance=recordCellType->cellInstances[i];
			String instanceName=GetCellInstanceName(cellInstance);
			String subCellTypeName=GetCellTypeName(cellInstance->type);
			StringPrintIndent(indent, true);
			if (instanceName==nil) instanceName="<no name>";
			if (subCellTypeName==nil) subCellTypeName="<no name>";
			printf("CellInstance %s: %s", instanceName, subCellTypeName);
			StringPrintIndent(indent, true);
			printf("	Actual wire: ");
			/*!! IF NOT CoreOps.Conform[instance.actual, instance.type.public] THEN out.PutF["\n*** Actual and Public do not conform\n"]; */
			printInstanceContext.public=cellInstance->type->public;
			for (j=0; j<cellInstance->type->public->size; j++) VisitBinding(cellInstance->actual->elements[j], cellInstance->type->public->elements[j], PrintInstanceBinding, &printInstanceContext);
			PropertyPrint(cellInstance->properties, 1, true, level);
			};
		};
		
/* Transistor Cell Class */
	
	CellClass transistorCellClass;
		
	String transistorTypeNames[3]={"nE", "pE", "nD"};
		
	String transistorPortNames[4]={"gate", "ch1", "ch2", "Vdd"};
		
	void TransistorPrint(data, indent, level)
		Ref data;
		Nat indent;
		Nat level;
		{
		Transistor transistor=(Transistor)data;
		printf("\n\ntype: %s", transistorTypeNames[Val(transistor->type)]);
		};
		
	Atom lengthProp;
		
	Atom widthProp;
		
	CellType CreateTransistor(type, length, width, name, properties)
		TransistorType type;
		int length;
		int width;
		String name;
		Properties properties;
		{
		Transistor data=New(Transistor, TransistorRec);
		CellType cellType;
		List tranPublic=nil;
		if (type==pE) tranPublic=ListCons(tranPublic, CreateWire(nil, transistorPortNames[Val(Vdd)], nil));
		tranPublic=ListCons(tranPublic, CreateWire(nil, transistorPortNames[Val(ch2)], nil));
		tranPublic=ListCons(tranPublic, CreateWire(nil, transistorPortNames[Val(ch1)], nil));
		tranPublic=ListCons(tranPublic, CreateWire(nil, transistorPortNames[Val(gate)], nil));
		data->type=type;
		cellType=CreateCellType(transistorCellClass, CreateWire(tranPublic, nil, nil), data, name, properties);
		if (length!=-1) PutCellTypeProp(cellType, lengthProp, length);
		if (width!=-1) PutCellTypeProp(cellType, widthProp, width);
		return(cellType);
		};
		
/* Initialization */
	
	void CoreInitialize()
		{
		nameProp=PropertyRegister("CoreName", nil);
		printClassProcProp=PropertyRegister("CorePrintClassProc", nil);
		wireToNamesCacheProp=PropertyRegister("wireToNamesCacheProp", nil);
		nameToWireCacheProp=PropertyRegister("nameToWireCacheProp", nil);
		lengthProp=PropertyRegister("lengthProp", nil);
		widthProp=PropertyRegister("widthProp", nil);
		recordCellClass=SetClassPrintProc(CreateCellClass("Record", nil, nil, false), RecordPrint);
		transistorCellClass=SetClassPrintProc(CreateCellClass("Transistor", nil, nil, false), TransistorPrint);
		};