/*
impedanx.hoc
Modified 7/12/95 for space plot of natural log Atten vs. x
Based on Imp1 (impedanc.hoc>

A wrapper for the Impedance class that makes it convenient to
define measuring electrode location, and current injection location
This is useful for plotting impedance vs time and freq.

usage:
	imp = new Imp1([impedanceobject]) // if no arg then this creates one
	section imp.measure(x)	//specify measurement location
	section imp.inject(x)	//specify injection location
	imp.ratio(freq)		// v(measure)/v(inject)
	imp.input(freq)		// v(measure)/i(measure)
	imp.transfer(freq)	// v(measure)/i(inject)

	imp.map()		// a widget for setting measure and inject
				// locations

	imp.move_callback = object	// if you want object.move() called
				// each time inject or measure is moved
Notes:
	Impedance objects are expensive in terms of storage but efficiently
	calculate input impedances everywhere and
	transfer impedances everywhere relative to a specific location.
	For this reason the Imp1 constructor can use a pre-existing Impedance
	object if you don't want it to construct its own instance.

	This object calls Impedance.compute() everytime a ratio,etc is
	requested.
*/

begintemplate Impx
public measure, inject, ratio, input, transfer, extended, imp
public begin, flush, view_count

objectvar imp, sec[3], shape, b1, this
objref seclist, g, rvp

double x[3], color[3]

strdef s0, s1, s2, stemp, sdist, fstyle, tstr

proc init() {
	style_ = 0
	fstyle = "log(Attenuation)"
	extended = 0
	mark_ = 0
	flush_ = 0
	scale_ = 0
	direct_ = 0
	
	freq = 0
	variable_domain(&freq, 0, 1e9) units(&freq, "Hz")
	
/*
	if (numarg() > 0) {
		imp = $o1
	}else{
		imp = new Impedance()
	}
don't bother checking for pre-existing object of Impedance class
this is called without argument, so always must create a new object
that way won't get mixed up with stuff created by Imp1
*/
	imp = new Impedance()
	imp.deltafac(.0001)
	sec[0] = new SectionRef()
	x[0] = .5
	startpath(0)
	endpath(1)
	build()
	measure(.5)
}

proc measure() {
	sec[0] = new SectionRef()
	x[0] = $1
	setdir(direct_)
}

proc setdir() {
    sec[0].sec {   
        direct_ = $1
        if (style_ == 1) { // input
                sprint(s0, "(red) %s(%g)", secname(), x[0])
        }else if (direct_ == 0) {
                sprint(s0, "Measure (red) %s(%g)", secname(), x[0])
        }else {
                sprint(s0, "Inject (red) %s(%g)", secname(), x[0])
        }
    }           
        draw()
}

proc startpath() {
	sec[1] = new SectionRef()
	x[1] = $1
	sectionname(stemp)
	sprint(s1, "path start (blue): %s(%g)", stemp, $1)
}

proc endpath() {
	sec[2] = new SectionRef()
	x[2] = $1
	sectionname(stemp)
	sprint(s2, "path end (green): %s(%g)", stemp, $1)
}

/*
func ratio() {local y
	compute($1)
	sec[1].sec y = imp.ratio(x[1])
	return y
}
func transfer() {local y
	compute($1)
	sec[1].sec y = imp.transfer(x[1]) // since v(x)/i(loc) == v(loc)/i(x)
	return y
}
func input() {local y
	compute($1)
	sec[0].sec y = imp.input(x[0])
	return y
}
*/

proc compute() {  // calcs impedances, argument is freq (Hz)
	sec[0].sec imp.loc(x[0])
	imp.compute($1, extended)
}

double sz[4]
proc draw() {  // MLH says this is the most inefficient way to do this
		// but if it works fast enough, who cares?
	// e.g. computing and setting path do not need to be linked
	compute(freq)  // freq is set by a field editor
	sec[1].sec rvp.begin(x[1])
	sec[2].sec rvp.end(x[2])
	seclist = new SectionList()
	rvp.list(seclist)
	shape.color_all(1)  // foreground color
	shape.color_list(seclist, 2)  // color 2
	g.flush()
	if (scale_) {// only after the flush does a graph vector have info
		g.size(&sz[0])
		g.size(sz[0], sz[1], 0, sz[3])
	}
}

proc dist() {local y
//	sec[0].sec distance(0,x[0])
	sec[1].sec distance(0,x[1])
	sec[2].sec y = distance(x[2])
	sprint(sdist, "path length (um) %g", y)
}

proc build() {local i
	dist()

	b1 = new VBox()
	b1.ref(this)
	b1.save("")
	b1.intercept(1)

	xpanel("", 1)
	xbutton("Redraw", "draw()")
	xmenu("SelectLocation")
		xradiobutton("measure/inject", "mark_=0", 1)
		xradiobutton("path start", "mark_=1")
		xradiobutton("path end", "mark_=2")
	xmenu()
	xmenu("Plot")
		xradiobutton("log(Attenuation", "style(0)", 1)
		xradiobutton("Zin (Mohm)", "style(1)")
		xradiobutton("Ztransfer (Mohm)", "style(2)")
		xradiobutton("V(measure)/V(inject)", "style(3)")
		xradiobutton("Ztransfer phase (rad)", "style(4)")
	xmenu()
	xmenu("Extras")
		xstatebutton("Movie mode", &flush_, "add_flush()")
		xstatebutton("Auto Scale", &scale_, "draw()")
	xmenu()
        xpanel()

	xpanel("")
	xcheckbox("include dstate/dt contribution", &extended, "draw()")
	// now a field editor for frequency
	xpvalue("frequency", &freq, 1, "draw()")
        xradiobutton("Vin", "setdir(0)", 1)
        xradiobutton("Vout", "setdir(1)")
 
	xvarlabel(s0)
	xvarlabel(s1)
	xvarlabel(s2) // new line here
	xvarlabel(sdist)
	xpanel()

	shape = new Shape()

	for i=0,2 {
		color[i] = i+2	// red, blue
	}
	shapemark(3)
	for i=0,2 {
		shapemark(i, sec[i], x[i])
	}

	shape.action("move()")

	g = new Graph()  // appends graph to box

	b1.intercept(0)
	sprint(stemp, "%s", this)
	b1.map(stemp)

	rvp = new RangeVarPlot("fun($1)")
	add_rvp()
	draw()
}

proc add_rvp() {
	g.erase_all()
	g.label(-100, -100, "")
	g.addobject(rvp)
	g.label(.5, .9, fstyle)
}

func fun() {local xx
	if (style_ == 0) {
		if (direct_ == 0) {
			return -log(imp.ratio($1))
		}else{
			sec[0].sec xx = imp.input(x[0])
			return -log(imp.transfer($1)/xx)
		}
	}else if (style_ == 1) {
		return imp.input($1)
	}else if (style_ == 2 ){
		return imp.transfer($1)
	}else if (style_ == 3 ){
		if (direct_ == 0) {
			return imp.ratio($1)
		}else{
			sec[0].sec xx = imp.input(x[0])
			return imp.transfer($1)/xx
		}
	}else if (style_ == 4 ){
		return imp.transfer_phase($1)
	}
}

proc style() {
	style_ = $1
	if (style_ == 0) {
		fstyle = "log(Attenuation)"
	}else if (style_ == 1) {
		fstyle = "Zin" 
	}else if (style_ == 2) {
		fstyle = "Ztransfer"
	}else if (style_ == 3) {
		fstyle = "V(measure)/V(inject)"
	} else if (style_ == 4) {
		fstyle = "ZtransferPhase"
	}
	setdir(direct_)
	add_rvp()
	draw()
}

proc move() {local i, xx
	i = mark_
	xx = hoc_ac_
	if (i == 0) {
		measure(xx)
	}else if (i == 1) {
		startpath(xx)
	}else {
		endpath(xx)
	}
	shapemark(i, sec[i], x[i], color[i])
	dist()
	draw()
}

// should put this in plotshape class and avoid a meaningless point process
objectvar stim[1]
proc shapemark() {local i
	if (numarg() == 1) {
		objectvar stim[$1]
		for i=0,$1-1 sec[i].sec stim[i] = new PointProcessMark(x[i])	
		for i=0,$1-1 shape.point_mark(stim[i], color[i])
	}else{
		$o2.sec stim[$1].loc($3)
	}
}
proc add_flush() {
        if (flush_) {
                sprint(tstr, "flush_list.append(%s)", this)
                execute(tstr)
        }
}
func view_count() {
	if (flush_) {
		return g.view_count()
	}else{
		return 0
	}
}
proc begin() {
}
proc flush() {
        draw()
}       

proc save() {}

endtemplate Impx

proc makeImpx() {
	if(!execute1("v", 0)) {
		continue_dialog("No accessed section: Can't start an Impx")
		return
	}
	hoc_obj_[0] = new Impx()
	hoc_obj_[0] = hoc_obj_[1]
}