package nxm.sys.intr;

import nxm.sys.lib.Args;
import nxm.sys.lib.Intrinsic;
import nxm.sys.lib.Macro;
import nxm.sys.lib.Midas;

/**
  Calls a macro language procedure or subroutine.

  @author Jeff Schoen

  @see Subroutine
  @see Procedure
*/
public class Call extends Intrinsic {

  /** The index into the macro. */
  int index;

  /** The Midas context that the METHOD or PROCEDURE will execute in. */
  Midas tM;

  /** default constructor */
  public Call () {}

  public int open () {
    if (M.macro == null) {
      M.error("No macro executing ...");
      return FINISH;
    }

    boolean bg = MA.getState("/BG");
    String procname = MA.getU("NAME");
    index = M.macro.lookupIndex (procname);

    if (index<=0) {
      M.error ("Subroutine/Procedure name " + procname + " not found");
    }

    Args targs = M.macro.lines[index];
    if (targs.name.equals("PROCEDURE")) {
      tM = M; // use this Context for a procedure
    }
    else if (targs.name.equals("SUBROUTINE")) {
      if (bg && M.macro.lines[index].cmd.M != M) {
        M.error("Subroutine already in use");
      }

      // create new Context for a subroutine
      //tM = M.macro.cloneContext(M,false); /* Was in 1.8.0/1.9.0 */
      tM = Macro.cloneContext(M,true);   /* Hack for 1.8.1/1.9.1 */

      if (bg) {
        tM.macro = M.macro.cloneOf();
      }

      M.macro.prepBranch(index,tM);
      targs.cmd.M.results.put("THIS",targs.cmd.args);
    }
    else {
      M.error ("Call target is not a PROCEDURE/SUBROUTINE: " + targs);
    }

    // exec Procedure/Subroutine statement here
    Macro.argsToResults (M,args,targs.cmd.args,1);

    // the calling thread executes this as a noop
    if (bg) {
      return NORMAL;
    }

    M.macro.level++;
    M.macro.results[M.macro.level]=M.results;
    M.macro.returns[M.macro.level]=M.macro.nextIndex;
    M.macro.nextIndex = index+1;

    return FINISH;
  }

  public int process () {
    // we only get here if this was a call/bg
    tM.macro.execBranch(index+1,1);
    M.macro.lines[index].cmd.M = M; // mark done

    return FINISH;
  }
}
