/**
 * Class Transform provides static methods for transformations of LexBase class
 * @author Roman Yedokov
 */

package rmi.rmiSynth.lex;


public class Transform {
  static final String TABS3 = "\t\t\t";

  /**
   * Substitutes all references to srcClass to tgtClass in the instance of LexBase class
   * (except for clone() method)
   * @param lb Instance of LexBase class
   * @param srcClass Name of the class that needs to be replaced
   * @param tgtClass Name of the class that will be put in
   * @return void
   */
  public static void castIt(LexBase lb, String srcClass, String tgtClass) {
    LexMethod[] mtd;        //methods of the class
    LexParam[] params;      //parameters of a method
    LexBody b;              //body of a method
    LexType mtdType;        //method return type
    String mtdTypeName;     //name of the return type
    String brackets;

    mtd = lb.getMethods();  //get methods from class
    //scan thru all methods
    for (int i = 0; i < mtd.length; i++) {
      brackets = "";
      //skip clone()
      if (stripPackageName(mtd[i].getName()).equals("clone")) {
        continue;       //always returns Object
      }
      mtdType = mtd[i].getType();                           //get method return type
      mtdTypeName = stripPackageName(mtdType.getName());    //get type name
      //if this type is type of srcClass replace it with target class
      if (mtdTypeName.equals(srcClass) || mtdTypeName.equals(srcClass + "[]")) {
        brackets = mtdType.isArray() ? "[]" : "";
        mtd[i].getType().setName(tgtClass + brackets);
        b = mtd[i].getBody();
        b.setDelegObj("( " + tgtClass + brackets + " ) " + b.getDelegObj());
      }
      //now check types of parameters passed to this method
      params = mtd[i].getParams();
      castParams(params, srcClass, tgtClass);
    }
  }

  /**
   * Substitutes all references to srcClass to tgtClass in the array of parameters
   * (except for clone() method)
   * @param prm Array of parameters
   * @param srcClass Name of the class that needs to be replaced
   * @param tgtClass Name of the class that will be put in
   * @return void
   */
  public static void castParams(LexParam[] prm, String srcClass, String tgtClass) {
    LexType prmType;        //parameter type
    String prmTypeName;     //name of the parameter type
    String brackets;
    for (int j = 0; j < prm.length; j++) {
      prmType = prm[j].getType();
      prmTypeName = stripPackageName(prmType.getName());
      if (prmTypeName.equals(srcClass) || prmTypeName.equals(srcClass + "[]")) {
        brackets = prmType.isArray() ? "[]" : "";
        prm[j].getType().setName(tgtClass + brackets);
      }
    }
  }

  /**
   * Removes everything before last dot in a string
   * @param String to be stripped - <java.lang.type>
   * @return Stripped string - <type>
   */
  public static String stripPackageName(String s) {
    int index = s.lastIndexOf('.');
    if (index == -1) return s;
    index++;
    return s.substring(index);
  }

  /**
   * Modifies a LexBase class such way that it complies to RMI interface specifications
   * Adds <extends java.rmi.Remote> to class declaration
   * Adds <throw java.rmi.RemoteException> to method declarations
   * @param lb Instance of LexBase class
   * @return void
   */
  public static void toRMIInterface(LexBase lb) {
    LexMethod[] mtd;
    lb.addPackage("rmi");
    lb.addInherit("extends", "java.rmi.Remote");
    mtd = lb.getMethods();
    for (int i = 0; i < mtd.length; i++) {
      mtd[i].addThrow("java.rmi.RemoteException");
    }

  }

  /**
   * Modifies a LexBase class such way that it complies to RMI server specifications
   * Imports java.rmi.* and java.rmi.registry.*
   * Adds <implements java.rmi.Remote> to class declaration
   * Adds <extends java.rmi.server.UnicastRemoteObject> to class declaration
   * Adds <throw java.rmi.RemoteException> to method declarations
   * @param lb Instance of LexBase class
   * @param ServerInter Name of interface to implement
   * @param ServerClass Name of new class
   */
  public static void toRMIServer(LexBase lb, String ServerInter, String ServerClass) {
    LexMethod[] mtd;
    String code = "";
    mtd = lb.getMethods();
    for (int i = 0; i < mtd.length; i++) {
      mtd[i].addThrow("java.rmi.RemoteException");
    }
    lb.addPackage("rmi");
    lb.addImport("java.rmi.*");
    lb.addImport("java.rmi.registry.*");
    lb.addInherit("implements", ServerInter);
    lb.addInherit("extends", "java.rmi.server.UnicastRemoteObject");
    lb.setConstr(ServerClass);
    lb.getConstr().addThrow("java.rmi.RemoteException");
    code = code + TABS3 + "// Create and install a security manager\n";
    code = code + TABS3 + "System.setSecurityManager(new RMISecurityManager());\n";
    code = code + TABS3 + ServerClass + " rs = new " + ServerClass + "();\n";
    code = code + TABS3 + "//Create the registry and bind the Server class to the registry\n";
    code = code + TABS3 + "LocateRegistry.createRegistry(1099);\n";
    code = code + TABS3 + "Registry r= LocateRegistry.getRegistry();\n";
    code = code + TABS3 + "r.bind(\"" + ServerClass + "\", rs);\n";
    code = code + TABS3 + "System.out.println(" + "\"" + ServerClass + " is running\");\n";

    lb.setMain(code);

  }

  /**
   * Modifies a LexBase class such way that it complies to RMI server specifications
   * Imports java.rmi.* and java.rmi.registry.*
   * @param lb Instance of LexBase class
   * @param ServerInter Name of interface to instanciate
   * @param ServerClass Name of server class to connect to
   * @param ClientClass Name of new class to create
   * @param fwdMethod Method from server which is called
   * @param IP Address of server to connect
   */
  public static void toRMIClient(LexBase lb, String ServerInter,
                                 String ServerClass, String ClientClass, String IP) {
    String code = "";

    lb.addPackage("rmi");
    lb.addImport("java.rmi.*");
    lb.addImport("java.rmi.registry.*");
    code = code + TABS3 + "//locate the server class on remote machine\n";
    code = code + TABS3 + "Registry r = LocateRegistry.getRegistry(" + IP + ");\n";
    code = code + TABS3 + ServerInter + " rs = (" + ServerInter + ")r.lookup(\"" + ServerClass + "\");\n";
    code = code + TABS3 + "//Type in your remote procedure calls below\n\n";

    lb.setMain(code);

  }
}