
import std / [os, strutils, tables]

import nimonyplugins


template traverse*(n: var NifCursor, body: untyped) =
  let n2 = n
  body
  n = n2

proc skip*(n: var NifCursor, count: int) =
  for _ in 0..<count:
    skip n

var knownTypes: seq[SymId]
var knownInstances: Table[SymId, SymId]  # name -> type
var knownOnChanged: Table[SymId, SymId]  # type -> `onChanged` template sym


proc typesTr(n: NifCursor) =
  var n = n
  inc n
  while n.kind != ParRi:
    knownTypes.add n.symId
    inc n
  inc n


proc trAsgn(n: var NifCursor, o: var NifBuilder) =
  var
    fieldName = ""
    access = NifCursor()
    instance = NifCursor()
    emitOnChanged = false
  traverse n:
    inc n
    if n.kind == ParLe and n.exprKind == DotX:
      traverse n:
        access = n
      inc n
      if n.kind == Symbol and n.symId in knownInstances and knownInstances[n.symId] in knownOnChanged:
        instance = n
        inc n
        if n.kind == Symbol:
          fieldName = n.symText
          fieldName.delete fieldName.find('.')..fieldName.high
          emitOnChanged = true
  let info = n.info
  o.takeTree(n)
  if emitOnChanged:
    o.withTree CallS, info:
      o.addSymUse knownOnChanged[knownInstances[instance.symId]], info
      o.addSubtree(instance)
      o.addSubtree(access)
      o.addStrLit fieldName


proc trGvar(n: var NifCursor, o: var NifBuilder) =
  traverse n:
    inc n
    let nameSym = n.symId
    skip n, 3
    if n.kind == Symbol and n.symId in knownTypes:
      knownInstances[nameSym] = n.symId
  o.takeTree(n)


proc trTemplate(n: var NifCursor, o: var NifBuilder) =
  traverse n:
    inc n
    let nameSym = n.symId
    let name = n.symText
    if name.startsWith("onChanged"):
      skip n, 4
      inc n  # params
      inc n  # (first) param
      skip n, 3
      if n.kind == ParLe and n.typeKind == MutT:
        inc n
      if n.kind == Symbol:
        knownOnChanged[n.symId] = nameSym
  o.takeTree(n)


proc trAux(n: var NifCursor, o: var NifBuilder) =
  case n.kind
  of ParLe:
    if n.stmtKind == AsgnS:
      trAsgn n, o
    elif n.stmtKind == GvarS:
      trGvar n, o
    elif n.stmtKind == TemplateS:
      trTemplate n, o
    else:
      o.copyInto(n):
        while n.kind != ParRi:
          trAux(n, o)
  else:
    o.takeTree(n)


proc tr(n: NifCursor): NifBuilder =
  result = createTree()
  let info = n.info
  var n = n
  if n.stmtKind == StmtsS: inc n
  result.withTree StmtsS, info:
    while n.kind != ParRi:
      trAux(n, result)


var inp = loadPluginInput()
var inpTypes = loadPluginInput(os.paramStr(3))

typesTr(inpTypes)
saveTree tr(inp), os.paramStr(2)
