import cairo

lines = []

maxLevel = 0

with open("genealogy.txt") as file:
  lines = file.readlines()
  lines = [l.rstrip() for l in lines]

def lineLevel(l):
  return (len(l) - len(l.lstrip())) / 2

def processLine(n):
  global maxLevel

  line = lines[n]

  level = lineLevel(line)

  if level > maxLevel:
    maxLevel = level

  sameLevelLines = [n]

  for i in range(n + 1,len(lines)):
    level2 = lineLevel(lines[i])

    if (level2 == level):
      sameLevelLines.append(i)
    elif (level2 < level):
      break

  result = { "info": None, "father": None, "mother": None, "generation": level }

  item = 0 if len(sameLevelLines) == 3 else 1

  for l in sameLevelLines:
    if item == 0:
      result["info"] = lines[l].lstrip()
    else:
       lineSplit = lines[l].split(":")
       personName = lineSplit[0].lstrip()
       personInfo = lineSplit[1].lstrip() if len(lineSplit) > 1 else None

       person = { "name": personName, "info": personInfo, "parents": None }

       if l < len(lines) - 1 and lineLevel(lines[l + 1]) > level:
         person["parents"] = processLine(l + 1)

       if item == 1:
         result["father"] = person
       else:
         result["mother"] = person

    item += 1


  return result

def personStr(p):
  return p["name"]

def printTree(tree, indent):
  if tree == None:
    return

  indentStr = " " * indent

  if tree["info"] != None:
    print(indentStr + tree["info"])

  if tree["father"] != None:
    print(indentStr + personStr(tree["father"]))
    printTree(tree["father"]["parents"],indent + 1)

  if tree["mother"] != None:
    print(indentStr + personStr(tree["mother"]))
    printTree(tree["mother"]["parents"],indent + 1)

ROW_HEIGHT = 100
ITEM_WIDTH = 90

BOX_WIDTH = 85
BOX_HEIGHT = 30

def treeWidth(tree):
  if tree == None:
    return ITEM_WIDTH

  lrWidth = 0

  if tree["mother"] != None:
    lrWidth += treeWidth(tree["mother"]["parents"])

  if tree["father"] != None:
    lrWidth += treeWidth(tree["father"]["parents"])

  return lrWidth if lrWidth > ITEM_WIDTH else ITEM_WIDTH

tree = processLine(0)

imageWidth = treeWidth(tree)

surface = cairo.SVGSurface("out.svg",imageWidth,(maxLevel + 2) * ROW_HEIGHT)
context = cairo.Context(surface)

def drawText(t,x,y,size=8):
  global context
  context.set_font_size(size)
  e = context.text_extents(t)
  context.move_to(x - e.width / 2,y)
  context.show_text(t)
  context.stroke()

def drawPerson(p,x,y,size = 1):
  global context

  w = BOX_WIDTH * size
  h = BOX_HEIGHT * size

  x = x - w / 2
  y = y - h / 2

  context.rectangle(x,y,w,h)
  context.set_source_rgb(1,1,1)
  context.fill()
  context.set_source_rgb(0,0,0)
  context.rectangle(x,y,w,h)
  context.stroke()

  textSize = size * 6

  drawText(p["name"],x + w / 2,y + 2 + textSize,textSize)
  drawText(p["info"],x + w / 2,y + 4 + 2 * textSize,max(6,textSize / 2))

def drawLine(x,y,x2,y2):
  global context
  context.move_to(x,y)
  context.curve_to(x,y + 100,x2,y2 - 100,x2,y2)
  context.stroke()

def drawTree(tree,x,y):
  global context
 
  if tree == None:
    return

  treeW = treeWidth(tree)

  drawX = x - treeW / 2
  nextY = y + ROW_HEIGHT

  boxSize = max(1,(3 - tree["generation"]) * 2)

  if tree["info"] != None:
    drawText(tree["info"],x,y + boxSize * BOX_HEIGHT)

  if tree["father"] != None:
    w = treeWidth(tree["father"]["parents"])
    drawX += w / 2;
    drawLine(x,y,drawX,nextY)
    drawTree(tree["father"]["parents"],drawX,nextY)
    drawPerson(tree["father"],drawX,nextY,boxSize)
    drawX += w / 2;

  if tree["mother"] != None:
    drawX += treeWidth(tree["mother"]["parents"]) / 2
    drawLine(x,y,drawX,nextY)
    drawTree(tree["mother"]["parents"],drawX,nextY)
    drawPerson(tree["mother"],drawX,nextY,boxSize)

printTree(tree,0)
drawTree(tree,imageWidth / 2,0)

surface.finish()
