package org.aikodi.chameleon.oo.method;

import java.util.List;

import org.aikodi.chameleon.core.declaration.Declaration;
import org.aikodi.chameleon.core.declaration.DeclarationContainer;
import org.aikodi.chameleon.core.declaration.Signature;
import org.aikodi.chameleon.core.element.Element;
import org.aikodi.chameleon.core.lookup.DeclarationSelector;
import org.aikodi.chameleon.core.lookup.LookupContext;
import org.aikodi.chameleon.core.lookup.LookupException;
import org.aikodi.chameleon.core.lookup.SelectionResult;
import org.aikodi.chameleon.core.lookup.Target;
import org.aikodi.chameleon.core.modifier.ElementWithModifiersImpl;
import org.aikodi.chameleon.oo.member.DeclarationWithParametersHeader;
import org.aikodi.chameleon.oo.member.Member;
import org.aikodi.chameleon.oo.member.SignatureWithParameters;
import org.aikodi.chameleon.oo.type.DeclarationWithType;
import org.aikodi.chameleon.oo.type.Type;
import org.aikodi.chameleon.oo.type.generics.TypeParameter;
import org.aikodi.chameleon.oo.variable.FormalParameter;
import org.aikodi.chameleon.util.Util;
import org.aikodi.chameleon.util.association.Single;

import com.google.common.collect.ImmutableList;

public abstract class DeclarationWithParameters extends ElementWithModifiersImpl implements Member, DeclarationContainer, Target,
    DeclarationWithType {

  /**
   * Initialize a new method with the given header.
   * 
   * @param header
   */
 /*@
   @ public behavior
   @
   @ post header() == header;
   @*/
  public DeclarationWithParameters(MethodHeader header) {
    setHeader(header);
  }

  protected DeclarationWithParameters() {

  }

  /**
   * Set the header of this method.
   * 
   * @param header
   */
  public void setHeader(MethodHeader header) {
    set(_header, header);
  }

  /**
   * Return the signature of this member.
   */
  public MethodHeader header() {
    return _header.getOtherEnd();
  }

  private Single<MethodHeader> _header = new Single<MethodHeader>(this, true);

  public SignatureWithParameters signature() {
    MethodHeader header = header();
    return header == null ? null : header.signature();
  }

  @Override
  public void setSignature(Signature signature) {
    setHeader((MethodHeader) header().createFromSignature(signature));
  }

  @Override
  public void setName(String name) {
    header().setName(name);
  }

  public List<TypeParameter> typeParameters() {
    return header().typeParameters();
  }

  public int nbTypeParameters() {
    return header().nbTypeParameters();
  }

  /**
   * Return the index-th type parameter. Indices start at 1.
   */
  public TypeParameter typeParameter(int index) {
    return header().typeParameter(index);
  }

  /**
   * Return a string representation for the name of the method. This is just a
   * convenience method. DO NOT USE IT TO IDENTIFY ELEMENTS! The signature is
   * used for identification, not just the name.
   * 
   * @return
   */
  @Override
  public String name() {
    DeclarationWithParametersHeader header = header();
    if (header != null) {
      return header.name();
    } else {
      return null;
    }
  }

  /**
	 */
  public FormalParameter formalParameter(int index) {
    return header().formalParameter(index);
  }

  public FormalParameter lastFormalParameter() {
    int nbFormalParameters = nbFormalParameters();
    if (nbFormalParameters > 0) {
      return formalParameter(nbFormalParameters - 1);
    } else {
      return null;
    }
  }

  public int nbFormalParameters() {
    return header().nbFormalParameters();
  }

  public List<FormalParameter> formalParameters() {
    DeclarationWithParametersHeader header = header();
    if (header != null) {
      return header.formalParameters();
    } else {
      return ImmutableList.<FormalParameter> of();
    }
  }

  public List<Type> formalParameterTypes() throws LookupException {
    DeclarationWithParametersHeader header = header();
    if (header != null) {
      return header.formalParameterTypes();
    } else {
      return ImmutableList.<Type> of();
    }
  }

  /*
   * @
   * @ also public behavior
   * @
   * @ // A method introduces itself as a method.
   * @ post \result.contains(this);
   * @ post \result.size() == 1;
   * @
   */
  @Override
  public List<Member> getIntroducedMembers() {
    return (List) Util.createSingletonList(this);
  }

  @Override
  public LookupContext lookupContext(Element element) throws LookupException {
    if (element == header()) {
      return parent().lookupContext(this);
    } else {
      if (_lexical == null) {
        _lexical = language().lookupFactory().createLexicalLookupStrategy(localLookupStrategy(), this);
      }
      return _lexical;
    }
  }

  public LookupContext localLookupStrategy() {
    if (_local == null) {
      _local = language().lookupFactory().createTargetLookupStrategy(this);
    }
    return _local;
  }

  @Override
  public LookupContext localContext() {
    return localLookupStrategy();
  }

  private LookupContext _local;

  private LookupContext _lexical;

  @Override
  public List<? extends Declaration> locallyDeclaredDeclarations() throws LookupException {
    return declarations();
  }

  @Override
  public List<? extends Declaration> declarations() {
    return header().declarations();
  }

  @Override
  public <D extends Declaration> List<? extends SelectionResult> declarations(DeclarationSelector<D> selector)
      throws LookupException {
    return header().declarations(selector);
  }

}

@@ -1276,30 +1276,8 @@
 ber,
- DeclarationContainer,
  Tar
@@ -1593,24 +1593,147 @@
 ers() %7B%0A%0A  %7D
+%0A  %0A  @Override%0A  public LookupContext targetContext() throws LookupException %7B%0A  %09return Member.super.targetContext();%0A  %7D
 %0A%0A  /**%0A   *

resolve conflict