proc wrap {file} {
  set f [open $file]
  while {[gets $f line] >= 0} {
    if [regexp {^// =(.*)} [string trim $line] - cmd] {
      puts stderr "Triggers on <$line>"
      while {[gets $f line2] >= 0} {
        if [regexp {^//(.*)} [string trim $line2] - cmd2] {
          append cmd " $cmd2"
        } else {
          break
        }
      }

      set verb [lindex $cmd 0]
      puts stderr "Verb is <$verb>"
      puts stderr "Cmd is <$cmd>"

      if {$verb == "meth"} {
        puts stderr "Got meth."

        set cls [lindex $cmd 1]
        set group [lindex $cmd 2]
        set kws [lindex $cmd 3]
        set remark [lindex $cmd 4]

        set kw [split $kws ","]
        set kw0 [lindex $kw 0]
        set kw1 [lindex $kw 1]

        set decl $line2
        while {[gets $f line3] >= 0} {
          append decl " " $line3
          if [regexp {\}$} [string trim $line3] - cmd2] {
            break
          }
        }
        if [regexp {\s*public\s+(abstract\s+)?(static\s+)?(\w+)\s+(\w+)\s*[(]([^{}]*)[)]} $decl - abstract static type name params] {
           set pp [split $params ","]
           puts "    // CMD $cmd TYPE $type NAME $name PARAMS $pp"
           puts "    // CLS $cls GROUP \"$group\" METH $kws REMARK \"$remark\""
	   puts ""
	   puts "    new JavaMeth(terp.wrap.cls$cls, \"$kw0\", \"$kw1\") {"
	   puts "      public Pro apply(Frame frame, Pro r, Pro\[\] args) {"
	   if [string match {[A-Za-z_]*Cls} $cls] {
	     if ![string match static* $static] {error "Should be static"}
	     puts "        Cls self = terp.wrap.cls$cls;"
	     set call "[string range $cls 0 end-3].$name (terp"  ;# Drop the trailing Cls.
	     set pp [lrange $pp 1 end]  ;# Pop off Terp.
	     set comma ","
	   } else {
	     if {$static != ""} {error "Should not be static"}
	     puts "      $cls self = ($cls) r;"
	     set call "self.$name ("
	     set comma ""
	   }
	   set i 0
	   foreach p $pp {
	     set atype [lindex $p 0]
	     set aname [lindex $p 1]
	     append call "$comma"
	     set c [format "%c" [expr 97+$i]]
             if {$atype eq "String"} {
               # Special String case.
	       puts "        Str $c = (Str) args\[$i\];"
	       append call "$c.str"
             } elseif {$atype eq "int"} {
               # Special int case.
	       puts "        Num $c = (Num) args\[$i\];"
	       append call "$c.toNearestInt()"
             } elseif {$atype eq "double"} {
               # Special double case.
	       puts "        Num $c = (Num) args\[$i\];"
	       append call "$c.num"
             } else {
               # Normal TerseTalk types.
	       puts "        $atype $c = ($atype) args\[$i\];"
	       append call "$c"
             }
	     incr i
	     set comma ", "
	   }
	   append call ")"
	   if {$type == "void"} {
	     puts "        $call;"
	     puts "        return self;"
	   } elseif {$type == "String"} {
	     puts "        String z = $call;"
	     puts "        return (z == null) ? terp.instNil : terp.newStr(z);"
	   } elseif {$type == "int"} {
	     puts "        return terp.newNum($call);"
	   } elseif {$type == "double"} {
	     puts "        return terp.newNum($call);"
	   } else {
	     puts "        return $call;"
	   }
	   puts "      }"
	   puts "    };"
	   puts ""
        } else {
          error "Did not find public meth pattern for <$decl>."
        }


      } elseif {$verb == "cls"} {
        set category [lindex $cmd 1]
        set cls [lindex $cmd 2]
        set supercls [lindex $cmd 3]
        set instvars [lindex $cmd 4]
        set vv [split $instvars ","]

        append ::slots [subst {
  public Cls cls${cls}Cls;
  public Cls cls${cls};
        }]

        if [string length $supercls] {
          append ::installClasses [subst {
    this.cls${cls}Cls = terp.clss.get("[string tolower ${cls}Cls]");
    if (this.cls${cls}Cls == null) {
      this.cls${cls}Cls = new Cls(terp.tMetaclsCls, terp, "${cls}Cls", this.cls${supercls}Cls);
    }
    this.cls$cls      = terp.clss.get("[string tolower $cls]");
    if (this.cls$cls == null) {
      this.cls${cls} = new Cls(this.cls${cls}Cls, terp, "${cls}", this.cls${supercls});
    }
          }]
	}

        if [llength $vv] {
            append ::installClasses [subst {
    this.cls${cls}.eval("self defineInstanceVars: '$vv'");
            }]
	}

      } elseif {$verb == "get"} {
        set cls [lindex $cmd 1]
        set type [lindex $cmd 2]
        set slot [lindex $cmd 3]
        set meth [lindex $cmd 4]
        set doc [lindex $cmd 5]

	   puts "    new JavaMeth(terp.wrap.cls$cls, \"$meth\", \"\") {"
	   puts "      public Pro apply(Frame f, Pro r, Pro\[\] args) {"
	   puts "        $cls self = ($cls) r;" 

	   if { $type == "int" } {
	     puts "        return terp.newNum(self.$slot);"
	   } elseif { $type == "double" } {
	     puts "        return terp.newNum(self.$slot);"
	   } elseif { $type == "String" } {
	     puts "        return terp.newStr(self.$slot);"
	   } elseif { $type == {Pro[]} } {
	     puts "        return new Vec(terp, terp.pros(self.$slot));"
	   } elseif { $type == "." } {
	     puts "        return self.$slot;"
	   } else {
	     error "Unknown type <$type> in getter for cls $cls slot $slot"
	   }

	   puts "      }"
	   puts "    };"

      } else {
        error "Dont understand method decl <$decl> after <$cmd>"
      }
    }
  }
}

puts "package terse.vm;

import java.util.Arrays;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.regex.Pattern;

import terse.vm.Cls.JavaMeth;
import terse.vm.Cls.UsrMeth;
import terse.vm.Cls.Meth;
import terse.vm.Cls.StrMeth;
import terse.vm.Expr.Seq;
import terse.vm.Expr.Send;
import terse.vm.Expr.Top;
import terse.vm.Expr.LvName;
import terse.vm.Expr.LvTuple;
import terse.vm.Expr.LValue;
import terse.vm.Terp.Frame;

import terse.vm.Pro.Blk;
import terse.vm.Pro.Buf;
import terse.vm.Pro.Dict;
import terse.vm.Pro.Undefined;
import terse.vm.Pro.Num;
import terse.vm.Pro.Obj;
import terse.vm.Pro.Str;
import terse.vm.Pro.Sys;
import terse.vm.Pro.Vec;
import terse.vm.Usr.Tmp;

public class Wrap extends Static \{

  public void installMethods(final Terp terp) \{

"

foreach j $argv {
  wrap $j
}

puts "
  \}
"

puts "
  public void installClasses(final Terp terp) {
    this.clsProCls = terp.tProCls;
    this.clsPro = terp.tPro;

    $::installClasses
  }
  $::slots
"

puts "
\}
"
