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.StrMeth;
import terse.vm.Expr.Seq;
import terse.vm.Expr.Send;
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) {


    // CMD meth Obj "eval" eval: "Evaluate a string as code in this receiver." TYPE Pro NAME eval PARAMS {String code}
    // CLS Obj GROUP "eval" METH eval: REMARK "Evaluate a string as code in this receiver."

    new JavaMeth(terp.wrap.wrapObj, "eval:", "") {
      public Pro apply(Frame f, Pro r, Pro[] args) {
      Obj self = (Obj) r;
        Str a = (Str) args[0];
        return self.eval (a.str);
      }
    };

    // CMD meth Obj "eval" eval:arg:  "Evaluate a string as code in this receiver." TYPE Pro NAME eval PARAMS {String code} { Pro a}
    // CLS Obj GROUP "eval" METH eval:arg: REMARK "Evaluate a string as code in this receiver."

    new JavaMeth(terp.wrap.wrapObj, "eval:arg:", "") {
      public Pro apply(Frame f, Pro r, Pro[] args) {
      Obj self = (Obj) r;
        Str a = (Str) args[0];
        Pro b = (Pro) args[1];
        return self.eval (a.str, b);
      }
    };

    // CMD meth Obj "eval" eval:arg:arg:  "Evaluate a string as code in this receiver." TYPE Pro NAME eval PARAMS {String code} { Pro a} { Pro b}
    // CLS Obj GROUP "eval" METH eval:arg:arg: REMARK "Evaluate a string as code in this receiver."

    new JavaMeth(terp.wrap.wrapObj, "eval:arg:arg:", "") {
      public Pro apply(Frame f, Pro r, Pro[] args) {
      Obj self = (Obj) r;
        Str a = (Str) args[0];
        Pro b = (Pro) args[1];
        Pro c = (Pro) args[2];
        return self.eval (a.str, b, c);
      }
    };

    // CMD meth SysCls "io" files  "Return list of tuples: names, mtime, length." TYPE Vec NAME files PARAMS {Terp terp}
    // CLS SysCls GROUP "io" METH files REMARK "Return list of tuples: names, mtime, length."

    new JavaMeth(terp.wrap.wrapSysCls, "files", "") {
      public Pro apply(Frame f, Pro r, Pro[] args) {
        return Sys.files (terp);
      }
    };

    // CMD meth SysCls "sys" envAt: "Interpreter Environment" TYPE String NAME envAt PARAMS {Terp terp} { String a}
    // CLS SysCls GROUP "sys" METH envAt: REMARK "Interpreter Environment"

    new JavaMeth(terp.wrap.wrapSysCls, "envAt:", "") {
      public Pro apply(Frame f, Pro r, Pro[] args) {
        Str a = (Str) args[0];
        String z = Sys.envAt (terp,a.str);
        return (z == null) ? terp.instNil : terp.newStr(z);
      }
    };

    // CMD meth SysCls "sys" trigraphs "keyboard character substitution dict" TYPE Pro NAME trigraphs PARAMS {Terp terp}
    // CLS SysCls GROUP "sys" METH trigraphs REMARK "keyboard character substitution dict"

    new JavaMeth(terp.wrap.wrapSysCls, "trigraphs", "") {
      public Pro apply(Frame f, Pro r, Pro[] args) {
        return Sys.trigraphs (terp);
      }
    };

    // CMD meth Blk "macro" implode,imp  "Implode strings and ints (as chars) and subvectors." TYPE Str NAME implode PARAMS 
    // CLS Blk GROUP "macro" METH implode,imp REMARK "Implode strings and ints (as chars) and subvectors."

    new JavaMeth(terp.wrap.wrapBlk, "implode", "imp") {
      public Pro apply(Frame f, Pro r, Pro[] args) {
      Blk self = (Blk) r;
        return self.implode ();
      }
    };

    // CMD meth Blk "macro" dyct "Construct a Dict from a list of tuples." TYPE Dict NAME dyct PARAMS 
    // CLS Blk GROUP "macro" METH dyct REMARK "Construct a Dict from a list of tuples."

    new JavaMeth(terp.wrap.wrapBlk, "dyct", "") {
      public Pro apply(Frame f, Pro r, Pro[] args) {
      Blk self = (Blk) r;
        return self.dyct ();
      }
    };

    // CMD meth Num "binop" == "eq two Nums" TYPE Num NAME _eq_ PARAMS {Num a}
    // CLS Num GROUP "binop" METH == REMARK "eq two Nums"

    new JavaMeth(terp.wrap.wrapNum, "==", "") {
      public Pro apply(Frame f, Pro r, Pro[] args) {
      Num self = (Num) r;
        Num a = (Num) args[0];
        return self._eq_ (a);
      }
    };

    // CMD meth Num "binop" != "ne two Nums" TYPE Num NAME _ne_ PARAMS {Num a}
    // CLS Num GROUP "binop" METH != REMARK "ne two Nums"

    new JavaMeth(terp.wrap.wrapNum, "!=", "") {
      public Pro apply(Frame f, Pro r, Pro[] args) {
      Num self = (Num) r;
        Num a = (Num) args[0];
        return self._ne_ (a);
      }
    };

    // CMD meth Num "binop" < "lt two Nums" TYPE Num NAME _lt_ PARAMS {Num a}
    // CLS Num GROUP "binop" METH < REMARK "lt two Nums"

    new JavaMeth(terp.wrap.wrapNum, "<", "") {
      public Pro apply(Frame f, Pro r, Pro[] args) {
      Num self = (Num) r;
        Num a = (Num) args[0];
        return self._lt_ (a);
      }
    };

    // CMD meth Num "binop" <= "le two Nums" TYPE Num NAME _le_ PARAMS {Num a}
    // CLS Num GROUP "binop" METH <= REMARK "le two Nums"

    new JavaMeth(terp.wrap.wrapNum, "<=", "") {
      public Pro apply(Frame f, Pro r, Pro[] args) {
      Num self = (Num) r;
        Num a = (Num) args[0];
        return self._le_ (a);
      }
    };

    // CMD meth Num "binop" > "gt two Nums" TYPE Num NAME _gt_ PARAMS {Num a}
    // CLS Num GROUP "binop" METH > REMARK "gt two Nums"

    new JavaMeth(terp.wrap.wrapNum, ">", "") {
      public Pro apply(Frame f, Pro r, Pro[] args) {
      Num self = (Num) r;
        Num a = (Num) args[0];
        return self._gt_ (a);
      }
    };

    // CMD meth Num "binop" >= "ge two Nums" TYPE Num NAME _ge_ PARAMS {Num a}
    // CLS Num GROUP "binop" METH >= REMARK "ge two Nums"

    new JavaMeth(terp.wrap.wrapNum, ">=", "") {
      public Pro apply(Frame f, Pro r, Pro[] args) {
      Num self = (Num) r;
        Num a = (Num) args[0];
        return self._ge_ (a);
      }
    };

    // CMD meth Num "binop" + "add two Nums" TYPE Num NAME _pl_ PARAMS {Num a}
    // CLS Num GROUP "binop" METH + REMARK "add two Nums"

    new JavaMeth(terp.wrap.wrapNum, "+", "") {
      public Pro apply(Frame f, Pro r, Pro[] args) {
      Num self = (Num) r;
        Num a = (Num) args[0];
        return self._pl_ (a);
      }
    };

    // CMD meth Num "binop" - "add two Nums" TYPE Num NAME _mi_ PARAMS {Num a}
    // CLS Num GROUP "binop" METH - REMARK "add two Nums"

    new JavaMeth(terp.wrap.wrapNum, "-", "") {
      public Pro apply(Frame f, Pro r, Pro[] args) {
      Num self = (Num) r;
        Num a = (Num) args[0];
        return self._mi_ (a);
      }
    };

    // CMD meth Num "binop" | "bitwise-or two Nums as 32bit integers" TYPE Num NAME _or_ PARAMS {Num a}
    // CLS Num GROUP "binop" METH | REMARK "bitwise-or two Nums as 32bit integers"

    new JavaMeth(terp.wrap.wrapNum, "|", "") {
      public Pro apply(Frame f, Pro r, Pro[] args) {
      Num self = (Num) r;
        Num a = (Num) args[0];
        return self._or_ (a);
      }
    };

    // CMD meth Num "binop" ^ "bitwise-xor two Nums as 32bit integers" TYPE Num NAME _xo_ PARAMS {Num a}
    // CLS Num GROUP "binop" METH ^ REMARK "bitwise-xor two Nums as 32bit integers"

    new JavaMeth(terp.wrap.wrapNum, "^", "") {
      public Pro apply(Frame f, Pro r, Pro[] args) {
      Num self = (Num) r;
        Num a = (Num) args[0];
        return self._xo_ (a);
      }
    };

    // CMD meth Num "binop" * "multiply two Nums" TYPE Num NAME _ti_ PARAMS {Num a}
    // CLS Num GROUP "binop" METH * REMARK "multiply two Nums"

    new JavaMeth(terp.wrap.wrapNum, "*", "") {
      public Pro apply(Frame f, Pro r, Pro[] args) {
      Num self = (Num) r;
        Num a = (Num) args[0];
        return self._ti_ (a);
      }
    };

    // CMD meth Num "binop" / "divide two Nums" TYPE Num NAME _di_ PARAMS {Num a}
    // CLS Num GROUP "binop" METH / REMARK "divide two Nums"

    new JavaMeth(terp.wrap.wrapNum, "/", "") {
      public Pro apply(Frame f, Pro r, Pro[] args) {
      Num self = (Num) r;
        Num a = (Num) args[0];
        return self._di_ (a);
      }
    };

    // CMD meth Num "binop" % "modulo two Nums" TYPE Num NAME _mo_ PARAMS {Num a}
    // CLS Num GROUP "binop" METH % REMARK "modulo two Nums"

    new JavaMeth(terp.wrap.wrapNum, "%", "") {
      public Pro apply(Frame f, Pro r, Pro[] args) {
      Num self = (Num) r;
        Num a = (Num) args[0];
        return self._mo_ (a);
      }
    };

    // CMD meth Num "binop" & "bitwise-and two Nums as 32bit integers" TYPE Num NAME _an_ PARAMS {Num a}
    // CLS Num GROUP "binop" METH & REMARK "bitwise-and two Nums as 32bit integers"

    new JavaMeth(terp.wrap.wrapNum, "&", "") {
      public Pro apply(Frame f, Pro r, Pro[] args) {
      Num self = (Num) r;
        Num a = (Num) args[0];
        return self._an_ (a);
      }
    };

    // CMD meth Str "access" applySubstitutions "" TYPE Str NAME applySubstitutions PARAMS 
    // CLS Str GROUP "access" METH applySubstitutions REMARK ""

    new JavaMeth(terp.wrap.wrapStr, "applySubstitutions", "") {
      public Pro apply(Frame f, Pro r, Pro[] args) {
      Str self = (Str) r;
        return self.applySubstitutions ();
      }
    };

    // CMD meth Vec "access" len "return length of the Vec" TYPE Num NAME _len PARAMS 
    // CLS Vec GROUP "access" METH len REMARK "return length of the Vec"

    new JavaMeth(terp.wrap.wrapVec, "len", "") {
      public Pro apply(Frame f, Pro r, Pro[] args) {
      Vec self = (Vec) r;
        return self._len ();
      }
    };

    // CMD meth Vec "access" at: "get element at index a, modulo length of Vec" TYPE Pro NAME at_ PARAMS {Num a}
    // CLS Vec GROUP "access" METH at: REMARK "get element at index a, modulo length of Vec"

    new JavaMeth(terp.wrap.wrapVec, "at:", "") {
      public Pro apply(Frame f, Pro r, Pro[] args) {
      Vec self = (Vec) r;
        Num a = (Num) args[0];
        return self.at_ (a);
      }
    };

    // CMD meth Vec "access" at:put:,at:p:  "put element b at given a, modulo length of Vec" TYPE void NAME at_put_ PARAMS {Num a} { Pro b}
    // CLS Vec GROUP "access" METH at:put:,at:p: REMARK "put element b at given a, modulo length of Vec"

    new JavaMeth(terp.wrap.wrapVec, "at:put:", "at:p:") {
      public Pro apply(Frame f, Pro r, Pro[] args) {
      Vec self = (Vec) r;
        Num a = (Num) args[0];
        Pro b = (Pro) args[1];
        self.at_put_ (a, b);
        return self;
      }
    };

    // CMD meth Vec "access" append:,ap: "add new element ato end of Vec" TYPE void NAME append_ PARAMS {Pro a}
    // CLS Vec GROUP "access" METH append:,ap: REMARK "add new element ato end of Vec"

    new JavaMeth(terp.wrap.wrapVec, "append:", "ap:") {
      public Pro apply(Frame f, Pro r, Pro[] args) {
      Vec self = (Vec) r;
        Pro a = (Pro) args[0];
        self.append_ (a);
        return self;
      }
    };


  }


  public void installClasses(final Terp terp) {
    wrapProCls = terp.tProCls;
    wrapPro = terp.tPro;

    
    this.wrapSuperCls = terp.clss.get("supercls");
    if (this.wrapSuperCls == null) {
      this.wrapSuperCls = new Cls(terp.tMetaclsCls, terp, "SuperCls", this.wrapProCls);
    }
    this.wrapSuper      = terp.clss.get("super");
    if (this.wrapSuper == null) {
      this.wrapSuper = new Cls(this.wrapSuperCls, terp, "Super", this.wrapPro);
    }
          
    this.wrapObjCls = terp.clss.get("objcls");
    if (this.wrapObjCls == null) {
      this.wrapObjCls = new Cls(terp.tMetaclsCls, terp, "ObjCls", this.wrapProCls);
    }
    this.wrapObj      = terp.clss.get("obj");
    if (this.wrapObj == null) {
      this.wrapObj = new Cls(this.wrapObjCls, terp, "Obj", this.wrapPro);
    }
          
    this.wrapSysCls = terp.clss.get("syscls");
    if (this.wrapSysCls == null) {
      this.wrapSysCls = new Cls(terp.tMetaclsCls, terp, "SysCls", this.wrapObjCls);
    }
    this.wrapSys      = terp.clss.get("sys");
    if (this.wrapSys == null) {
      this.wrapSys = new Cls(this.wrapSysCls, terp, "Sys", this.wrapObj);
    }
          
    this.wrapBogusCls = terp.clss.get("boguscls");
    if (this.wrapBogusCls == null) {
      this.wrapBogusCls = new Cls(terp.tMetaclsCls, terp, "BogusCls", this.wrapSysCls);
    }
    this.wrapBogus      = terp.clss.get("bogus");
    if (this.wrapBogus == null) {
      this.wrapBogus = new Cls(this.wrapBogusCls, terp, "Bogus", this.wrapSys);
    }
          
    this.wrapBogus.eval("self defineInstanceVars: 'slot1 slot2 slot3'");
            
    this.wrapUndefinedCls = terp.clss.get("undefinedcls");
    if (this.wrapUndefinedCls == null) {
      this.wrapUndefinedCls = new Cls(terp.tMetaclsCls, terp, "UndefinedCls", this.wrapObjCls);
    }
    this.wrapUndefined      = terp.clss.get("undefined");
    if (this.wrapUndefined == null) {
      this.wrapUndefined = new Cls(this.wrapUndefinedCls, terp, "Undefined", this.wrapObj);
    }
          
    this.wrapBlkCls = terp.clss.get("blkcls");
    if (this.wrapBlkCls == null) {
      this.wrapBlkCls = new Cls(terp.tMetaclsCls, terp, "BlkCls", this.wrapObjCls);
    }
    this.wrapBlk      = terp.clss.get("blk");
    if (this.wrapBlk == null) {
      this.wrapBlk = new Cls(this.wrapBlkCls, terp, "Blk", this.wrapObj);
    }
          
    this.wrapNumCls = terp.clss.get("numcls");
    if (this.wrapNumCls == null) {
      this.wrapNumCls = new Cls(terp.tMetaclsCls, terp, "NumCls", this.wrapObjCls);
    }
    this.wrapNum      = terp.clss.get("num");
    if (this.wrapNum == null) {
      this.wrapNum = new Cls(this.wrapNumCls, terp, "Num", this.wrapObj);
    }
          
    this.wrapBufCls = terp.clss.get("bufcls");
    if (this.wrapBufCls == null) {
      this.wrapBufCls = new Cls(terp.tMetaclsCls, terp, "BufCls", this.wrapObjCls);
    }
    this.wrapBuf      = terp.clss.get("buf");
    if (this.wrapBuf == null) {
      this.wrapBuf = new Cls(this.wrapBufCls, terp, "Buf", this.wrapObj);
    }
          
    this.wrapStrCls = terp.clss.get("strcls");
    if (this.wrapStrCls == null) {
      this.wrapStrCls = new Cls(terp.tMetaclsCls, terp, "StrCls", this.wrapObjCls);
    }
    this.wrapStr      = terp.clss.get("str");
    if (this.wrapStr == null) {
      this.wrapStr = new Cls(this.wrapStrCls, terp, "Str", this.wrapObj);
    }
          
    this.wrapVecCls = terp.clss.get("veccls");
    if (this.wrapVecCls == null) {
      this.wrapVecCls = new Cls(terp.tMetaclsCls, terp, "VecCls", this.wrapObjCls);
    }
    this.wrapVec      = terp.clss.get("vec");
    if (this.wrapVec == null) {
      this.wrapVec = new Cls(this.wrapVecCls, terp, "Vec", this.wrapObj);
    }
          
    this.wrapDictCls = terp.clss.get("dictcls");
    if (this.wrapDictCls == null) {
      this.wrapDictCls = new Cls(terp.tMetaclsCls, terp, "DictCls", this.wrapObjCls);
    }
    this.wrapDict      = terp.clss.get("dict");
    if (this.wrapDict == null) {
      this.wrapDict = new Cls(this.wrapDictCls, terp, "Dict", this.wrapObj);
    }
          
  }
  
  public Cls wrapProCls;
  public Cls wrapPro;
        
  public Cls wrapSuperCls;
  public Cls wrapSuper;
        
  public Cls wrapObjCls;
  public Cls wrapObj;
        
  public Cls wrapSysCls;
  public Cls wrapSys;
        
  public Cls wrapBogusCls;
  public Cls wrapBogus;
        
  public Cls wrapUndefinedCls;
  public Cls wrapUndefined;
        
  public Cls wrapBlkCls;
  public Cls wrapBlk;
        
  public Cls wrapNumCls;
  public Cls wrapNum;
        
  public Cls wrapBufCls;
  public Cls wrapBuf;
        
  public Cls wrapStrCls;
  public Cls wrapStr;
        
  public Cls wrapVecCls;
  public Cls wrapVec;
        
  public Cls wrapDictCls;
  public Cls wrapDict;
        


}

