// Author: Ronald van Elburg  (RonaldAJ at vanElburg eu)
//
// NEURON script for the paper:
//
//   Ronald A.J. van Elburg and Arjen van Ooyen (2010) `Impact of dendritic size and
//   dendritic topology on burst firing in pyramidal cells', 
//   PLoS Comput Biol 6(5): e1000781. doi:10.1371/journal.pcbi.1000781.
//
// Please consult readme.txt or instructions on the usage of this file.
//
// This software is released under the GNU GPL version 3: 
// http://www.gnu.org/copyleft/gpl.html

/**************************************************************************
* This hoc code which runs in Neuron contains  classes which 
* after initialization can generate tree topologies
* in a form suitable for the construction off dendritic and/or axonic
* trees in Neuron
* 
* Class/Template names:   
* - ClassTopology
* 	Methods: 
*		GenerateTopologies(in: int iNoOfTerminals)
* 		SetNextAtomicTopologyList(inout: objref ListOfTopologyLists, 
*				           in: String szIrreducibleTopology)
*		GetTopology(out String Topology, in: NoOfTerminlas, in: TopologyNo)
*		GetNoOfTopologies(out: int iNoOfTerminals)
*	Important Variables:
*		ListOfTopologyLists
*		TopologyList
*
* - Topology
*	Methods:
*	Important Variables:
*		sz             String Representation of the Topology
*								
***************************************************************************/

begintemplate Topology
public sz
strdef sz
endtemplate Topology

begintemplate ClassTopology
	
	public GenerateTopologies, PrintTopologies, GetTopology, GetNoOfTopologies
	/* Truly Global Objects */
	objref ListOfTopologyLists

	/* Object References for Init() */

	/* Object References for SetNextAtomicTopologyList() */
	objref tTopList, tTop, tListOfTopL 

	/* Object References for GenerateTopologies() */
	objref tTopList[3], tTop[3]
	double iNoOfTerm[3], iNoOfTop[3]
	strdef sz1,sz2

	/* Object References for PrintTopologies()*/
	objref var,bvar, templist, mstr, file

	/* Object References for GetNextTopology() */
	objref  CurrentTopology
	double CurrentTopologyNo[1]

proc init(){

	// "Initialize ListOfTopologyLists, first make sure its empty"
	ListOfTopologyLists = new List()
	ListOfTopologyLists.remove_all()

	/* Then add the atomic topology list which form the starting 	**
	** point for the construction of the higher degree topologies	*/
	
	SetNextAtomicTopologyList(ListOfTopologyLists,"1")
	SetNextAtomicTopologyList(ListOfTopologyLists,"2(1,1)")	
} 

proc SetNextAtomicTopologyList(){

	tTopList = new List()
	tTop = new Topology()
	
	tListOfTopL = $o1
	tTop.sz = $s2

	tTopList.append(tTop)
	tListOfTopL.append(tTopList)
}



proc  GenerateTopologies(){local iNoOfTerminals,Skip
	
	iNoOfTerminals=$1
	

	for(iNoOfTerm[0]=ListOfTopologyLists.count();iNoOfTerm[0]<iNoOfTerminals;iNoOfTerm[0]+=1){
		//fprint("Now creating degree %d tree \n", var+1)
		tTopList[0]= new List()				
		for(iNoOfTerm[1]=0;iNoOfTerm[1]<(iNoOfTerm[0]-1)/2;iNoOfTerm[1]+=1){

			tTopList[1]=ListOfTopologyLists.object(iNoOfTerm[1])
			tTopList[2]=ListOfTopologyLists.object(iNoOfTerm[0]-iNoOfTerm[1]-1)
	
			
			for(iNoOfTop[1]=0; iNoOfTop[1] < tTopList[1].count(); iNoOfTop[1]+=1){
				for(iNoOfTop[2]=0; iNoOfTop[2]< tTopList[2].count(); iNoOfTop[2]+=1){
					tTop[0] = new Topology()		
					tTop[1]=tTopList[1].object(iNoOfTop[1])
					tTop[2]=tTopList[2].object(iNoOfTop[2])
					sz1=tTop[1].sz
					sz2=tTop[2].sz
					sprint(tTop[0].sz,"%d(%s,%s)",iNoOfTerm[0]+1,sz2 ,sz1 )
					tTopList[0].append(tTop[0])
				}
			}
		}

		if(iNoOfTerm[0]-1==2*iNoOfTerm[1]){
			tTopList[1]=ListOfTopologyLists.object(iNoOfTerm[1])
			tTopList[2]=tTopList[1]
			for(iNoOfTop[1]=0; iNoOfTop[1] < tTopList[1].count(); iNoOfTop[1]+=1){
				for(iNoOfTop[2]=iNoOfTop[1]; iNoOfTop[2]< tTopList[2].count(); iNoOfTop[2]+=1){
					tTop[0] = new Topology()		
					tTop[1]=tTopList[1].object(iNoOfTop[1])
					tTop[2]=tTopList[2].object(iNoOfTop[2])
					sz1=tTop[1].sz
					sz2=tTop[2].sz
					sprint(tTop[0].sz,"%d(%s,%s)",iNoOfTerm[0]+1,sz2 ,sz1 )
					tTopList[0].append(tTop[0])
				}
			}
		}
	ListOfTopologyLists.append(tTopList[0])
	}
}

proc PrintTopologies(){local var,bvar
	file= new File("trees.txt")
	file.wopen("trees.txt")	
	var=ListOfTopologyLists.count()-1
	templist=ListOfTopologyLists.object(var)
	for(bvar=0; bvar < templist.count(); bvar+=1) {	
		mstr=templist.object(bvar)
		file.printf("%d \t%s\n",bvar+1, mstr.sz)
	
	}
}

proc GetTopology(){local lNoOfTerm, lTopologyNo
	$s1="No Topology Found"
	lNoOfTerm  =$2
	lTopologyNo=$3
	
	if(lNoOfTerm ==1 /*&& lTopologyNo==1*/){
		sprint($s1,"1")
		return
	} else if (lNoOfTerm ==2){
	  sprint($s1,"2(1,1)")
		return
	 }
	
	if(lNoOfTerm> ListOfTopologyLists.count()){
		GenerateTopologies(lNoOfTerm) 
	} 
	tTopList=ListOfTopologyLists.object(lNoOfTerm-1)
	
	if(lTopologyNo>tTopList.count()){
		$s1="Error: TopologyNo out of Range"
	}else{ 
		if (lTopologyNo > 0){
			CurrentTopologyNo[0]=lTopologyNo
			CurrentTopology=tTopList.object(CurrentTopologyNo[0]-1)
			$s1=CurrentTopology.sz
		}else{
			$s1="Error: TopologyNo invalid, i.e. TopologyNo <= 0"
		}
	}
}


proc GetNoOfTopologies(){local lNoOfTerm
	
	lNoOfTerm  =$2
		
	if(lNoOfTerm > ListOfTopologyLists.count()){
		GenerateTopologies(lNoOfTerm) 
		fprint("GenerateTopologies(%d)",lNoOfTerm)
	} 
	tTopList=ListOfTopologyLists.object(lNoOfTerm-1)
		fprint("tTopList.count(%d)",tTopList.count())
	$&1=tTopList.count()
}



endtemplate ClassTopology