//
// Copyright (c) 2006, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
//   9 Jan 06  Brian Frank  Creation
//

**
** Fantom Disassembler
**
class Fanp
{

//////////////////////////////////////////////////////////////////////////
// Execute
//////////////////////////////////////////////////////////////////////////

  Void execute(Str target)
  {
    colon := target.index(":")
    dot   := target.index(".")
    if (colon  == null) printPod(Pod.find(target))
    else if (dot < 0) printType(Type.find(target))
    else printSlot(Slot.find(target))
  }

  Void executeFile(Str? target)
  {
    scriptFile := File.os(file)
    input := CompilerInput()
    {
      podName        = "script"
      summary        = "script"
      version        = Version("0")
      log.level      = LogLevel.warn
      includeDoc     = true
      isScript       = true
      srcStr         = scriptFile.readAllStr
      srcStrLoc      = Loc.makeFile(scriptFile)
      mode           = CompilerInputMode.str
      output         = CompilerOutputMode.transientPod
    }
    compiler = Compiler(input)
    compiler.compile

    pod := compiler.output.transientPod

    if (target == null)
    {
      printPod(pod)
      return
    }

    dot := target.index(".")
    if (dot < 0)
    {
      printType(pod.type(target))
    }
    else
    {
      typeName := target[0..<dot]
      slotName := target[dot+1..-1]
      printSlot(pod.type(typeName).slot(slotName))
    }
  }

  Void printPod(Pod pod)
  {
    p := printer(pod)
    if (showTables) { p.tables; return }
    p.ftypes
  }

  Void printType(Type t)
  {
    p := printer(t.pod)
    if (showTables) { p.tables; return }
    ftype := ftype(p.pod, t.name)
    p.ftype(ftype)
  }

  Void printSlot(Slot slot)
  {
    p := printer(slot.parent.pod)
    if (showTables) { p.tables; return }
    fslot := fslot(ftype(p.pod, slot.parent.name), slot.name)
    p.slot(fslot)
  }

//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////

  FPrinter printer(Pod pod)
  {
    printer := FPrinter(fpod(pod.name))
    printer.showCode  = showCode
    printer.showLines = showLines
    printer.showIndex = showIndex
    return printer
  }

  FPod fpod(Str podName)
  {
    if (file == null)
    {
      ns := FPodNamespace(null)
      FPod fpod := ns.resolvePod(podName, null)
      fpod.readFully
      return fpod
    }
    else
    {
      return compiler.fpod
    }
  }

  FType ftype(FPod pod, Str typeName)
  {
    ftype := pod.ftypes.find |FType ft->Bool|
    {
      r := pod.typeRef(ft.self)
      return typeName == pod.n(r.typeName)
    }
    if (ftype == null) throw UnknownTypeErr(pod.name + "::" + typeName)
    return ftype
  }

  FSlot fslot(FType ftype, Str slotName)
  {
    FSlot? slot := null

    slot = ftype.ffields.find |FSlot s->Bool|
    {
      return slotName == ftype.fpod.n(s.nameIndex)
    }
    if (slot != null) return slot

    slot = ftype.fmethods.find |FSlot s->Bool|
    {
      return slotName == ftype.fpod.n(s.nameIndex)
    }
    if (slot != null) return slot

    throw UnknownSlotErr(slotName)
  }

//////////////////////////////////////////////////////////////////////////
// Run
//////////////////////////////////////////////////////////////////////////

  Void run(Str[] args)
  {
    if (args.isEmpty) { help; return }

    Str? target := null

    // process args
    for (i:=0; i<args.size; ++i)
    {
      a := args[i]
      if (a.isEmpty) return
      if (a == "-help" || a == "-h" || a == "-?")
      {
        help
        return
      }
      else if (a == "-t") { showTables  = true }
      else if (a == "-c") { showCode    = true }
      else if (a == "-l") { showLines   = true }
      else if (a == "-i") { showIndex   = true }
      else if (a == "-f")
      {
        i += 1
        file = args[i]
      }
      else if (a[0] == '-')
      {
        echo("WARNING: Unknown option $a")
      }
      else
      {
        target = a
      }
    }

    if (target == null && file == null) { help; return }
    if (file == null) execute(target)
    else executeFile(target)
  }

  Void help()
  {
    echo("Fantom Disassembler");
    echo("Usage:");
    echo("  fanp [options] <pod>");
    echo("  fanp [options] <pod>::<type>");
    echo("  fanp [options] <pod>::<type>.<method>");
    echo("Options:");
    echo("  -help, -h, -?  print usage help");
    echo("  -t             print constant pool tables");
    echo("  -c             print code buffers");
    echo("  -l             print line number table");
    echo("  -i             print table indexes in code");
    echo("  -f <file>      disassemble from script file");
  }

//////////////////////////////////////////////////////////////////////////
// Main
//////////////////////////////////////////////////////////////////////////

  static Void main()
  {
    make.run(Env.cur.args)
  }

//////////////////////////////////////////////////////////////////////////
// Fields
//////////////////////////////////////////////////////////////////////////

  Compiler? compiler
  Bool showTables := false
  Bool showCode   := false
  Bool showLines  := false
  Bool showIndex  := false
  Str? file

}