package nxm.sys.intr;

import java.lang.reflect.Array;

import nxm.sys.lib.Convert;
import nxm.sys.lib.KeyObject;
import nxm.sys.lib.MidasException;
import nxm.sys.lib.Parser;

/** Handles the creation of an object. Since NeXtMidas 2.1.0 this is just a simple extension of
    {@link Invoke} (in fact invoke is capable of creating new objects if a class is given with
    the method name "&lt;init&gt;" -- this is the internal name Java uses for the constructors). Prior
    to NeXtMidas 2.1.0 this class manually instantiated the classes using the only the no-argument
    constructor.

    @see Invoke
    @author Jeff Schoen, J Czechowski IV
*/
public class New extends Invoke {

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

  public int process () {
    // Get parameters
    String name  = MA.getCS("NAME");
    String label = MA.getU("LABEL");
    Object object;

    if (name.endsWith("]")) { // creating an array
      object = newArray(name, MA.getS("TABLE"));
    } else {                  // creating an object
      nxm.sys.lib.Table table = MA.getTable("TABLE");
      int index = name.indexOf('(');

      if (index > 0) {
        name = name.substring(0,index) + ".<init>" + name.substring(index);
      } else {
        name = name + ".<init>()";
      }
      object = doInvoke(name, true);

      if (table!=null) KeyObject.setKeys(object,table,args,0);
    }

    MR.put(label,object, args);

    return FINISH;
  }

  /** Creates a new array .
      @param name    Array name (e.g. "java.lang.String[3]" or "int[]")
      @param initial Initial value for 1D array with no size specified
      @return        The new array
      @since         NeXtMidas 2.9.0 (BUG 2204)
   */
  private Object newArray (String name, String initial) {
    Object   array;
    Class<?> clazz;

    try {
      clazz = nxm.sys.lib.Util.getClass(name.substring(0,name.indexOf('['))); // base class
    } catch (ClassNotFoundException e) {
      throw new MidasException("Could not find base class for '"+name+"'", e);
    }

    if (name.endsWith("[]")) {  // creating an array with initial values
      if (initial == null)          throw new MidasException("No initial value given for array without dimensions");
      if (!initial.startsWith("{")) throw new MidasException("Illegal syntax for initial value. Must start with '{'");
      if (!initial.endsWith("}"))   throw new MidasException("Illegal syntax for initial value. Must end with '}'");

      Parser p = new Parser(initial.substring(1,initial.length()-1));
      int len  = p.elements();

      array = Array.newInstance(clazz, len);

      for (int i=0; i<len; i++) {
        Object val;
        Object obj = p.get(i+1);

        if      (clazz == Byte.TYPE)      val = Convert.o2b(obj,this);
        else if (clazz == Short.TYPE)     val = Convert.o2i(obj,this);
        else if (clazz == Integer.TYPE)   val = Convert.o2l(obj,this);
        else if (clazz == Long.TYPE)      val = Convert.o2x(obj,this);
        else if (clazz == Float.TYPE)     val = Convert.o2f(obj,this);
        else if (clazz == Double.TYPE)    val = Convert.o2d(obj,this);
        else if (clazz == Boolean.TYPE)   val = Convert.o2z(obj,this);
        else if (clazz == Character.TYPE) val = Convert.o2s(obj,this);
        else                              val = Convert.o2o(obj,this);

        Array.set(array, i, val);
      }
    } else {
      if ((initial != null) && (initial.length() > 0)) {
        throw new MidasException("Cannot include both array size and an initial value");
      }

      int[] dimensionVals = new int[32768];  // contains the dimensions of the array
      int dim = 0;
      int idx = 0;
      int end = 0;

      while ((idx = name.indexOf('[',end)) > 0) {
        end = name.indexOf(']',idx);
        String len = name.substring(idx+1, end);
        dimensionVals[dim++] = Convert.s2l(len, this);
      }

      int[] dimensions = new int[dim];
      System.arraycopy(dimensionVals, 0, dimensions, 0, dim);
      array = Array.newInstance(clazz, dimensions);
    }
    return array;
  }
}
