/**
 * Class LexBase - string representation of a class/an interface
 * in the following layout:
 * package
 * import
 * modifier classname extends implements{
 *  fields;
 *  methods;
 * }
 */

package rmi.rmiSynth.lex;

public class LexBase extends LexStructure {
  private final String CLASS = "class";
  private final String INTERFACE = "interface";
  private final String EXT = "extends";
  private final String IMP = "implements";
  private final String IMPORT = "import";
  private final String PACK = "package";
  //private Object o;               //Object which class will be represented
  private boolean kind;         //Class (true) or interface (false)
  private Reflector ref;
  private StringVector packages;    //"package" clause
  private StringVector imports; //"import" clauses
  private StringVector extend;  //"extends" clauses
  private StringVector implem;  //"implements" clauses
  private LexMethod[] methods;  //Methods of a class
  private LexField[] fields;        //Fields of a class
  private boolean hasConstructor;
  private LexMethod constr;     //constructor method
  private boolean hasMain;
  private LexMethod m;          //main method

  /**
   * Constructor
   * @param _kind Class or interface
   * @param _name Class/interface name
   * @param _o Object which will be hacked
   */
  public LexBase(boolean isInterface, String _name, Object o) {
    kind = isInterface;
    ref = new Reflector(o);
    if (isInterface)
      getType().setName(INTERFACE);
    else
      getType().setName(CLASS);

    getType().setArray(false);
    setName(_name);
    setModif(ref.getModifiers());
    setFields(ref.getFields());
    setMethods(ref.getMethods());
    extend = new StringVector();
    implem = new StringVector();
    imports = new StringVector();
    packages = new StringVector();
    constr = new LexMethod();
    hasConstructor = false;
    hasMain = false;
  }

  /**
   * Returns true if class
   * @return kind
   */
  public boolean isClass() {
    return kind;
  }

  /**
   * Returns true if interface
   * @return !kind
   */
  public boolean isInterface() {
    return !kind;
  }

  /**
   * Adds package statement
   * @param packName Package name
   */
  public void addPackage(String packName) {
    packages.add(packName);
  }

  /**
   * Returns package statement
   * @return s Package name
   */
  public String packToString() {
    String s = "";
    for (int i = 0; i < packages.size(); i++) {
      s = s + PACK + " " + packages.elementAt(i) + ";\n";
    }
    return s;
  }

  /**
   * Adds import statement
   * @param impPack Import name
   */
  public void addImport(String impPack) {
    imports.add(impPack);
  }

  /**
   * Returns import statement
   * @return s Import names
   */
  public String importToString() {
    String s = "";
    for (int i = 0; i < imports.size(); i++) {
      s = s + IMPORT + " " + imports.elementAt(i) + ";\n";
    }
    return s;
  }

  /**
   * Adds extends/implements statement
   * @param action Extend or implement
   * @param className Inherited class
   */
  public void addInherit(String action, String className) {
    if (action.equals(EXT)) {   //extends
      extend.add(className);
    }
    if (action.equals(IMP)) {   //implements
      implem.add(className);
    }
  }

  /**
   * Gets fields
   * @return fileds
   */
  public LexField[] getFields() {
    return fields;
  }

  /**
   * Sets fields
   * @param _fields Fields
   */
  public void setFields(LexField[] _fields) {
    fields = _fields;
  }

  /**
   * Gets methods
   * @return methods
   */
  public LexMethod[] getMethods() {
    return methods;
  }

  /**
   * Sets methods
   * @param _methods Methods
   */
  public void setMethods(LexMethod[] _methods) {
    methods = _methods;
  }

  /**
   * Sets constructor for output code
   * @param exName Class name
   */
  public void setConstr(String exName) {
    constr = new LexMethod();
    constr.getModif().setVisibility(1);     //public
    constr.setName(this.getName());         //ClassName
    constr.getBody().setRet(false);         //{
    constr.getBody().setDelegObj("");       //this.
    constr.getBody().setName("super");      //super();
    hasConstructor = true;                  //}
  }

  /**
   * Gets constructor for output code
   * @return constr Constructor
   */
  public LexMethod getConstr() {
    return constr;
  }

  /**
   * Sets main method for output code
   * @param code Main method code
   */
  public void setMain(String code) {
    m = new LexMethod();
    m.getModif().setVisibility(1);      //public
    m.getModif().setStatic(true);       //static
    m.getType().setName("void");        //void
    m.setName("main");                  //main
    m.setParams(new LexParam[1]);       //(String args[])
    LexParam p[] = m.getParams();       //{
    p[0] = new LexParam();
    p[0].setName("args[]");
    p[0].getType().setName("String");
    p[0].getType().setArray(true);
    LexBody b = m.getBody();
    b.setTryCatch(true);                //try/catch
    b.setCode(code);                    //assign code
    hasMain = true;                     //}
  }

  /**
   * Gets main method for output code
   * @return m Main method
   */
  public LexMethod getMain() {
    return m;
  }

  /**
   * Class/interface to string
   */
  public String toString() {
    return toString(true, true, false);
  }

  /**
   * Class/interface to string
   * @param onlyPublic Public members only
   * @param noMain No main method
   * @param withDelegate With cutils.delegate field
   * @return s
   */
  public String toString(boolean onlyPublic, boolean noMain, boolean withDelegate) {
    String s = "";
    int i;
    s = s + "//Created automatically by CentiJ\n";
    //s = s + packToString();   ???
    s = s + "package graphics.raytracer;\n";

    s = s + importToString();                   //imports
    s = s + getHeader();                        //modifiers + name
    s = s + getExtendsImplementsString(EXT) + getExtendsImplementsString(IMP);  //extends and implements
    s = s + "{\n\n";
    if (withDelegate) {                         //cutils.delegate field
      s = s + getDelegateString() + "\n";
    }
    if (isClass() && hasConstructor) {          //constructor
      s = s + constr.toString(true, false);
    }
    for (i = 0; i < fields.length; i++) {       //fields
      if (onlyPublic && !fields[i].getModif().isPublic()) {
        continue;
      }
      s = s + fields[i].toString();
    }
    s = s + "\n";
    for (i = 0; i < methods.length; i++) {      //general methods
      if (onlyPublic && !methods[i].getModif().isPublic()) {
        continue;
      }
      if (noMain && methods[i].getName().equals("main")) {
        continue;
      }
      if (methods[i].getName().equals("hashCode")) {
        continue;
      } //rmic
      if (methods[i].getName().equals("clone")) {
        continue;
      }     //rmic
      if (methods[i].getName().equals("toString")) {
        continue;
      } //rmic
      s = s + methods[i].toString(kind, false);
    }
    if (isClass() && hasMain) {                 //main method
      s = s + m.toString(true, true);
    }

    s = s + "}";
    return s;
  }

  /**
   * Returns string of extends/implements
   * @return s
   */
  public String getExtendsImplementsString(String action) {
    String s = "";
    StringVector sv = new StringVector();
    if (action.equals(EXT)) {   //extends
      sv = extend;
    }
    if (action.equals(IMP)) {   //implements
      sv = implem;
    }
    int svSize = sv.size();
    if (svSize > 0) {
      s = s + "\n\t" + action + " " + sv.toCSV();
    }
    return s;
  }

  /**
   * Get cutils.delegate field
   * @return s
   */
  public String getDelegateString() {
    String s = "";
    if (isClass()) {
      s = s + "\tprivate ";
      s = s + ref.getName();
      s = s + " v = new ";
      s = s + ref.getName();
      s = s + "();\n";
      ;
    }
    return s;
  }
}