using System;
using System.Configuration;
using PostSharp.Aspects;
using PostSharp.Extensibility;
using PostSharp.Reflection;
using PostSharp.Serialization;
 
namespace PostSharp.Samples.Persistence
{
  [PSerializable]
  [LinesOfCodeAvoided(2)]
  [MulticastAttributeUsage(TargetMemberAttributes = MulticastAttributes.Static)]
  public sealed class AppSettingsValueAttribute : LocationInterceptionAspect
  {
    private bool isFetched;
    private string settingName;
 
    public AppSettingsValueAttribute()
    {
    }
 
    public AppSettingsValueAttribute(string settingName)
    {
      this.settingName = settingName;
    }
 
    public override void CompileTimeInitialize(LocationInfo targetLocation, AspectInfo aspectInfo)
    {
      if (settingName == null)
        settingName = targetLocation.Name;
    }
 
    public override bool CompileTimeValidate(LocationInfo locationInfo)
    {
      bool isReadOnly;
      switch (locationInfo.LocationKind)
      {
        case LocationKind.Field:
          isReadOnly = locationInfo.FieldInfo.IsInitOnly;
          break;
 
        case LocationKind.Property:
          isReadOnly = locationInfo.PropertyInfo.SetMethod == null;
          break;
 
        default:
          throw new Exception();
      }
 
      if (isReadOnly)
      {
        Message.Write(locationInfo, SeverityType.Error, "MY001",
          "Cannot apply AppSettingsValueAttribute to a read-only field or property {0}.", locationInfo);
        return false;
      }
 
      return true;
    }
 
    public override void OnGetValue(LocationInterceptionArgs args)
    {
      if (isFetched)
      {
        args.ProceedGetValue();
      }
      else
      {
        isFetched = true;
 
        var stringValue = ConfigurationManager.AppSettings[settingName];
 
        if (!string.IsNullOrEmpty(stringValue))
        {
          var value = Convert.ChangeType(stringValue, args.Location.LocationType);
          args.SetNewValue(value);
          args.Value = value;
        }
        else
        {
          args.ProceedGetValue();
        }
      }
    }
 
    public override void OnSetValue(LocationInterceptionArgs args)
    {
      if (isFetched)
        throw new InvalidOperationException(
          "The value of this field or property should not be changed after it has been read for the first time.");
 
      base.OnSetValue(args);
    }
  }
}