using System.Reflection;
using System.Text;
using PostSharp.Aspects;
using PostSharp.Serialization;
 
namespace PostSharp.Samples.CustomLogging.Aspects
{
  /// <summary>
  ///   Aspect that, when applied to a method, appends a record to the <see cref="Logger" /> class whenever this method is
  ///   executed.
  /// </summary>
  [PSerializable]
  [LinesOfCodeAvoided(6)]
  public sealed class LogMethodAttribute : OnMethodBoundaryAspect
  {
    /// <summary>
    ///   Method invoked before the target method is executed.
    /// </summary>
    /// <param name="args">Method execution context.</param>
    public override void OnEntry(MethodExecutionArgs args)
    {
      var stringBuilder = new StringBuilder();
 
      stringBuilder.Append("Entering ");
      AppendCallInformation(args, stringBuilder);
      Logger.WriteLine(stringBuilder.ToString());
 
      Logger.Indent();
    }
 
 
    /// <summary>
    ///   Method invoked after the target method has successfully completed.
    /// </summary>
    /// <param name="args">Method execution context.</param>
    public override void OnSuccess(MethodExecutionArgs args)
    {
      Logger.Unindent();
 
      var stringBuilder = new StringBuilder();
 
      stringBuilder.Append("Exiting ");
      AppendCallInformation(args, stringBuilder);
 
      if (!args.Method.IsConstructor && ((MethodInfo) args.Method).ReturnType != typeof(void))
      {
        stringBuilder.Append(" with return value ");
        stringBuilder.Append(args.ReturnValue);
      }
 
      Logger.WriteLine(stringBuilder.ToString());
    }
 
    /// <summary>
    ///   Method invoked when the target method has failed.
    /// </summary>
    /// <param name="args">Method execution context.</param>
    public override void OnException(MethodExecutionArgs args)
    {
      Logger.Unindent();
 
      var stringBuilder = new StringBuilder();
 
      stringBuilder.Append("Exiting ");
      AppendCallInformation(args, stringBuilder);
 
      if (!args.Method.IsConstructor && ((MethodInfo) args.Method).ReturnType != typeof(void))
      {
        stringBuilder.Append(" with exception ");
        stringBuilder.Append(args.Exception.GetType().Name);
      }
 
      Logger.WriteLine(stringBuilder.ToString());
    }
 
    private static void AppendCallInformation(MethodExecutionArgs args, StringBuilder stringBuilder)
    {
      var declaringType = args.Method.DeclaringType;
      Formatter.AppendTypeName(stringBuilder, declaringType);
      stringBuilder.Append('.');
      stringBuilder.Append(args.Method.Name);
 
      if (args.Method.IsGenericMethod)
      {
        var genericArguments = args.Method.GetGenericArguments();
        Formatter.AppendGenericArguments(stringBuilder, genericArguments);
      }
 
      var arguments = args.Arguments;
 
      Formatter.AppendArguments(stringBuilder, arguments);
    }
  }
}