###

Prints in "2d", e.g. instead of 1/(x+1)^2 :

     1
----------
        2
 (1 + x)

 Note that although this looks more natural, a) it's not parsable and
 b) it can be occasionally be ambiguous, such as:

   1
 ----
   2
 x

is 1/x^2 but it also looks a little like x^(1/2)

###

#-----------------------------------------------------------------------------
#
#	Examples:
#
#	   012345678
#	-2 .........
#	-1 .........
#	 0 ..hello..	x=2, y=0, h=1, w=5
#	 1 .........
#	 2 .........
#
#	   012345678
#	-2 .........
#	-1 ..355....
#	 0 ..---....	x=2, y=-1, h=3, w=3
#	 1 ..113....
#	 2 .........
#
#-----------------------------------------------------------------------------



YMAX = 10000
class glyph
	c: 0
	x: 0
	y: 0

# will contain glyphs
chartab = []
for charTabIndex in [0...YMAX]
	chartab[charTabIndex] = new glyph()


yindex = 0
level = 0
emit_x = 0
expr_level = 0
display_flag = 0


# this is not really the translated version,
# the original is in window.cpp and is
# rather more complex
printchar_nowrap = (character) ->
	accumulator = ""
	accumulator += character
	return accumulator

printchar = (character) ->
	return printchar_nowrap character

print2dascii = (p) ->
	h = 0
	w = 0
	y = 0

	save()

	yindex = 0
	level = 0
	emit_x = 0
	emit_top_expr(p)

	# if too wide then print flat

	[h,w,y] = get_size(0, yindex)

	if (w > 100)
		printline(p)
		restore()
		return

	beenPrinted = print_glyphs()

	restore()
	return beenPrinted

emit_top_expr = (p) ->
	if (car(p) == symbol(SETQ))
		emit_expr(cadr(p))
		__emit_str(" = ")
		emit_expr(caddr(p))
		return

	if (istensor(p))
		emit_tensor(p)
	else
		emit_expr(p)

will_be_displayed_as_fraction = (p) ->
	if (level > 0)
		return 0
	if (isfraction(p))
		return 1
	if (car(p) != symbol(MULTIPLY))
		return 0
	if (isfraction(cadr(p)))
		return 1
	while (iscons(p))
		if (isdenominator(car(p)))
			return 1
		p = cdr(p)
	return 0

emit_expr = (p) ->
	#	if (level > 0) {
	#		printexpr(p)
	#		return
	#	}
	expr_level++
	if (car(p) == symbol(ADD))
		p = cdr(p)
		if (__is_negative(car(p)))
			__emit_char('-')
			if (will_be_displayed_as_fraction(car(p)))
				__emit_char(' ')
		emit_term(car(p))
		p = cdr(p)
		while (iscons(p))
			if (__is_negative(car(p)))
				__emit_char(' ')
				__emit_char('-')
				__emit_char(' ')
			else
				__emit_char(' ')
				__emit_char('+')
				__emit_char(' ')

			emit_term(car(p))
			p = cdr(p)
	else
		if (__is_negative(p))
			__emit_char('-')
			if (will_be_displayed_as_fraction(p))
				__emit_char(' ')
		emit_term(p)
	expr_level--

emit_unsigned_expr = (p) ->
	if (car(p) == symbol(ADD))
		p = cdr(p)
		#		if (__is_negative(car(p)))
		#			__emit_char('-')
		emit_term(car(p))
		p = cdr(p)
		while (iscons(p))
			if (__is_negative(car(p)))
				__emit_char(' ')
				__emit_char('-')
				__emit_char(' ')
			else
				__emit_char(' ')
				__emit_char('+')
				__emit_char(' ')
			emit_term(car(p))
			p = cdr(p)
	else
		#		if (__is_negative(p))
		#			__emit_char('-')
		emit_term(p)

__is_negative = (p) ->
	if (isnegativenumber(p))
		return 1
	if (car(p) == symbol(MULTIPLY) && isnegativenumber(cadr(p)))
		return 1
	return 0

emit_term = (p) ->
	if (car(p) == symbol(MULTIPLY))
		n = count_denominators(p)
		if (n && level == 0)
			emit_fraction(p, n)
		else
			emit_multiply(p, n)
	else
		emit_factor(p)

isdenominator = (p) ->
	if (car(p) == symbol(POWER) && cadr(p) != symbol(E) && __is_negative(caddr(p)))
		return 1
	else
		return 0

count_denominators = (p) ->
	count = 0
	p = cdr(p)
	#	if (isfraction(car(p))) {
	#		count++
	#		p = cdr(p)
	#	}
	while (iscons(p))
		q = car(p)
		if (isdenominator(q))
			count++
		p = cdr(p)
	return count

# n is the number of denominators, not counting a fraction like 1/2
emit_multiply = (p, n) ->
	if (n == 0)
		p = cdr(p)
		if (isplusone(car(p)) || isminusone(car(p)))
			p = cdr(p)
		emit_factor(car(p))
		p = cdr(p)
		while (iscons(p))
			__emit_char(' ')
			emit_factor(car(p))
			p = cdr(p)
	else
		emit_numerators(p)
		__emit_char('/')
		# need grouping if more than one denominator
		if (n > 1 || isfraction(cadr(p)))
			__emit_char('(')
			emit_denominators(p)
			__emit_char(')')
		else
			emit_denominators(p)

#define A p3
#define B p4

# sign of term has already been emitted

emit_fraction = (p, d) ->
	count = 0
	k1 = 0
	k2 = 0
	n = 0
	x = 0

	save()

	p3 = one; # p3 is A
	p4 = one; # p4 is B

	# handle numerical coefficient

	if (isrational(cadr(p)))
		push(cadr(p))
		mp_numerator()
		absval()
		p3 = pop();  # p3 is A
		push(cadr(p))
		mp_denominator()
		p4 = pop(); # p4 is B

	if (isdouble(cadr(p)))
		push(cadr(p))
		absval()
		p3 = pop(); # p3 is A

	# count numerators

	if (isplusone(p3)) # p3 is A
		n = 0
	else
		n = 1
	p1 = cdr(p)
	if (isnum(car(p1)))
		p1 = cdr(p1)
	while (iscons(p1))
		p2 = car(p1)
		if (isdenominator(p2))
			doNothing = 1
		else
			n++
		p1 = cdr(p1)

	# emit numerators

	x = emit_x

	k1 = yindex

	count = 0

	# emit numerical coefficient

	if (!isplusone(p3))  # p3 is A
		emit_number(p3, 0);  # p3 is A
		count++

	# skip over "multiply"

	p1 = cdr(p)

	# skip over numerical coefficient, already handled

	if (isnum(car(p1)))
		p1 = cdr(p1)

	while (iscons(p1))
		p2 = car(p1)
		if (isdenominator(p2))
			doNothing = 1
		else
			if (count > 0)
				__emit_char(' ')
			if (n == 1)
				emit_expr(p2)
			else
				emit_factor(p2)
			count++
		p1 = cdr(p1)

	if (count == 0)
		__emit_char('1')

	# emit denominators

	k2 = yindex

	count = 0

	if (!isplusone(p4))  # p4 is B
		emit_number(p4, 0);  # p4 is B
		count++
		d++

	p1 = cdr(p)

	if (isrational(car(p1)))
		p1 = cdr(p1)

	while (iscons(p1))
		p2 = car(p1)
		if (isdenominator(p2))
			if (count > 0)
				__emit_char(' ')
			emit_denominator(p2, d)
			count++
		p1 = cdr(p1)

	fixup_fraction(x, k1, k2)

	restore()

# p points to a multiply

emit_numerators = (p) ->
	save()

	n = 0

	p1 = one

	p = cdr(p)

	if (isrational(car(p)))
		push(car(p))
		mp_numerator()
		absval()
		p1 = pop()
		p = cdr(p)
	else if (isdouble(car(p)))
		push(car(p))
		absval()
		p1 = pop()
		p = cdr(p)

	n = 0

	if (!isplusone(p1))
		emit_number(p1, 0)
		n++

	while (iscons(p))
		if (isdenominator(car(p)))
			doNothing = 1
		else
			if (n > 0)
				__emit_char(' ')
			emit_factor(car(p))
			n++
		p = cdr(p)

	if (n == 0)
		__emit_char('1')

	restore()

# p points to a multiply

emit_denominators = (p) ->

	save()

	n = 0

	p = cdr(p)

	if (isfraction(car(p)))
		push(car(p))
		mp_denominator()
		p1 = pop()
		emit_number(p1, 0)
		n++
		p = cdr(p)

	while (iscons(p))
		if (isdenominator(car(p)))
			if (n > 0)
				__emit_char(' ')
			emit_denominator(car(p), 0)
			n++
		p = cdr(p)

	restore()

emit_factor = (p) ->
	if (istensor(p))
		if (level == 0)
			#emit_tensor(p)
			emit_flat_tensor(p)
		else
			emit_flat_tensor(p)
		return

	if (isdouble(p))
		emit_number(p, 0)
		return

	if (car(p) == symbol(ADD) || car(p) == symbol(MULTIPLY))
		emit_subexpr(p)
		return

	if (car(p) == symbol(POWER))
		emit_power(p)
		return

	if (iscons(p))
		#if (car(p) == symbol(FORMAL) && cadr(p).k == SYM)
		#	emit_symbol(cadr(p))
		#else
		emit_function(p)
		return

	if (isnum(p))
		if (level == 0)
			emit_numerical_fraction(p)
		else
			emit_number(p, 0)
		return

	if (issymbol(p))
		emit_symbol(p)
		return

	if (isstr(p))
		emit_string(p)
		return

emit_numerical_fraction = (p) ->
	k1 = 0
	k2 = 0
	x = 0

	save()

	push(p)
	mp_numerator()
	absval()
	p3 = pop();  # p3 is A

	push(p)
	mp_denominator()
	p4 = pop(); # p4 is B

	if (isplusone(p4))  # p4 is B
		emit_number(p3, 0); # p3 is A
		restore()
		return

	x = emit_x

	k1 = yindex

	emit_number(p3, 0);  # p3 is A

	k2 = yindex

	emit_number(p4, 0)  # p4 is B

	fixup_fraction(x, k1, k2)

	restore()

# if it's a factor then it doesn't need parens around it, i.e. 1/sin(theta)^2

isfactor = (p) ->
	if (iscons(p) && car(p) != symbol(ADD) && car(p) != symbol(MULTIPLY) && car(p) != symbol(POWER))
		return 1
	if (issymbol(p))
		return 1
	if (isfraction(p))
		return 0
	if (isnegativenumber(p))
		return 0
	if (isnum(p))
		return 1
	return 0

emit_power = (p) ->
	k1 = 0
	k2 = 0
	x = 0

	if (cadr(p) == symbol(E))
		__emit_str("exp(")
		emit_expr(caddr(p))
		__emit_char(')')
		return

	if (level > 0)
		if (isminusone(caddr(p)))
			__emit_char('1')
			__emit_char('/')
			if (isfactor(cadr(p)))
				emit_factor(cadr(p))
			else
				emit_subexpr(cadr(p))
		else
			if (isfactor(cadr(p)))
				emit_factor(cadr(p))
			else
				emit_subexpr(cadr(p))
			__emit_char('^')
			if (isfactor(caddr(p)))
				emit_factor(caddr(p))
			else
				emit_subexpr(caddr(p))
		return

	# special case: 1 over something

	if (__is_negative(caddr(p)))
		x = emit_x
		k1 = yindex
		__emit_char('1')
		k2 = yindex
		#level++
		emit_denominator(p, 1)
		#level--
		fixup_fraction(x, k1, k2)
		return

	k1 = yindex
	if (isfactor(cadr(p)))
		emit_factor(cadr(p))
	else
		emit_subexpr(cadr(p))
	k2 = yindex
	level++
	emit_expr(caddr(p))
	level--
	fixup_power(k1, k2)

# if n == 1 then emit as expr (no parens)

# p is a power

emit_denominator = (p, n) ->
	k1 = 0
	k2 = 0

	# special case: 1 over something

	if (isminusone(caddr(p)))
		if (n == 1)
			emit_expr(cadr(p))
		else
			emit_factor(cadr(p))
		return

	k1 = yindex

	# emit base

	if (isfactor(cadr(p)))
		emit_factor(cadr(p))
	else
		emit_subexpr(cadr(p))

	k2 = yindex

	# emit exponent, don't emit minus sign

	level++

	emit_unsigned_expr(caddr(p))

	level--

	fixup_power(k1, k2)

emit_function = (p) ->
	if (car(p) == symbol(INDEX) && issymbol(cadr(p)))
		emit_index_function(p)
		return

	if (car(p) == symbol(FACTORIAL))
		emit_factorial_function(p)
		return

	if (car(p) == symbol(DERIVATIVE))
		__emit_char('d')
	else
		emit_symbol(car(p))
	__emit_char('(')
	p = cdr(p)
	if (iscons(p))
		emit_expr(car(p))
		p = cdr(p)
		while (iscons(p))
			__emit_char(',')
			#__emit_char(' ')
			emit_expr(car(p))
			p = cdr(p)
	__emit_char(')')

emit_index_function = (p) ->
	p = cdr(p)
	if (caar(p) == symbol(ADD) || caar(p) == symbol(MULTIPLY) || caar(p) == symbol(POWER) || caar(p) == symbol(FACTORIAL))
		emit_subexpr(car(p))
	else
		emit_expr(car(p))
	__emit_char('[')
	p = cdr(p)
	if (iscons(p))
		emit_expr(car(p))
		p = cdr(p)
		while(iscons(p))
			__emit_char(',')
			emit_expr(car(p))
			p = cdr(p)
	__emit_char(']')

emit_factorial_function = (p) ->
	p = cadr(p)
	if (car(p) == symbol(ADD) || car(p) == symbol(MULTIPLY) || car(p) == symbol(POWER) || car(p) == symbol(FACTORIAL))
		emit_subexpr(p)
	else
		emit_expr(p)
	__emit_char('!')

emit_subexpr = (p) ->
	__emit_char('(')
	emit_expr(p)
	__emit_char(')')

emit_symbol = (p) ->

	i = 0
	if (p == symbol(E))
		__emit_str("exp(1)")
		return

	pPrintName = get_printname(p)
	for i in [0...pPrintName.length]
		__emit_char(pPrintName[i])

emit_string = (p) ->
	i = 0
	pString = p.str
	__emit_char('"')
	for i in [0...pString.length]
		__emit_char(pString[i])
	__emit_char('"')

fixup_fraction = (x, k1, k2) ->
	dx = 0
	dy = 0
	i = 0
	w = 0
	y = 0
	h1 = 0
	w1 = 0
	y1 = 0
	h2 = 0
	w2 = 0
	y2 = 0

	[h1,w1,y1] = get_size(k1, k2)
	[h2,w2,y2] = get_size(k2, yindex)

	if (w2 > w1)
		dx = (w2 - w1) / 2;	# shift numerator right
	else
		dx = 0

	dx++
	# this is how much is below the baseline

	y = y1 + h1 - 1

	dy = -y - 1

	move(k1, k2, dx, dy)

	if (w2 > w1)
		dx = -w1
	else
		dx = -w1 + (w1 - w2) / 2
	
	dx++
	dy = -y2 + 1

	move(k2, yindex, dx, dy)

	if (w2 > w1)
		w = w2
	else
		w = w1
	
	w+=2
	emit_x = x

	for i in [0...w]
		__emit_char('-')

fixup_power = (k1, k2) ->
	dy = 0
	h1 = 0
	w1 = 0
	y1 = 0
	h2 = 0
	w2 = 0
	y2 = 0

	[h1,w1,y1] = get_size(k1, k2)
	[h2,w2,y2] = get_size(k2, yindex)

	# move superscript to baseline

	dy = -y2 - h2 + 1

	# now move above base

	dy += y1 - 1

	move(k2, yindex, 0, dy)

move = (j, k, dx, dy) ->
	i = 0
	for i in [j...k]
		chartab[i].x += dx
		chartab[i].y += dy

# finds the bounding rectangle and vertical position

get_size = (j, k)->
	i = 0
	min_x = chartab[j].x
	max_x = chartab[j].x
	min_y = chartab[j].y
	max_y = chartab[j].y
	for i in [(j + 1)...k]
		if (chartab[i].x < min_x)
			min_x = chartab[i].x
		if (chartab[i].x > max_x)
			max_x = chartab[i].x
		if (chartab[i].y < min_y)
			min_y = chartab[i].y
		if (chartab[i].y > max_y)
			max_y = chartab[i].y
	h = max_y - min_y + 1
	w = max_x - min_x + 1
	y = min_y
	return [h,w,y]

displaychar = (c) ->
	__emit_char(c)

__emit_char = (c) ->
	if (yindex == YMAX)
		return
	if !chartab[yindex]?
		debugger
	chartab[yindex].c = c
	chartab[yindex].x = emit_x
	chartab[yindex].y = 0
	yindex++
	emit_x++

__emit_str = (s) ->
	i = 0
	for i in [0...s.length]
		__emit_char(s[i])

emit_number = (p, emit_sign) ->
	tmpString = ""
	i = 0
	switch (p.k)
		when NUM
			tmpString = p.q.a.toString()
			if (tmpString[0] == '-' && emit_sign == 0)
				tmpString = tmpString.substring(1)
			for i in [0...tmpString.length]
				__emit_char(tmpString[i])
			tmpString = p.q.b.toString()
			if (tmpString == "1")
				break
			__emit_char('/')
			for i in [0...tmpString.length]
				__emit_char(tmpString[i])

		when DOUBLE
			tmpString = doubleToReasonableString(p.d)
			if (tmpString[0] == '-' && emit_sign == 0)
				tmpString = tmpString.substring(1)
			for i in [0...tmpString.length]
				__emit_char(tmpString[i])

# a and b are glyphs
cmpGlyphs = (a, b) ->

	if (a.y < b.y)
		return -1

	if (a.y > b.y)
		return 1

	if (a.x < b.x)
		return -1

	if (a.x > b.x)
		return 1

	return 0

print_glyphs = ->

	i = 0
	accumulator = ""
	
	# now sort the glyphs by their vertical positions,
	# since we are going to build a string where obviously the
	# "upper" line has to printed out first, followed by
	# a new line, followed by the other lines.
	#qsort(chartab, yindex, sizeof (struct glyph), __cmp)
	subsetOfStack = chartab.slice(0,yindex)
	subsetOfStack.sort(cmpGlyphs)
	chartab = [].concat(subsetOfStack).concat(chartab.slice(yindex))

	x = 0

	y = chartab[0].y

	for i in [0...yindex]

		while (chartab[i].y > y)
			accumulator += printchar('\n')
			x = 0
			y++

		while (chartab[i].x > x)
			accumulator += printchar_nowrap(' ')
			x++

		accumulator += printchar_nowrap(chartab[i].c)

		x++

	return accumulator

buffer = ""

getdisplaystr = ->
	yindex = 0
	level = 0
	emit_x = 0
	emit_expr(pop())
	fill_buf()
	return buffer

fill_buf = ->
	tmpBuffer = buffer
	sIndex = 0
	i = 0

	#qsort(chartab, yindex, sizeof (struct glyph), __cmp)
	subsetOfStack = chartab.slice(0,yindex)
	subsetOfStack.sort(cmpGlyphs)
	chartab = [].concat(subsetOfStack).concat(chartab.slice(yindex))

	x = 0

	y = chartab[0].y

	for i in [0...yindex]

		while (chartab[i].y > y)
			tmpBuffer[sIndex++] = '\n'
			x = 0
			y++

		while (chartab[i].x > x)
			tmpBuffer[sIndex++] = ' '
			x++

		tmpBuffer[sIndex++] = chartab[i].c

		x++

	tmpBuffer[sIndex++] = '\n'


N = 100

class oneElement
	x: 0
	y: 0
	h: 0
	w: 0
	index: 0
	count: 0

elem = []
for elelmIndex in [0...10000]
	elem[elelmIndex] = new oneElement

SPACE_BETWEEN_COLUMNS = 3
SPACE_BETWEEN_ROWS = 1

emit_tensor = (p) ->
	i = 0
	n = 0
	nrow = 0
	ncol = 0
	x = 0
	y = 0
	h = 0
	w = 0
	dx = 0
	dy = 0
	eh = 0
	ew = 0
	row = 0
	col = 0

	if (p.tensor.ndim > 2)
		emit_flat_tensor(p)
		return

	nrow = p.tensor.dim[0]

	if (p.tensor.ndim == 2)
		ncol = p.tensor.dim[1]
	else
		ncol = 1

	n = nrow * ncol

	if (n > N)
		emit_flat_tensor(p)
		return

	# horizontal coordinate of the matrix

	#if 0
	#emit_x += 2; # make space for left paren
	#endif

	x = emit_x

	# emit each element

	for i in [0...n]
		elem[i].index = yindex
		elem[i].x = emit_x
		emit_expr(p.tensor.elem[i])
		elem[i].count = yindex - elem[i].index
		[elem[i].h, elem[i].w, elem[i].y] = get_size(elem[i].index, yindex)

	# find element height and width

	eh = 0
	ew = 0

	for i in [0...n]
		if (elem[i].h > eh)
			eh = elem[i].h
		if (elem[i].w > ew)
			ew = elem[i].w

	# this is the overall height of the matrix

	h = nrow * eh + (nrow - 1) * SPACE_BETWEEN_ROWS

	# this is the overall width of the matrix

	w = ncol * ew + (ncol - 1) * SPACE_BETWEEN_COLUMNS

	# this is the vertical coordinate of the matrix

	y = -(h / 2)

	# move elements around

	for row in [0...nrow]
		for col in [0...ncol]

			i = row * ncol + col

			# first move to upper left corner of matrix

			dx = x - elem[i].x
			dy = y - elem[i].y

			move(elem[i].index, elem[i].index + elem[i].count, dx, dy)

			# now move to official position

			dx = 0

			if (col > 0)
				dx = col * (ew + SPACE_BETWEEN_COLUMNS)

			dy = 0

			if (row > 0)
				dy = row * (eh + SPACE_BETWEEN_ROWS)

			# small correction for horizontal centering

			dx += (ew - elem[i].w) / 2

			# small correction for vertical centering

			dy += (eh - elem[i].h) / 2

			move(elem[i].index, elem[i].index + elem[i].count, dx, dy)

	emit_x = x + w

	###
	if 0

		# left brace

		for (i = 0; i < h; i++) {
			if (yindex == YMAX)
				break
			chartab[yindex].c = '|'
			chartab[yindex].x = x - 2
			chartab[yindex].y = y + i
			yindex++
		}

		# right brace

		emit_x++

		for (i = 0; i < h; i++) {
			if (yindex == YMAX)
				break
			chartab[yindex].c = '|'
			chartab[yindex].x = emit_x
			chartab[yindex].y = y + i
			yindex++
		}

		emit_x++

	endif
	###


emit_flat_tensor = (p) ->
	emit_tensor_inner(p, 0, 0)

emit_tensor_inner = (p, j, k) ->
	i = 0
	__emit_char('(')
	for i in [0...p.tensor.dim[j]]
		if (j + 1 == p.tensor.ndim)
			emit_expr(p.tensor.elem[k])
			k = k + 1
		else
			k = emit_tensor_inner(p, j + 1, k)
		if (i + 1 < p.tensor.dim[j])
			__emit_char(',')
	__emit_char(')')
	return k


