package org.jetbrains.plugins.groovy.extensions;

import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.xmlb.annotations.AbstractCollection;
import com.intellij.util.xmlb.annotations.Attribute;
import com.intellij.util.xmlb.annotations.Property;
import com.intellij.util.xmlb.annotations.Tag;
import org.jetbrains.annotations.Nullable;

import java.util.*;

import static org.jetbrains.plugins.groovy.extensions.NamedArgumentDescriptor.*;

/**
 * @author Sergey Evdokimov
 */
public class GroovyMethodDescriptor {

  private static final String ATTR_NAMES_DELIMITER = " \t\n\r,;";

  @Attribute("name")
  public String methodName;

  @Attribute("checkParamsType")
  public Boolean checkParamsType;

  @Property(surroundWithTag = false)
  @AbstractCollection(surroundWithTag = false)
  public Param[] params;

  @Tag("param")
  public static class Param {

    public static final Param[] EMPTY_ARRAY = new Param[0];

    @Attribute("type")
    public String type;
  }

  @Attribute("returnType")
  public String returnType;

  @Attribute("returnTypeCalculator")
  public String returnTypeCalculator;

  @Attribute("namedArgs")
  public String namedArgs;

  @Attribute("namedArgsProvider")
  public String namedArgsProvider;

  @Attribute("namedArgsShowFirst")
  public Boolean isNamedArgsShowFirst;

  @Property(surroundWithTag = false)
  @AbstractCollection(surroundWithTag = false)
  //public Arguments[] arguments;
  public NamedArguments[] myArguments;

  @Tag("namedArguments")
  public static class NamedArguments {
    @Attribute("type")
    public String type;

    @Attribute("showFirst")
    public Boolean isFirst;

    @Attribute("names")
    public String names;

    @Attribute("referenceProvider")
    public String referenceProvider;

    protected Iterable<String> getNames() {
      assert !StringUtil.isEmptyOrSpaces(names);
      return StringUtil.tokenize(names, ATTR_NAMES_DELIMITER);
    }
  }

  @Nullable
  public List<String> getParams() {
    if (params != null) {
      assert (checkParamsType == null || checkParamsType);
      
      String[] paramsTypeNames = new String[params.length];
      for (int i = 0; i < params.length; i++) {
        String typeName = params[i].type;
        assert StringUtil.isNotEmpty(typeName); 
        paramsTypeNames[i] = typeName;
      }
      
      return Arrays.asList(paramsTypeNames);
    }

    if (checkParamsType != null && checkParamsType) {
      return Collections.emptyList();
    }
    else {
      return null;
    }
  }

  public Map<String, String> getNamedArgumentsReferenceProviders() {
    if (myArguments == null) return Collections.emptyMap();

    Map<String, String> res = new HashMap<String, String>();

    for (NamedArguments argument : myArguments) {
      if (argument.referenceProvider != null) {
        for (String name : argument.getNames()) {
          Object oldValue = res.put(name, argument.referenceProvider);
          assert oldValue == null;
        }
      }
    }

    return res;
  }

  @Nullable
  public Map<String, NamedArgumentDescriptor> getArgumentsMap() {
    if (myArguments == null && namedArgs == null) {
      assert isNamedArgsShowFirst == null;
      return null;
    }

    Map<String, NamedArgumentDescriptor> res =
      new HashMap<String, NamedArgumentDescriptor>();

    if (myArguments != null) {
      for (NamedArguments arguments : myArguments) {
        NamedArgumentDescriptor descriptor = getDescriptor(isNamedArgsShowFirst, arguments.isFirst, arguments.type);

        for (String name : arguments.getNames()) {
          Object oldValue = res.put(name, descriptor);
          assert oldValue == null;
        }
      }
    }

    if (!StringUtil.isEmptyOrSpaces(namedArgs)) {
      NamedArgumentDescriptor descriptor = getDescriptor(isNamedArgsShowFirst, null, null);

      for (StringTokenizer st = new StringTokenizer(namedArgs, ATTR_NAMES_DELIMITER); st.hasMoreTokens(); ) {
        String name = st.nextToken();

        Object oldValue = res.put(name, descriptor);
        assert oldValue == null : "Duplicated attribute name: " + name;
      }
    }

    return res;
  }

  private static NamedArgumentDescriptor getDescriptor(@Nullable Boolean methodFirstFlag,
                                                       @Nullable Boolean attrFirstFlag,
                                                       @Nullable String type) {
    Boolean objShowFirst = attrFirstFlag;
    if (objShowFirst == null) {
      objShowFirst = methodFirstFlag;
    }

    boolean showFirst = objShowFirst == null || objShowFirst;

    if (StringUtil.isEmptyOrSpaces(type)) {
      return showFirst ? SIMPLE_ON_TOP : SIMPLE_NORMAL;
    }

    NamedArgumentDescriptor descriptor = new NamedArgumentDescriptor.StringTypeCondition(type.trim());

    if (!showFirst) {
      descriptor.setPriority(Priority.NORMAL);
    }

    return descriptor;
  }
}

@@ -1454,17 +1454,16 @@
 Argument
-s
 %5B%5D myArg
@@ -1492,17 +1492,16 @@
 Argument
-s
 %22)%0A  pub
@@ -1530,17 +1530,16 @@
 Argument
-s
  %7B%0A    @
@@ -1658,17 +1658,16 @@
 te(%22name
-s
 %22)%0A    p
@@ -1683,17 +1683,16 @@
 ing name
-s
 ;%0A%0A    @
@@ -1849,17 +1849,16 @@
 ces(name
-s
 );%0A     
@@ -1889,17 +1889,16 @@
 ize(name
-s
 , ATTR_N
@@ -2715,33 +2715,32 @@
 r (NamedArgument
-s
  argument : myAr
@@ -3356,17 +3356,16 @@
 Argument
-s
  argumen

Rename namedArguments to namedArgument