package be.kuleuven.cs.distrinet.jnome.core.type;


import be.kuleuven.cs.distrinet.chameleon.core.declaration.SimpleNameSignature;
import be.kuleuven.cs.distrinet.chameleon.core.element.Element;
import be.kuleuven.cs.distrinet.chameleon.core.lookup.LookupException;
import be.kuleuven.cs.distrinet.chameleon.core.namespace.RootNamespace;
import be.kuleuven.cs.distrinet.chameleon.oo.language.ObjectOrientedLanguage;
import be.kuleuven.cs.distrinet.chameleon.oo.method.Method;
import be.kuleuven.cs.distrinet.chameleon.oo.method.SimpleNameMethodHeader;
import be.kuleuven.cs.distrinet.chameleon.oo.type.RegularType;
import be.kuleuven.cs.distrinet.chameleon.oo.type.Type;
import be.kuleuven.cs.distrinet.chameleon.oo.type.inheritance.SubtypeRelation;
import be.kuleuven.cs.distrinet.chameleon.oo.variable.RegularMemberVariable;
import be.kuleuven.cs.distrinet.chameleon.support.member.simplename.method.NormalMethod;
import be.kuleuven.cs.distrinet.chameleon.support.modifier.Final;
import be.kuleuven.cs.distrinet.chameleon.workspace.View;
import be.kuleuven.cs.distrinet.jnome.core.language.Java;
import be.kuleuven.cs.distrinet.rejuse.logic.ternary.Ternary;

/**
 * @author Marko van Dooren
 */
public class ArrayType extends RegularType {
  public ArrayType(Type type) {
    super(new SimpleNameSignature(getArrayName(type.name())));
    //FIXME: copy the modifiers?
    //addModifier(type.getAccessModifier());
    _type = type;
    setUniParent(type.parent());
    Java language = type.language(Java.class);
		JavaTypeReference jtr = language.createTypeReference("int");
		
		// JLS7 10.7 Array Members
    RegularMemberVariable var = new RegularMemberVariable(new SimpleNameSignature("length"), jtr);
    var.addModifier(new Final());
    add(var);
    
    String fullyQualifiedName = type.getFullyQualifiedName();
		JavaTypeReference returnType = new ArrayTypeReference((JavaTypeReference) language.createTypeReference(fullyQualifiedName));
    Method clone = new NormalMethod(new SimpleNameMethodHeader("clone", returnType));
    add(clone);
    // JLS3 4.10.3 p.64
    // FIXME May these should be implicit inheritance relations. Not that important, though, as
    //       these cannot be manipulated by programmers anyway.
    addInheritanceRelation(new SubtypeRelation(language.createTypeReference("java.lang.Object")));
    addInheritanceRelation(new SubtypeRelation(language.createTypeReference("java.lang.Cloneable")));
    addInheritanceRelation(new SubtypeRelation(language.createTypeReference("java.io.Serializable")));
  }
  

  
  public ArrayType(Type componentType, int dimension) {
  	this(consAux(componentType,dimension-1));
  }
  
  private static Type consAux(Type componentType, int dimension) {
  	if(dimension <0) {
  		throw new Error();
  	}
  	if(dimension == 0) {
  		return componentType;
  	} else {
  		return new ArrayType(componentType,dimension);
  	}
  }
  
  /**
	 * @param string
	 * @param dimension
	 * @return
	 */
	private static String getArrayName(String string) {
		StringBuffer result = new StringBuffer(string);
    result.append("[]"); 
    return result.toString();
	}

	/**
	 * 
	 * @uml.property name="_type"
	 * @uml.associationEnd 
	 * @uml.property name="_type" multiplicity="(1 1)"
	 */
	private Type _type;

  
  public Type elementType() {
    return _type; 
  }
    
  public int dimension() {
    Type elementType = elementType();
		if(elementType instanceof ArrayType) {
			return 1+(((ArrayType) elementType).dimension());
		} else {
			return 1;
		}
  }
  
  @Override
  public boolean uniSameAs(Element o) throws LookupException {
    return (o instanceof ArrayType) &&
           ((ArrayType)o).elementType().sameAs(elementType());
  }
  
  @Override
  public int hashCode() {
  	return 1+elementType().hashCode();
  }
  
  public boolean assignableTo(Type other) throws LookupException {
  	View view = view();
  	ObjectOrientedLanguage language = view.language(ObjectOrientedLanguage.class);
		Type objType = language.getDefaultSuperClass(view.namespace());
    return super.assignableTo(other) ||
           ( // Reference type
             elementType().subTypeOf(objType) &&
             (other instanceof ArrayType) &&
             elementType().assignableTo(((ArrayType)other).elementType())
           ) ||           
           ( // Primitive type
             (! elementType().subTypeOf(objType)) &&
             (other instanceof ArrayType) &&
             elementType().equals(((ArrayType)other).elementType())
           );
  }

  protected ArrayType cloneThis() {
    return new ArrayType(elementType());
  }

  @Override
  	public boolean auxSubTypeOf(Type second) throws LookupException {
  	 boolean result = false;
  	 if (second instanceof ArrayType && is(language(Java.class).REFERENCE_TYPE) == Ternary.TRUE) {
  		 ArrayType second2 = (ArrayType)second;
  		 result = elementType().subTypeOf(second2.elementType());
  	 }
  	 if(! result) {
  		 result = super.auxSubTypeOf(second);
  	 }
  	 return result;
  	}
  
}

@@ -332,16 +332,98 @@
 espace;%0A
+import be.kuleuven.cs.distrinet.chameleon.exception.ChameleonProgrammerException;%0A
 import b
@@ -1347,16 +1347,39 @@
 pe type)
+ throws LookupException
  %7B%0A    s
@@ -1947,110 +1947,161 @@
 e re
-turnType = new ArrayTypeReference((JavaTypeReference) language.createTypeReference(fullyQualifiedName)
+ference = (JavaTypeReference) language.reference(type);%0A%09%09reference.setUniParent(null);%0A%09%09JavaTypeReference returnType = new ArrayTypeReference(reference
 );%0A 
@@ -2749,24 +2749,47 @@
 t dimension)
+ throws LookupException
  %7B%0A  %09this(c
@@ -2895,16 +2895,39 @@
 mension)
+ throws LookupException
  %7B%0A  %09if
@@ -4754,24 +4754,33 @@
 his() %7B%0A    
+try %7B%0A%09%09%09
 return new A
@@ -4800,24 +4800,106 @@
 entType());%0A
+%09%09%7D catch (LookupException e) %7B%0A%09%09%09throw new ChameleonProgrammerException(e);%0A%09%09%7D%0A
   %7D%0A%0A  @Over

fix return type of clone for multi-dimensional array types