package scigol;

import java.util.*;
import java.lang.reflect.*;


//import antlr.collections.AST;

  
/// Hold information about a function type
///  (arg names & types, return type)
public class FuncInfo
{
  public FuncInfo()
  {
    // default to noarg, any return
    _paramNames = new String[0];
    _paramTypes = new TypeSpec[0];
    _paramDefaults = new Object[0];
    _paramHasDefault = new boolean[0];
    _returnType = TypeSpec.typeOf("any");
    _isCallSig = false;
  }
  
  
  public FuncInfo(FuncInfo fi)
  {
    _paramNames = fi._paramNames;
    _paramTypes = fi._paramTypes;
    _paramDefaults = fi._paramDefaults;
    _paramHasDefault = fi._paramHasDefault;
    _returnType = fi._returnType;
    _location = fi._location;
    _isCallSig = fi._isCallSig;
  }
  
  
  public FuncInfo(String[] paramNames, TypeSpec[] paramTypes, Object[] paramDefaults, boolean[] paramHasDefault, TypeSpec returnType)
  {
    Debug.Assert(paramNames.length == paramTypes.length);
    Debug.Assert(paramNames.length == paramDefaults.length);
    Debug.Assert(paramNames.length == paramHasDefault.length);
    _paramNames = paramNames;
    _paramTypes = paramTypes;
    _paramDefaults = paramDefaults;
    _paramHasDefault = paramHasDefault;
    _returnType = returnType;
    _isCallSig = false;
  }

  public FuncInfo(TypeSpec[] paramTypes, TypeSpec returnType)
  {
    int numArgs = paramTypes.length;
    
    _paramNames = new String[numArgs]; // no param names
    _paramDefaults = new Object[numArgs];
    _paramHasDefault = new boolean[numArgs];
    for(int a=0; a<numArgs;a++) {
      _paramNames[a] = null;
      _paramDefaults[a] = null;
      _paramHasDefault[a] = false;
    }
    
    _paramTypes = paramTypes;
    _returnType = returnType;
    _isCallSig = true;
  }
  
  
  // construct from a method signature
  public FuncInfo(Member member)
  {
    Debug.Assert(member != null, "no Member!");
    
    java.lang.Class[] parameters = null;
    
    if (member instanceof Method) 
      parameters = ((Method)member).getParameterTypes();
    else
      parameters = ((Constructor)member).getParameterTypes();
    
    _paramNames = new String[parameters.length];
    _paramTypes = new TypeSpec[parameters.length];
    _paramDefaults = new Object[parameters.length];
    _paramHasDefault = new boolean[parameters.length];
    
    for (int p = 0; p < parameters.length; p++) {
      _paramTypes[p] = new TypeSpec(parameters[p]);
      _paramHasDefault[p] = false; // java doesn't support default parameters
      _paramDefaults[p] = null;
      _paramNames[p] = "arg" + p; // !!! try to get the name from debug info
      // or annotations
    }

    // get return type
    if (member instanceof Method) {
      Type returnType = ((Method)member).getReturnType();
      if (returnType.equals(Void.TYPE)) // no return
        _returnType = TypeSpec.anyTypeSpec;
      else {
        /*
         * // if method has a CovariantReturn attribute, use its returnType as
         * the func return type // (as covariant returns aren't currently
         * supported in the CLI) TypeSpec covariantReturn =
         * TypeSpec.covariantReturn(mb); if (covariantReturn != null)
         * _returnType = covariantReturn; else
         */
        _returnType = new TypeSpec(returnType);
      }
    }
    else if (member instanceof Constructor) {
      _returnType = new TypeSpec(((Constructor)member).getDeclaringClass());
    }
    
    _isCallSig = false;
  }
  
  
  
  
  
  
  
  // construct from types of an argument list
  public FuncInfo(ArrayList args) 
  {
    _isCallSig = true;

    if (args==null) {
      // default to noarg, any return
      _paramNames = new String[0];
      _paramTypes = new TypeSpec[0];
      _paramDefaults = new Object[0];
      _paramHasDefault = new boolean[0];
      _returnType = TypeSpec.typeOf("any");
    }
    else {
      int n = args.size();
      _paramNames = new String[n];
      _paramTypes = new TypeSpec[n];
      _paramDefaults = new Object[n];
      _paramHasDefault = new boolean[n];
      _returnType = TypeSpec.typeOf("any");
      for(int a=0; a<n; a++) {
        _paramNames[a] = null;
        _paramTypes[a] = null;
        
        Object arg = args.get(a);
        boolean isValue = arg instanceof Value;
        
        // handle named argument
        if ((arg instanceof NamedArgument) || (isValue && (((Value)arg).getValue() instanceof NamedArgument))) {
          NamedArgument na = isValue?(NamedArgument)((Value)arg).getValue():(NamedArgument)arg;
          arg = na.value;
          isValue=true;
          _paramNames[a] = na.paramName;
        }
        
        if (isValue) {
          // if an arg is null and is an LValue, extract it's declared type
          Value v = (Value)arg;
          boolean isNull = (v.getValue() == null) || ((v.getValue() instanceof Any) && (((Any)v.getValue()).value == null));
          if (isNull && v.isLValue()) {
            _paramTypes[a] = v.getLValue().getSymbol().getType();
          }
          // if we have an Any value, try to deduce the type from the actual value within
          else if (v.getValue() instanceof Any) { // if we have an Any
            if (v.getValue() != null)
              _paramTypes[a] = TypeSpec.typeOf( ((Any)v.getValue()).value );
          }
        }
        if (_paramTypes[a] == null)  // still don't know type
          _paramTypes[a] = TypeSpec.typeOf(arg);
          
        _paramDefaults[a] = null;
        _paramHasDefault[a] = false;
      }
    }
    
  }
  
  
  
  

  /// is this a formal func type (i.e. including param names, etc.) or just a signature type spec?
  public boolean isFormal()
  {
    if (_paramNames.length == 0) return true; // equivelent for zero-param func
    return (_paramNames[0] != null);
  }
  
  
  public String[] getParamNames()
  {
    return _paramNames; 
  }
  
  
  public TypeSpec[] getParamTypes()
  {
    return _paramTypes; 
  }
  
  
  public java.lang.Class[] getExternParamTypes()
  {
    java.lang.Class[] eParamTypes = new java.lang.Class[_paramTypes.length];
    for(int i=0; i<_paramTypes.length;i++) {
      TypeSpec type = _paramTypes[i];
      if (type.isBuiltin() || type.isBuiltinClass())
        eParamTypes[i] = (java.lang.Class)type.getSysType();
      else {
        if (type.isClassOrInterface()) {
          ClassInfo ci = type.getClassInfo();
          if (ci.isExternal())
            eParamTypes[i] = (java.lang.Class)ci.getSysType();
          else
            eParamTypes[i] = Class.class;
        }
        else
          eParamTypes[i] = Func.class;
      }
    }
    return eParamTypes;
  }
  
  
  public Object[] getParamDefaults()
  { 
    return _paramDefaults; 
  }

  public boolean[] getParamHasDefault()
  {
    return _paramHasDefault;
  }

  
  public TypeSpec getReturnType()
  {
    return _returnType;
  }
  
  public void setReturnType(TypeSpec value)
  {
    _returnType = value;
  }

  
  public int numArgs()
  {
    return _paramTypes.length;
  }
  
  
  public int numRequiredArgs()
  {
    int c = 0;
    for(boolean paramHasDefault : _paramHasDefault)
      if (!paramHasDefault) c++;
      return c;
  }

  
  // the singelton object of this class is used to indicate that an argument to a func
  //  was omitted and should take its default value
  public static class DefaultArgument {
    protected DefaultArgument() 
    {}
    
    public static DefaultArgument getInstance()
    {
      if (instance == null) instance = new DefaultArgument();
      return instance;
    }
    
    public String toString() { return "default"; }
    
    private static DefaultArgument instance = null;
  };  
  
  public static final DefaultArgument defaultArg = DefaultArgument.getInstance();

  // represents a named argment in a func call
  public static class NamedArgument {
    public NamedArgument(String paramName, Value value)
    {
      this.paramName = paramName;
      this.value = value;
    }
    public String paramName;
    public Value value;
  };
  
  

  
  public String toString()
  {
    return toStringArgs(null);
  }
  
  public String toStringWithLocation()
  {
    String s = toStringArgs(null);
    if (_location.isKnown()) s += "["+_location+"]";
    return s;
  }
  
  
  public String toStringArgs(Object[] args)
  {
    final TypeSpec defaultArgType = new TypeSpec(DefaultArgument.class);
    boolean haveArgs = (args!=null);
    if (haveArgs)
      if (args.length < _paramNames.length) haveArgs=false;
    String s = "func(";
    for(int i=0; i<_paramNames.length; i++) {
      if (_paramNames[i] != null) {
        s += _paramNames[i];
        if (!_isCallSig)
          s += ":";
        else
          s += "=";
      }
      if (!_paramTypes[i].equals(defaultArgType))
        s +=_paramTypes[i].typeName();
      if (!haveArgs) {
        if (_paramHasDefault[i]) {
          if (_paramDefaults[i] != null) {
            String str = null;
            try { str = _paramDefaults[i].toString(); } catch (java.lang.Exception e) {}
            if (str != null) {
              if (!_paramTypes[i].isString())
                s += " ="+str;
              else
                s += " =\""+str+"\"";
            }
            else
              s += " =?";
          }
          else
            s += " =null";
        }
      }
      else {
        Object a = args[i];
        if (a != null) {
          String str = null;
          try { str = a.toString(); } catch(java.lang.Exception e) {}
          if (str != null)
            s += " =<"+str+">";
          else
            s += " =<?>";
        }
        else
          s += " =<null>";
      }
      if (i != _paramNames.length-1)
        s += ", ";
    }
    if ((_returnType != null) && (!_isCallSig))
      s += " -> "+_returnType.typeName();
    s += ")";

    return s;
  }
  
  
  
  
  
  
  // are func types equivelent? (same arg types & return type?)
  //  NB: doens't consider param names or default values
  public boolean equals(Object o)
  {
    if (o == null) return false;
    if (!(o instanceof FuncInfo)) return false;
    
    FuncInfo f = (FuncInfo)o;
    
    if (!equalsParams(f)) return false;
    
    if ((_returnType == null) && (f._returnType != null)) return false;
    if (_returnType != null)
      if (!_returnType.equals(f._returnType)) return false;
    
    return true; 
  }
  
  
  // compare for equality, ignoring return types
  //  NB: doens't consider param names or default values
  public boolean equalsParams(FuncInfo f)
  {
    if (f==null) return false;
    if (f.numArgs() != numArgs()) return false;
    
    if (f.numRequiredArgs() != numRequiredArgs()) return false;
    
    for(int a=0; a<numArgs();a++) {
      if (!_paramTypes[a].equals(f._paramTypes[a]))
        return false;
    }
    return true;
  }
  
  
  
  
  // can this func be called using a call with signature callSig?
  //  if callSig.returnType is any, then ignore return type compatibility
  // (NB: this doesn't consider implicit argument conversions, use TypeManager.isMatchingFunc() for that)
  public boolean callCompatible(FuncInfo callSig)
  {
    Debug.Assert(callSig != null);
//!!! FIXME to consider NamedArgs
    if (callSig.numArgs() < numRequiredArgs()) return false; // not enough args
    if (callSig.numArgs() > numArgs()) return false; // too many args

    
    // check types
    for(int a=0; a<callSig.numArgs();a++) {
      if (!_paramTypes[a].equals(callSig._paramTypes[a])) // not equals?
            return false;
    }

    if (callSig.getReturnType() != null)
      if (!callSig.getReturnType().equals(TypeSpec.typeOf("any")))
        if (!_returnType.equals(callSig.getReturnType())) // has required return type?
          return false;

    return true;
  }
  
  
  
  
  // convert the args for a call with signature callSig that is compatible with this
  //  to exactly the types this func expects (including filling in defaults if possible)
  // throws on arg type or number mismatch
  public Object[] convertParameters(FuncInfo callSig, Object[] args, boolean externCall) 
  {
    if (args==null) args=new Object[0];
    
    Debug.Assert(callSig != null);
    Debug.Assert(callSig.numArgs() == args.length, "supplied arg count must match callSig");
    
    
    // if last param is Object[], could indicate a vararg func 
    boolean lastParamObjArray = false;
    TypeSpec objArrayType = new TypeSpec((new Object[0]).getClass());
    if (numArgs()>0)
      if (_paramTypes[numArgs()-1].equals(objArrayType))
        lastParamObjArray = true;

    
    boolean[] haveValue = new boolean[numArgs()]; // got a value for formal param?
    Object[] convertedArgs = new Object[numArgs()];
    for(int pi=0; pi<numArgs();pi++) haveValue[pi] = false;
    boolean sawNamedArg = false;
    int p=0; // param index
    
    // for each argument
    for(int a=0; a<args.length; a++) {
      
      Object arg = args[a];
      if (arg instanceof Value) arg = ((Value)arg).getValue();
      
      if (arg instanceof NamedArgument) {
        sawNamedArg = true;
        NamedArgument na = (NamedArgument)arg;
        
        String paramName = na.paramName;
        int paramIndex = getParamIndex(paramName);
        if (paramIndex == -1)
          ScigolTreeParser.semanticError(callSig.getDefinitionLocation(),"function "+this+" doesn't have a formal parameter named '"+paramName+"' in call with signature "+callSig);

        arg = na.value.getValue();
        p = paramIndex;
      }
      else { // positional argument
        
        if(sawNamedArg) 
          ScigolTreeParser.semanticError(callSig.getDefinitionLocation(),"cannot use a positional argument after named arguments in call with signature "+callSig);

        if (p >= numArgs()) 
          ScigolTreeParser.semanticError(callSig.getDefinitionLocation(),"too many arguments for function "+this+" in call with signature "+callSig);

      }
      
      // check for special defaultArg value of omitted arguments
      if (arg == defaultArg) {
        Debug.Assert(!sawNamedArg,"a named argument with a defaulted value is non-sensical");
        
        if (!_paramHasDefault[p])
          ScigolTreeParser.semanticError(callSig.getDefinitionLocation(),"argument "+p+" ("+_paramNames[p]+") to func with signature "+this+" was omitted, but has no default value");
        
        arg = _paramDefaults[p];
      }
        
      haveValue[p] = true;
      
      
      boolean needImplicitConversion = true;
      
      TypeSpec argType = TypeSpec.typeOf(arg);
      if (argType.isAny() || argType.isNum()) {
        arg = TypeSpec.unwrapAnyOrNum(arg);
        argType = TypeSpec.typeOf(arg);
        needImplicitConversion = false;  // don't convert again after unwrapping
      }
      
      
      // determine if we need to convert a vararg parameter
      //  (i.e. if we're looking at the last parameter, it is an Object[]
      //         and we have more arguments remaining.  The exception
      //         is if there is exactly 1 argument remaining and it is an Object[])
      boolean argIsObjArray = argType.equals(objArrayType);
      int numRemainingArgs = args.length - (a+1) +1;
      boolean vararg = lastParamObjArray && (!sawNamedArg) && (p==numArgs()-1)
                        && !(argIsObjArray && (numRemainingArgs==1));
      
      if (!vararg) {
      
        // convert
        if (needImplicitConversion) {  // implicit conversion from any always exists
          // does an implicit conversion from the argType to the parameter type exist?
          if (!TypeManager.existsImplicitConversion(argType, _paramTypes[p], new Value(arg)))
            ScigolTreeParser.semanticError(callSig.getDefinitionLocation(),"argument "+p+" ("+_paramNames[p]+") to func with signature "+this+" is of type '"+argType+"' which is incompatible with parameter type '"+_paramTypes[p]+"'");
        }

      
        // conversion
        Value convertedArg = TypeManager.performImplicitConversion(argType, _paramTypes[p], new Value(arg));

        if (convertedArg == null) // conversion failed
          ScigolTreeParser.semanticError(callSig.getDefinitionLocation(),"argument "+p+" ("+_paramNames[p]+") to func with signature "+this+" is of type '"+argType+"' which cannot be converted into the required parameter type '"+_paramTypes[p]+"'");
        
        convertedArgs[p] = convertedArg.getValue();
        
      }
      else { // vararg
        
        // put remaining args into an Object[]
        Object[] remainingArgs = new Object[numRemainingArgs];
        for(int ra=a; ra<args.length; ra++) {
          
          arg = args[ra];
          if (arg instanceof Value) arg = ((Value)arg).getValue();
          arg = TypeSpec.unwrapAnyOrNum(arg);
          if (arg == defaultArg) // an omitted vararg argument is passed as a null
            arg = null;
          
          remainingArgs[ra-a] = arg;
        } // for remaining args

        convertedArgs[p] = remainingArgs;
        a = args.length; // assign arg loop var to terminate
      }

      
      if (!sawNamedArg) p++; // move to next param position
      
    } // for each arg
    
    
    // Now we've converted the args we have, check we have a value
    //  for each parameter (and use/convert the default if we don't and it
    //  exists)
    // (one exception, if we have all parameter values except the last, whos
    //  type of Object[] (vararg) then we can satisfy that with Object[0])
    
    int numMissingParams = 0;
    for(p=0; p<numArgs(); p++) {
    
      if (!haveValue[p]) {
        if (_paramHasDefault[p]) {
          Object arg = _paramDefaults[p];
          
          boolean needImplicitConversion = true;
          
          TypeSpec argType = TypeSpec.typeOf(arg);
          if (argType.isAny() || argType.isNum()) {
            arg = TypeSpec.unwrapAnyOrNum(arg);
            argType = TypeSpec.typeOf(arg);
            needImplicitConversion = false;  // don't convert again after unwrapping
          }

          // convert
          if (needImplicitConversion) {  // implicit conversion from any always exists
            // does an implicit conversion from the argType to the parameter type exist?
            if (!TypeManager.existsImplicitConversion(argType, _paramTypes[p], new Value(arg)))
              ScigolTreeParser.semanticError(callSig.getDefinitionLocation(),"default value of argument "+p+" ("+_paramNames[p]+") of func with signature "+this+" is of type '"+argType+"' which is incompatible with parameter type '"+_paramTypes[p]+"'");
          }

        
          // conversion
          Value convertedArg = TypeManager.performImplicitConversion(argType, _paramTypes[p], new Value(arg));

          if (convertedArg == null) // conversion failed
            ScigolTreeParser.semanticError(callSig.getDefinitionLocation(),"default value of argument "+p+" ("+_paramNames[p]+") of func with signature "+this+" is of type '"+argType+"' which cannot be converted into the required parameter type '"+_paramTypes[p]+"'");
          
          convertedArgs[p] = convertedArg.getValue();
          haveValue[p] = true;
        }
        else
          numMissingParams++;
      }
      
    }
    
    
    // exception for 0 arguments for a trailing varargs param
    if ((numMissingParams==1) && !haveValue[numArgs()-1] && lastParamObjArray) {
      p = numArgs()-1;
      convertedArgs[p] = new Object[0];
      haveValue[p] = true;
      numMissingParams--;
    }
    
    
    // So, do we have all the required args?
    if (numMissingParams > 0) 
      ScigolTreeParser.semanticError(callSig.getDefinitionLocation(),"too few arguments for function "+this+" in call with signature "+callSig);
    
    
    
    
    
    
    
    
    
    /* old impl before named args
    
//    if (externCall) //!!!
//      Debug.WriteLine("Warning: FuncInfo.convertParameters() - externCall being ignored (unimplemented)");
    
    // if last param is Object[], could indicate a vararg func 
    boolean lastParamObjArray = false;
    if (numArgs()>0)
      if (_paramTypes[numArgs()-1].equals(new TypeSpec((new Object[0]).getClass())))
        lastParamObjArray = true;
/*
    if (numRequiredArgs() > callSig.numArgs()) {
      // a call to a vararg func is allowed to supply zero args for the vararg param (in which case
      //  a Object[0] is passed)
      if (!(lastParamObjArray && (callSig.numArgs()==numRequiredArgs()-1)))
        ScigolTreeParser.semanticError(callSig.getDefinitionLocation(),"not enough arguments for function "+this+" in call with signature "+callSig);
    }
* /    
    
    if (callSig.numArgs() > numArgs()) {
      // there may be too many arguments, or this func has a vararg parameter as last
      if (!lastParamObjArray) // last param isn't Object[] - not vararg
        ScigolTreeParser.semanticError(callSig.getDefinitionLocation(),"too many arguments for function "+this+" in call with signature "+callSig);
    }

    boolean[] argHasValue = new boolean[numArgs()];
    for(int a=0; a<numArgs(); a++) argHasValue[a]=false;
    
    Object[] convertedArgs = new Object[numArgs()];  
    
    // for each formal param
    for(int a=0; a<numArgs(); a++) {
      
      // special case for last param being Object[], indicating a vararg param
      //  (in which case the remaining args, if any, are put into an Object[] and passed as the last param)
      if (lastParamObjArray && (a==numArgs()-1) && (callSig.numArgs() >= numArgs()-1)) {
        // handle varargs
        
        // if there are no more, use Object[0] as last arg
        if (a > callSig.numArgs()-1) {
          convertedArgs[a] = new Object[0];
          argHasValue[a] = true;
        }
        else {
          // convert remaining args to array
          Object[] varargs = new Object[callSig.numArgs()-(numArgs()-1)];
          for(int va=a; va<callSig.numArgs(); va++) {
            Object arg = ((args[va] instanceof Value)?(((Value)args[va]).getValue()):args[va]); // convert from Value if necessary
            arg = TypeSpec.unwrapAnyOrNum(arg);
            if (arg == FuncInfo.defaultArg) 
              Debug.Assert(false,"extra args to varargs func can't have special defaultArg value");
            if (arg instanceof NamedArgument) {
              NamedArgument na = (NamedArgument)arg;
              ScigolTreeParser.semanticError(callSig.getDefinitionLocation(),"named argument "+na.paramName+") to func with signature "+this+" is invalid for trailing variable argument list");
            }
            varargs[va-a] = arg;
          }
          convertedArgs[a] = varargs;
        }
        
        
      }
      else { // normal arguments (non-varargs)

        if (a < callSig.numArgs()) { // if param supplied
        
          Object arg = ((args[a] instanceof Value)?(((Value)args[a]).getValue()):args[a]); // convert from Value if necessary
  
          // first, see if there is a named arg for this param
          NamedArgument na = findNamedArg(_paramNames[a],args);
          if (na != null) {
            //...
          }
          //...
          
          // if omitted, it will have special defaultValue, so subst actual default value
          if (arg == defaultArg) {
            if (!_paramHasDefault[a])
              ScigolTreeParser.semanticError(callSig.getDefinitionLocation(),"argument "+a+" ("+_paramNames[a]+") to func with signature "+this+" was omitted, but has no default value");
            arg = _paramDefaults[a];
          }
          
          if (arg instanceof NamedArgument) {
            NamedArgument na = (NamedArgument)arg;
            
            int paramIndex = getParamIndex(na.paramName);
            if (paramIndex==-1)
              ScigolTreeParser.semanticError(callSig.getDefinitionLocation(),"func with signature "+this+" has no formal parameter named '"+na.paramName+"'");
            
          }
          
          TypeSpec argType = TypeSpec.typeOf(arg);
          if (!argType.isAny()) {  // implicit conversion from any always exists
            // does an implicit conversion from the argType to the parameter type exist?
            if (!TypeManager.existsImplicitConversion(argType, _paramTypes[a], new Value(arg)))
              ScigolTreeParser.semanticError(callSig.getDefinitionLocation(),"argument "+a+" ("+_paramNames[a]+") to func with signature "+this+" is of type '"+argType+"' which is incompatible with parameter type '"+_paramTypes[a]+"'");
          }
          else {
            arg = TypeSpec.unwrapAny(arg);
            argType = TypeSpec.typeOf(arg);
          }
  
          // conversion
          Value convertedArg = TypeManager.performImplicitConversion(argType, _paramTypes[a], new Value(arg));
  
          if (convertedArg == null) // conversion failed
            ScigolTreeParser.semanticError(callSig.getDefinitionLocation(),"argument "+a+" ("+_paramNames[a]+") to func with signature "+this+" is of type '"+argType+"' which cannot be converted into the required parameter type '"+_paramTypes[a]+"'");
          
          convertedArgs[a] = convertedArg.getValue();
        }
        else { // not supplied
          // do we have a default value?
          if (!_paramHasDefault[a])
            ScigolTreeParser.semanticError(callSig.getDefinitionLocation(),"in call to function "+this+", no argument was supplied for parameter "+_paramNames[a]+" (which has no default)");
          
          // use default
          convertedArgs[a] = _paramDefaults[a];
        }
      }
      
    } // for each param
    */
    return convertedArgs;
  }
  
  
  private int getParamIndex(String paramName)
  {
    for(int p=0; p<_paramNames.length;p++)
      if (_paramNames[p].equals(paramName))
        return p;
    return -1;
  } 
  
  /*
  private NamedArgument findNamedArg(String paramName, Object[] args)
  {
    for(int a=0; a<args.length;a++) {
      Object arg = ((args[a] instanceof Value)?(((Value)args[a]).getValue()):args[a]); // convert from Value if necessary

      if (arg instanceof NamedArgument) {
        NamedArgument na = (NamedArgument)arg;
        if (na.paramName.equals(paramName))
          return na;
      }
    }
    return null;
  }
  */
  
  // convenience version of above
  public Object[] convertParameters(FuncInfo callSig, ArrayList args, boolean externCall) 
  {
    Object[] aargs = new Object[args.size()];
    for(int a=0; a<aargs.length;a++) aargs[a] = args.get(a);
    return convertParameters(callSig, aargs, externCall);
  }
  
  
  
  // make a property accessor signature from this property args and the property type
  public FuncInfo accessorSig(String accessorName, TypeSpec propertyType)
  {
    FuncInfo fi = null; 
    if (accessorName.equals("set")) { // add the propertyType as an extra parameter named 'value'
      fi = new FuncInfo();
      int numPropArgs = _paramTypes.length;
      
      TypeSpec[] pTypes = new TypeSpec[numPropArgs+1];
      for(int i=0; i<numPropArgs;i++) {
        pTypes[i] = _paramTypes[i];
      }
      pTypes[numPropArgs] = propertyType;
      
      String[] pNames = new String[numPropArgs+1];
      for(int i=0; i< numPropArgs; i++)
        pNames[i] = _paramNames[i];
      pNames[numPropArgs] = "value";
      
      Object[] pDefaults = new Object[numPropArgs+1];
      for(int i=0; i<numPropArgs;i++) 
        pDefaults[i] = _paramDefaults[i];
      pDefaults[numPropArgs] = null;
      
      boolean[] pHasDefault = new boolean[numPropArgs+1];
      for(int i=0; i<numPropArgs;i++)
        pHasDefault[i] = _paramHasDefault[i];
      pHasDefault[numPropArgs] = false;
      
      fi._paramNames = pNames;
      fi._paramTypes = pTypes;
      fi._paramDefaults = pDefaults;
      fi._paramHasDefault = pHasDefault;
    }
    else if (accessorName.equals("get")) { // set the returnType to the propertyType
      fi = new FuncInfo(this);
      fi._returnType = propertyType;
    }
    else
      Debug.Assert(false, "invalid accessor kind name (should be 'get' or 'set'");
    return fi;
  }
  
  
  // make a property args from the this accessor sig and kind
  public FuncInfo propertySig(String accessorName)
  {
    FuncInfo fi = null; 
    if (accessorName.equals("set")) {
      
      // remove the last argument from the set accessor
      
      Debug.Assert(+_paramTypes.length>0, "a set accessor must have at least one argument (the value arg)");
      
      fi = new FuncInfo(this); // copy
      int numPropArgs = _paramTypes.length-1;
      
      
      TypeSpec[] pTypes = new TypeSpec[numPropArgs];
      for(int i=0; i<numPropArgs;i++) {
        pTypes[i] = _paramTypes[i];
      }
      
      String[] pNames = new String[numPropArgs];
      for(int i=0; i< numPropArgs; i++)
        pNames[i] = _paramNames[i];
      
      Object[] pDefaults = new Object[numPropArgs];
      for(int i=0; i<numPropArgs;i++) 
        pDefaults[i] = _paramDefaults[i];
      
      boolean[] pHasDefault = new boolean[numPropArgs];
      for(int i=0; i<numPropArgs;i++)
        pHasDefault[i] = _paramHasDefault[i];
      
      fi._paramNames = pNames;
      fi._paramTypes = pTypes;
      fi._paramDefaults = pDefaults;
      fi._paramHasDefault = pHasDefault;
    }
    else if (accessorName.equals("get")) {
      // property args are the same as the get accessor args, no change
      fi = new FuncInfo(this); // copy
    }
    else
      Debug.Assert(false, "invalid accessor kind name (should be 'get' or 'set'");
    return fi;
  }
  
  
  public static TypeSpec propertyTypeFromAccessor(String accessorName, FuncInfo accessorSig)
  {
    if (accessorName.equals("get")) { 
      return accessorSig._returnType;
    }
    else if (accessorName.equals("set")) {
      return accessorSig._paramTypes[accessorSig.numArgs()-1];
    }
    else
      Debug.Assert(false, "invalid accessor name");
    return null;
  }
  
  
  public static String accessorName(String propertyName, boolean get)
  {
    String suffix = propertyName;
    if (propertyName.equals("operator()")) suffix = "Item";
    return get?"get_"+suffix:"set_"+suffix;
  }
  
  
  public static String propertyName(String accessorName)
  {
    String propName = accessorName.substring(4);
    if (propName.equals("Item")) propName = "operator()";
    return propName;
  }
  
  
  
  public Location getDefinitionLocation()
  { 
    if (_location != null) return _location; else return new Location();  
  }
  
  public void setDefinitionLocation(Location value)
  {
    _location = value; 
  }
  
  
  protected String[] _paramNames;  
  protected TypeSpec[] _paramTypes;
  protected Object[] _paramDefaults;
  protected boolean[] _paramHasDefault;
  protected TypeSpec _returnType;
  protected boolean _isCallSig; // does this represent a call signature, or a func formal parameter spec?
  
  protected Location _location = new Location();
  
  
  // convenience for args
  public static ArrayList toArrayList(Object[] argArray)
  {
    if (argArray==null) return new ArrayList();
    ArrayList l = new ArrayList(argArray.length);
    for(int i=0; i<argArray.length;i++)
      l.add(argArray[i]);
    return l;
  }
  
  public static Object[] toArray(ArrayList argList)
  {
    if (argList == null) return new Object[0];
    Object[] a = new Object[argList.size()];
    for(int i=0; i<argList.size();i++)
      a[i] = argList.get(i);
    return a;
  }

}

