<#@ template language="C#" debug="true" hostspecific="true" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="Microsoft.VisualStudio.Shell.Interop" #>
<#@ assembly name="EnvDTE" #>
<#@ assembly name="EnvDTE80" #>
<#@ assembly name="VSLangProj" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="System.Xml.Linq" #>
<#@ assembly name="C:\Windows\Microsoft.NET\Framework\v4.0.30319\CustomMarshalers.dll" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#@ import namespace="Microsoft.VisualStudio.Shell.Interop" #>
<#@ import namespace="EnvDTE" #>
<#@ import namespace="EnvDTE80" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>
<#@ import namespace="System.Runtime.InteropServices.CustomMarshalers" #>
<#@ import namespace="System.Runtime.InteropServices" #>
<#@ import namespace="System.Reflection" #>



<#
   // Get the DTE service from the host
    EnvDTE.DTE Dte = null;
	var serviceProvider = Host as IServiceProvider;
    if (serviceProvider != null)
        Dte = (EnvDTE.DTE)serviceProvider.GetService(typeof(EnvDTE.DTE));

    // Fail if we couldn't get the DTE. This can happen when trying to run in TextTransform.exe
    if (Dte == null)
        throw new Exception("T4MVC can only execute through the Visual Studio host");

	List<Permission> permissions = new List<Permission>();
	List<Permission> groups = new List<Permission>();

	var piRolePermission = Dte.Solution.FindProjectItem(Host.ResolvePath(@"Roles\RoleClaims.cs"));
	var piGroups = FindClaimGroupProjectItems(piRolePermission.ContainingProject.ProjectItems.Item("Authorization").ProjectItems.Item("Roles").ProjectItems.Item("ClaimGroups")).ToList();

	ParseProjectItem(piRolePermission, groups, permissions);
    foreach (ProjectItem piItem in piGroups)
		 ParseProjectItem(piItem, groups, permissions);

	var permissionRoot = BuildHierarchy(groups, permissions);

	

	#>
// <auto-generated />
// This file was generated by a T4 template.
// Don't change it directly as your change would get overwritten.  Instead, make changes
// to the .tt file (i.e. the T4 template) and save it to regenerate this file.
using Disco.Models.Services.Authorization;
using Disco.Models.Repository;
using Disco.Services.Authorization.Roles;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Disco.Services.Authorization
{
	public static class Claims
	{
		private static Dictionary<string, Tuple<Func<RoleClaims, bool>, Action<RoleClaims, bool>, string, string, bool>> _roleClaims;
		private static ClaimNavigatorItem _claimNavigator;

		static Claims()
        {
#region Role Claim Dictionary
            _roleClaims = new Dictionary<string, Tuple<Func<RoleClaims, bool>, Action<RoleClaims, bool>, string, string, bool>>()
			{
<#WriteAccessHashes(permissionRoot);#>
			};
#endregion

#region Role Claim Navigator
			_claimNavigator =
<#WriteNavigator(permissionRoot);#>;
#endregion
        }

		public static ClaimNavigatorItem RoleClaimNavigator
		{
			get { return _claimNavigator; }
		}

		internal static Tuple<Func<RoleClaims, bool>, Action<RoleClaims, bool>, string, string, bool> GetClaimDefinition(string ClaimKey) {
			Tuple<Func<RoleClaims, bool>, Action<RoleClaims, bool>, string, string, bool> claimDef;

            if (!_roleClaims.TryGetValue(ClaimKey, out claimDef))
                throw new ArgumentException("Unknown Claim Key", "ClaimKey");

            return new Tuple<Func<RoleClaims, bool>, Action<RoleClaims, bool>, string, string, bool>(claimDef.Item1, claimDef.Item2, claimDef.Item3, claimDef.Item4, claimDef.Item5);
		}

		public static Func<RoleClaims, bool> GetClaimAccessor(string ClaimKey) {
			Tuple<Func<RoleClaims, bool>, Action<RoleClaims, bool>, string, string, bool> claimDef;

            if (!_roleClaims.TryGetValue(ClaimKey, out claimDef))
                throw new ArgumentException("Unknown Claim Key", "ClaimKey");

            return claimDef.Item1;
		}

		public static Action<RoleClaims, bool> GetClaimSetter(string ClaimKey) {
			Tuple<Func<RoleClaims, bool>, Action<RoleClaims, bool>, string, string, bool> claimDef;

            if (!_roleClaims.TryGetValue(ClaimKey, out claimDef))
                throw new ArgumentException("Unknown Claim Key", "ClaimKey");

            return claimDef.Item2;
		}

		public static Tuple<string, string, bool> GetClaimDetails(string ClaimKey) {
			Tuple<Func<RoleClaims, bool>, Action<RoleClaims, bool>, string, string, bool> claimDef;

            if (!_roleClaims.TryGetValue(ClaimKey, out claimDef))
                throw new ArgumentException("Unknown Claim Key", "ClaimKey");

            return new Tuple<string, string, bool>(claimDef.Item3, claimDef.Item4, claimDef.Item5);
		}

		public static RoleClaims BuildClaims(IEnumerable<string> ClaimKeys){
			var c = new RoleClaims();
			foreach (var claimKey in ClaimKeys)
				c.Set(claimKey, true);

			return c;
		}

		public static List<string> GetClaimKeys(RoleClaims Claims)
        {
            var claims = Claims;
            return _roleClaims.Where(rc => rc.Value.Item1(claims)).Select(rc => rc.Key).ToList();
        }

		public static RoleClaims AdministratorClaims() {
			var c = new RoleClaims();
#region Set All Administrator Claims
<#WriteAdministratorClaims(permissionRoot);#>
#endregion
			return c;
		}

		public static RoleClaims ComputerAccountClaims() {
			return new RoleClaims() {
				ComputerAccount = true
			};
		}

#region Role Claim Constants
<#WritePermissionConsts(permissionRoot);#>
#endregion
	}
	public static class ClaimExtensions
	{
        public static bool Has(this RoleClaims c, string ClaimKey)
        {
            Func<RoleClaims, bool> claimFunc = Claims.GetClaimAccessor(ClaimKey);

            return claimFunc(c);
        }

		public static void Set(this RoleClaims c, string ClaimKey, bool Value)
        {
            var claimDefinition = Claims.GetClaimDefinition(ClaimKey);

			if (!claimDefinition.Item5)
				claimDefinition.Item2(c, Value);
        }

		public static void SetClaims(this AuthorizationRole role, RoleClaims Claims)
        {
            role.ClaimsJson = Newtonsoft.Json.JsonConvert.SerializeObject(Claims);
        }
	}
}
<#+

void DumpPermission(Permission p){
	PushIndent("				");
	WriteLine(p.Name);
		foreach (Permission pChild in p.Children)
		{
			DumpPermission(pChild);
		}
	PopIndent();
}

void WriteNavigator(Permission root){
	PushIndent("				");
	WriteLine("new ClaimNavigatorItem(\"Claims\", \"Permissions\", \"Top-level node for all permissions\", false, new List<IClaimNavigatorItem>() {");
	PushIndent("    ");	
	for (int childIndex = 0; childIndex < root.Children.Count; childIndex++)
    {
		WriteNavigator_Recurse(root.Children[childIndex], string.Empty);
		if (childIndex < root.Children.Count -1)
			WriteLine(",");
    }
	WriteLine(string.Empty);
	PopIndent();
	Write("})");
	PopIndent();
}

void WriteNavigator_Recurse(Permission p, string Prefix){
	var key = string.Concat(Prefix, p.Name);

	if (p.IsGroup){
		var groupPrefix = string.Concat(key, ".");
		WriteLine("new ClaimNavigatorItem(\"{0}{1}\", \"{2}\", \"{3}\", {4}, new List<IClaimNavigatorItem>() {{", Prefix, p.Name, p.FriendlyName, p.Description, p.Hidden ? "true" : "false");
		PushIndent("    ");
		var children = p.Children.OrderBy(c => !c.IsGroup).ThenBy(c => c.FriendlyName).ToList();
		for (int childIndex = 0; childIndex < children.Count; childIndex++)
		{
			WriteNavigator_Recurse(children[childIndex], groupPrefix);
			if (childIndex < children.Count -1)
				WriteLine(",");
			else
				WriteLine(string.Empty);
		}
		PopIndent();
		Write("})");
    }else{
		Write("new ClaimNavigatorItem(\"{0}{1}\", {2})", Prefix, p.Name, p.Hidden ? "true" : "false");
    }
}

void WriteAdministratorClaims(Permission root){
	PushIndent("			");
	Stack<string> parents = new Stack<string>();
	parents.Push("c");
	foreach (var pChild in root.Children)
			WriteAdministratorClaims_Recurse(pChild, parents);
	PopIndent();
}

void WriteAdministratorClaims_Recurse(Permission p, Stack<string> parents){
	if (p.IsGroup){
		parents.Push(string.Format("{0}.{1}", parents.Peek(), p.Name));
		foreach (var pChild in p.Children)
			WriteAdministratorClaims_Recurse(pChild, parents);
		parents.Pop();
    }else{
		if (p.Name != "ComputerAccount")
			WriteLine("{0}.{1} = true;", parents.Peek(), p.Name);
    }
}


void WriteAccessHashes(Permission root){
	StringBuilder hashes = new StringBuilder();
	Stack<string> parents = new Stack<string>();
	parents.Push("c");
	foreach (var pChild in root.Children)
			WriteAccessHashes_Recurse(pChild, string.Empty, parents, hashes);
	WriteLine(hashes.ToString().TrimEnd().TrimEnd(','));
}

void WriteAccessHashes_Recurse(Permission p, string Prefix, Stack<string> parents, StringBuilder hashes){
	if (p.IsGroup){
		parents.Push(string.Format("{0}.{1}", parents.Peek(), p.Name));
		foreach (var pChild in p.Children)
			WriteAccessHashes_Recurse(pChild, string.Concat(Prefix, p.Name, "."), parents, hashes);
		parents.Pop();
    }else{
		var fqn = string.Concat(Prefix, p.Name);
		hashes.AppendFormat("				{{ \"{0}\", new Tuple<Func<RoleClaims, bool>, Action<RoleClaims, bool>, string, string, bool>(c => {1}.{2}, (c, v) => {1}.{2} = v, \"{3}\", \"{4}\", {5}) }},", fqn, parents.Peek(), p.Name, p.FriendlyName, p.Description, p.Hidden ? "true" : "false");
		hashes.AppendLine();
    }
}

void WritePermissionConsts(Permission root){
	PushIndent("    ");
	PushIndent("    ");
	foreach (var pChild in root.Children)
			WritePermissionConsts_Recurse(pChild, string.Empty);
	PopIndent();
	PopIndent();
}
void WritePermissionConsts_Recurse(Permission p, string Prefix){
	if (p.IsGroup){
		WriteLine("");
		WriteLine("/// <summary>{0}", p.FriendlyName);
		WriteLine("/// <para>{0}</para>", p.Description);
		WriteLine("/// </summary>");
		WriteLine("public static class {0}", p.Name);
		WriteLine("{");
		PushIndent("    ");
        foreach (var pChild in p.Children)
			WritePermissionConsts_Recurse(pChild, string.Concat(Prefix, p.Name, "."));
		PopIndent();
		WriteLine("}");
	}else{
		WriteLine("");
		WriteLine("/// <summary>{0}", p.FriendlyName);
		WriteLine("/// <para>{0}</para>", p.Description);
		WriteLine("/// </summary>");
		WriteLine("public const string {0} = \"{1}{0}\";", p.Name, Prefix);
    }
}

Permission BuildHierarchy(List<Permission> groups, List<Permission> permissions){
	var top = groups[0];

	// Find Parents
    var groupDict = groups.ToDictionary(p => p.FullName);

	// Hierarchy
    foreach (var p in permissions.Where(p => p.IsGroup))
    {
		var g = groupDict[p.Type];
		p.Children = g.Children;
		p.FriendlyName = g.FriendlyName;
		p.Description = g.Description;
    }

	return top;
}

void ParseProjectItem(ProjectItem item, List<Permission> groups, List<Permission> permissions){
	foreach (var ns in item.FileCodeModel.CodeElements.OfType<CodeNamespace>())
    {
        foreach (var cl in ns.Members.OfType<CodeClass2>())
        {
			var g = BuildPermission(cl);
			groups.Add(g);
            foreach (var pr in cl.Members.OfType<CodeProperty2>())
            {
				var p = BuildPermission(pr);
				permissions.Add(p);
				g.Children.Add(p);
            }
			g.Children = g.Children.OrderBy(p => p.IsGroup).OrderBy(p => p.FriendlyName).ToList();
        }
    }
}

IEnumerable<ProjectItem> FindClaimGroupProjectItems(ProjectItem item){
	const string collectionGuid = "{6BB5F8EF-4483-11D3-8BCF-00C04F8EC28C}";

	if (item.Kind == collectionGuid)
        foreach (ProjectItem subItem in item.ProjectItems)
            foreach (var returnItem in FindClaimGroupProjectItems(subItem))
				yield return returnItem;
	else
		yield return item;
}

Permission BuildPermission(CodeClass2 item){
	return new Permission(item.FullName, item.Name, item.FullName, item.Attributes);
}  

Permission BuildPermission(CodeProperty2 item){
	return new Permission(item.FullName, item.Name, item.Type.AsFullName, item.Attributes);
}

[ComImport, Guid("00020400-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDispatch
{
    void Reserved();
    [PreserveSig]
    int GetTypeInfo(uint nInfo, int lcid,
        [MarshalAs(
            UnmanagedType.CustomMarshaler,
            MarshalTypeRef = typeof(TypeToTypeInfoMarshaler))]
        out System.Type typeInfo);
}

class Permission {
	public string FullName {get;set;}
	public string Name {get;set;}
	public string Type {get;set;}
	public bool Hidden {get;set;}

	public bool IsGroup {get { return Type != "System.Boolean"; } }

	public List<Permission> Children {get;set;}
	  
	public string FriendlyName {get;set;}
	public string Description {get;set;}

    public Permission(string FullName, string Name, string Type, CodeElements Attributes)
    {
		this.FullName = FullName;
		this.Name = Name;
		this.Type = Type;
		
		Children = new List<Permission>();

		if (Attributes.Count > 0){
			CodeAttribute att = (CodeAttribute)Attributes.Item("ClaimDetails");
			
			if (att.Children != null){
				
				var attChildren = att.Children.OfType<CodeAttributeArgument>().ToArray();
				this.FriendlyName = attChildren[0].Value.Trim('"');
				this.Description = attChildren[1].Value.Trim('"');

				if (attChildren.Length > 2) {
					this.Hidden = bool.Parse(attChildren[2].Value);
                }
			}
		}
    }
}
#>