###

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 (isNumericAtom(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 (isNumericAtom(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 (isNumericAtom(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 (isNumericAtom(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 (isfraction(p) || 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


