// Subject: Complex object with set of properties and inner objectss
// Mission: Describe fluent DSL to define scheme and delegate action execution 
// 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Text.RegularExpressions;

namespace ConsoleApplication1
{
    internal class Program
    {
        // Scheme:
        
        public class Group
        {
            public string Name;
        }

        private class User
        {
            public string Profile { get; set; }
            public Group Group;
        }

        // DSL:
        public class Cascade<T> where T:class ,new ()
        {
            private T Entity;
            protected List<Func<object>> References = new List<Func<object>>(); 
            
            public Cascade(T entity) { Entity = entity; }

            public Cascade(T entity, List<Func< object>> references)
            {
                Entity = entity;
                References = references;
            }
            

            public Cascade<T> Reference(Expression<Func<T, object>> expression)
            {
                RegisterReference(expression);
                return this;
            }

            public Cascade<TEntity> Depended<TEntity>(Expression<Func<T, TEntity>> expression) where TEntity : class, new()
            {
                TEntity depended = RegisterReference(expression)(Entity);
                return new Cascade<TEntity>(depended, References);
            }


            public void Execute(string action)
            {
                foreach (Func<object> reference in References)
                {
                    var value = reference();
                    Console.WriteLine("Executed {0} for {1}",action, value);
                }
            }

            public Func<T, TResult> RegisterReference<TResult>(Expression<Func<T, TResult>> expression) where TResult : class, new ()

            {
                Func<T, TResult> func = expression.Compile();
                References.Add(() => func(Entity) );
                Debug("Registered", expression);
                return func;
            }

            // Action delegate:
            private static void Debug<TResult>(string action, Expression<Func<T, TResult>> expression)
            {
                Console.WriteLine("{0} {1}", action, MemberExpressionName(expression));
            }
            
            private static string MemberExpressionName<TResult>(Expression<Func<T, TResult>> expression)
            {
                MemberExpression memberExpression = expression.Body as MemberExpression;
               return memberExpression.Member.Name;
            }
        }

        // Usage:
        private static void Main(string[] args)
        {
            User user = new User();
            user.Profile = "admin";
            user.Group = new Group();
            user.Group.Name = "users";

            Cascade<User> userCascade
                = new Cascade<User>(user);

            userCascade
                .Reference(u => u.Profile)
                .Depended(u => u.Group)
                    .Reference(group => group.Name);

            userCascade.Execute("delete");


            Console.ReadLine();

            return;
        }
    }
}