<#@ assembly name="System.Core" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#+
// Copyright (C) 2003-2010 Xtensive LLC.
// All rights reserved.
// For conditions of distribution and use, see license.
// Created by: Denis Krjuchkov
// Created:    2010.03.10

enum DelegateType {
  Func,
  Action,
}

class DelegateGenerator
{
  public const int StandardMaxNumberOfArguments = 4;
  public const int MaxNumberOfArguments = 16;
  
  public static IEnumerable<DelegateType> GetDelegateTypes()
  {
    yield return DelegateType.Func;
    yield return DelegateType.Action;
  }
  
  public static string GenerateOneArgumentFunc(string prefixArgument, string suffixArgument, string argumentName, int numberOfArguments)
  {
    return "Func" + PackGenericArgumentSequence(GenerateSequence(
      prefixArgument, suffixArgument, 1, numberOfArguments, delegate (int i) { return argumentName; } ));
  }
  
  public static string GenerateArrayArgumentUsage(string prefixArgument, string arrayName, int numberOfArguments)
  {
    return PackArgumentSequence(GenerateSequence(
      prefixArgument, null, 0, numberOfArguments, delegate (int i) { return string.Format("{0}[{1}]", arrayName, i); }));
  }
  
  public static string GenerateDelegate(DelegateType type, string prefixArgument, int offset, int numberOfArguments)
  {
    switch (type) {
    case DelegateType.Func:
      return GenerateFunc(prefixArgument, offset, numberOfArguments);
    case DelegateType.Action:
      return GenerateAction(prefixArgument, offset, numberOfArguments);
    default:
      throw new InvalidOperationException();
    }
  }

  public static string GenerateFunc(string prefixArgument, int offset, int numberOfArguments)
  {
    return "Func" + GenerateFuncArguments(prefixArgument, offset, numberOfArguments);
  }

  public static string GenerateAction(string prefixArgument, int offset, int numberOfArguments)
  {
    return "Action" + GenerateActionArguments(prefixArgument, offset, numberOfArguments);
  }

  public static string GenerateDelegateArguments(DelegateType type, string prefixArgument, int offset, int numberOfArguments)
  {
    switch (type) {
    case DelegateType.Func:
      return GenerateFuncArguments(prefixArgument, offset, numberOfArguments);
    case DelegateType.Action:
      return GenerateActionArguments(prefixArgument, offset, numberOfArguments);
    default:
      throw new InvalidOperationException();
    }
  }
  
  public static string GenerateFuncArguments(string prefixArgument, int offset, int numberOfArguments)
  {
    return GenerateGenericArguments(prefixArgument, "TResult", offset, numberOfArguments);
  }

  public static string GenerateActionArguments(string prefixArgument, int offset, int numberOfArguments)
  {
    if (numberOfArguments==0 && string.IsNullOrEmpty(prefixArgument))
      return string.Empty;
    return GenerateGenericArguments(prefixArgument, null, offset, numberOfArguments);
  }

  public static string GenerateArgumentDeclaration(string prefixArg, int offset, int numberOfArguments)
  {
    return PackArgumentSequence(GenerateSequence(
      prefixArg, null, offset, numberOfArguments, GetArgumentDeclaration));
  }

  public static string GenerateArgumentUsage(string prefixArg, int offset, int numberOfArguments)
  {
    return PackArgumentSequence(GenerateSequence(
      prefixArg, null, offset, numberOfArguments, GetArgumentUsage));
  }

  private static string GenerateGenericArguments(string prefixArgument, string suffixArgument, int offset, int numberOfArguments)
  {
    return PackGenericArgumentSequence(GenerateSequence(
      prefixArgument, suffixArgument, offset, numberOfArguments, GetGenericArgumentName));
  }

  private static string GetArgumentUsage(int number)
  {
    return string.Format("arg{0}", number);
  }

  private static string GetArgumentDeclaration(int number)
  {
    return string.Format("T{0} arg{0}", number);
  }

  private static string GetGenericArgumentName(int number)
  {
    return string.Format("T{0}", number);
  }
  
  private static string PackArgumentSequence(IEnumerable<string> arguments)
  {
    return PackSequence("(", ")", ", ", arguments);
  }
  
  private static string PackGenericArgumentSequence(IEnumerable<string> arguments)
  {
    return PackSequence("<", ">", ", ", arguments);
  }

  private static IEnumerable<string> GenerateSequence(
    string prefix, string suffix,
    int offset, int numberOfItems, Func<int, string> projector)
  {
    if (!string.IsNullOrEmpty(prefix))
      yield return prefix;
    if (numberOfItems > 0)
      foreach (var i in Enumerable.Range(offset, numberOfItems))
        yield return projector.Invoke(i);
    if (!string.IsNullOrEmpty(suffix))
      yield return suffix;
  }

  public static string PackSequence(string prologue, string epilogue,
    string delimiter, IEnumerable<string> items)
  {
    var builder = new StringBuilder();
    builder.Append(prologue);
    foreach (var item in items) {
      if (builder.Length > prologue.Length)
        builder.Append(delimiter);
      builder.Append(item);
    }
    builder.Append(epilogue);
    return builder.ToString();
  }
}
#>