//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------

namespace System.Activities
{
    using System;
    using System.Activities.DynamicUpdate;
    using System.Activities.Expressions;
    using System.Activities.Hosting;
    using System.Activities.Runtime;
    using System.Activities.Validation;
    using System.Activities.XamlIntegration;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.Linq;
    using System.Runtime;
    using System.Threading;
    using System.Windows.Markup;
    using System.Xaml;

    [ContentProperty("Implementation")]
    public abstract class Activity
    {
        const string generatedArgumentPrefix = "Argument";
        static int nextCacheId;

        static readonly IList<Activity> emptyChildren = new List<Activity>(0);
        static readonly IList<Variable> emptyVariables = new List<Variable>(0);
        static readonly IList<RuntimeArgument> emptyArguments = new List<RuntimeArgument>(0);
        static readonly IList<ActivityDelegate> emptyDelegates = new List<ActivityDelegate>(0);

        internal static readonly ReadOnlyCollection<Constraint> EmptyConstraints = new ReadOnlyCollection<Constraint>(new Constraint[0]);

        string displayName;
        bool isDisplayNameSet;
        int id;
        RootProperties rootProperties;

        IList<RuntimeArgument> arguments;

        IList<Activity> children;
        IList<Activity> implementationChildren;
        IList<Activity> importedChildren;

        IList<ActivityDelegate> delegates;
        IList<ActivityDelegate> implementationDelegates;
        IList<ActivityDelegate> importedDelegates;

        IList<Variable> variables;
        IList<Variable> implementationVariables;

        IList<ValidationError> tempValidationErrors;
        IList<RuntimeArgument> tempAutoGeneratedArguments;

        Collection<Constraint> constraints;
        Activity runtimeImplementation;

        Activity rootActivity;
        object thisLock;

        QualifiedId qualifiedId;

        // For a given cacheId this tells us whether we've called InternalCacheMetadata yet or not
        CacheStates isMetadataCached;
        int cacheId;

        RelationshipType relationshipToParent;
        Nullable<bool> isSubtreeEmpty;

        int symbolCount;

        // alternatives are extended through DynamicActivity, CodeActivity, and NativeActivity
        protected Activity()
        {
            this.thisLock = new object();
        }

        [TypeConverter(typeof(ImplementationVersionConverter))]
        [DefaultValue(null)]
        protected virtual internal Version ImplementationVersion
        {
            get;
            set;
        }

        [XamlDeferLoad(typeof(FuncDeferringLoader), typeof(Activity))]
        [DefaultValue(null)] 
        [Browsable(false)]
        [Ambient]
        protected virtual Func<Activity> Implementation
        {
            get;
            set;
        }        
                
        protected Collection<Constraint> Constraints
        {
            get
            {
                if (this.constraints == null)
                {
                    this.constraints = new Collection<Constraint>();
                }
                return this.constraints;
            }
        }

        protected internal int CacheId
        {
            get
            {
                return this.cacheId;
            }
        }

        internal RelationshipType RelationshipToParent
        {
            get
            {
                return this.relationshipToParent;
            }
        }

        internal bool HasNonEmptySubtree
        {
            get
            {
                if (this.isSubtreeEmpty.HasValue)
                {
                    return !this.isSubtreeEmpty.Value;
                }
                else
                {
                    if (this.Children.Count > 0 || this.ImplementationChildren.Count > 0 || this.ImportedChildren.Count > 0 ||
                        this.Delegates.Count > 0 || this.ImplementationDelegates.Count > 0 || this.ImportedDelegates.Count > 0 ||
                        this.RuntimeVariables.Count > 0 || this.ImplementationVariables.Count > 0 ||
                        this.RuntimeArguments.Count > 0)
                    {
                        this.isSubtreeEmpty = false;
                    }
                    else
                    {
                        this.isSubtreeEmpty = true;
                    }
                    return !this.isSubtreeEmpty.Value;
                }
            }
        }

        internal int SymbolCount
        {
            get
            {
                return this.symbolCount;
            }
        }

        internal IdSpace MemberOf
        {
            get;
            set;
        }

        internal IdSpace ParentOf
        {
            get;
            set;
        }

        internal QualifiedId QualifiedId
        {
            get
            {
                if (this.qualifiedId == null)
                {
                    this.qualifiedId = new QualifiedId(this);
                }

                return this.qualifiedId;
            }
        }

        // This flag governs special behavior that we need to keep for back-compat on activities
        // that implemented TryGetValue in 4.0.
        internal bool UseOldFastPath
        {
            get;
            set;
        }

        internal bool SkipArgumentResolution
        {
            get;
            set;
        }

        internal bool IsFastPath
        {
            get
            {
                return this.SkipArgumentResolution && IsActivityWithResult;
            }
        }

        internal virtual bool IsActivityWithResult
        {
            get
            {
                return false;
            }
        }

        internal object Origin
        {
            get;
            set;
        }

        public string DisplayName
        {
            get
            {
                if (!this.isDisplayNameSet && string.IsNullOrEmpty(this.displayName))
                {
                    this.displayName = ActivityUtilities.GetDisplayName(this);
                }

                return this.displayName;
            }
            set
            {
                if (value == null)
                {
                    this.displayName = string.Empty;
                }
                else
                {
                    this.displayName = value;
                }
                this.isDisplayNameSet = true;
            }
        }

        public string Id
        {
            get
            {
                if (this.id == 0)
                {
                    return null;
                }
                else
                {
                    return this.QualifiedId.ToString();
                }
            }
        }

        internal bool IsExpressionRoot
        {
            get
            {
                return this.relationshipToParent == RelationshipType.ArgumentExpression;
            }
        }

        internal bool HasStartedCachingMetadata
        {
            get
            {
                return this.isMetadataCached != CacheStates.Uncached;
            }
        }

        internal bool IsMetadataCached
        {
            get
            {
                return this.isMetadataCached != CacheStates.Uncached;
            }
        }

        internal bool IsMetadataFullyCached
        {
            get
            {
                return (this.isMetadataCached & CacheStates.Full) == CacheStates.Full;
            }
        }

        internal bool IsRuntimeReady
        {
            get
            {
                return (this.isMetadataCached & CacheStates.RuntimeReady) == CacheStates.RuntimeReady;
            }
        }

        internal Activity RootActivity
        {
            get
            {
                return this.rootActivity;
            }
        }

        internal int InternalId
        {
            get
            {
                return this.id;
            }
            set
            {
                Fx.Assert(value != 0, "0 is an invalid ID");
                ClearIdInfo();
                this.id = value;
            }
        }

        internal ActivityDelegate HandlerOf
        {
            get;
            private set;
        }

        internal Activity Parent
        {
            get;
            private set;
        }

        internal LocationReferenceEnvironment HostEnvironment
        {
            get
            {
                if (this.RootActivity != null && this.RootActivity.rootProperties != null)
                {
                    return this.RootActivity.rootProperties.HostEnvironment;
                }

                return null;
            }
        }

        internal IList<RuntimeArgument> RuntimeArguments
        {
            get
            {
                return this.arguments;
            }
        }

        internal IList<Activity> Children
        {
            get
            {
                return this.children;
            }
        }

        internal IList<Activity> ImplementationChildren
        {
            get
            {
                return this.implementationChildren;
            }
        }

        internal IList<Activity> ImportedChildren
        {
            get
            {
                return this.importedChildren;
            }
        }

        internal IList<ActivityDelegate> Delegates
        {
            get
            {
                return this.delegates;
            }
        }

        internal IList<ActivityDelegate> ImplementationDelegates
        {
            get
            {
                return this.implementationDelegates;
            }
        }

        internal IList<ActivityDelegate> ImportedDelegates
        {
            get
            {
                return this.importedDelegates;
            }
        }

        internal bool HasBeenAssociatedWithAnInstance
        {
            get
            {
                if (this.rootProperties != null)
                {
                    return this.rootProperties.HasBeenAssociatedWithAnInstance;
                }
                else if (this.IsMetadataCached && this.RootActivity != null && this.RootActivity.rootProperties != null)
                {
                    return this.RootActivity.rootProperties.HasBeenAssociatedWithAnInstance;
                }
                else
                {
                    return false;
                }
            }
            set
            {
                Fx.Assert(this.rootProperties != null, "This should only be called on the root and we should already be cached.");
                Fx.Assert(value, "We really only let you set this to true.");

                this.rootProperties.HasBeenAssociatedWithAnInstance = value;
            }
        }

        internal Dictionary<string, List<RuntimeArgument>> OverloadGroups
        {
            get
            {
                Fx.Assert(this.rootProperties != null || System.Diagnostics.Debugger.IsAttached, "This should only be called on the root.");
                return this.rootProperties.OverloadGroups;
            }
            set
            {
                Fx.Assert(this.rootProperties != null, "This should only be called on the root.");
                this.rootProperties.OverloadGroups = value;
            }
        }

        internal List<RuntimeArgument> RequiredArgumentsNotInOverloadGroups
        {
            get
            {
                Fx.Assert(this.rootProperties != null || System.Diagnostics.Debugger.IsAttached, "This should only be called on the root.");
                return this.rootProperties.RequiredArgumentsNotInOverloadGroups;
            }
            set
            {
                Fx.Assert(this.rootProperties != null, "This should only be called on the root.");
                this.rootProperties.RequiredArgumentsNotInOverloadGroups = value;
            }
        }

        internal ValidationHelper.OverloadGroupEquivalenceInfo EquivalenceInfo
        {
            get
            {
                Fx.Assert(this.rootProperties != null || System.Diagnostics.Debugger.IsAttached, "This should only be called on the root.");
                return this.rootProperties.EquivalenceInfo;
            }
            set
            {
                Fx.Assert(this.rootProperties != null, "This should only be called on the root.");
                this.rootProperties.EquivalenceInfo = value;
            }
        }

        internal IList<Variable> RuntimeVariables
        {
            get
            {
                return this.variables;
            }
        }

        internal IList<Variable> ImplementationVariables
        {
            get
            {
                return this.implementationVariables;
            }
        }

        internal IList<Constraint> RuntimeConstraints
        {
            get
            {
                return InternalGetConstraints();
            }
        }

        internal LocationReferenceEnvironment PublicEnvironment
        {
            get;
            set;
        }

        internal LocationReferenceEnvironment ImplementationEnvironment
        {
            get;
            set;
        }

        internal virtual bool InternalCanInduceIdle
        {
            get
            {
                return false;
            }
        }

        internal bool HasTempViolations
        {
            get
            {
                return (this.tempValidationErrors != null && this.tempValidationErrors.Count > 0);
            }
        }

        internal object ThisLock
        {
            get
            {
                return this.thisLock;
            }
        }

        internal int RequiredExtensionTypesCount
        {
            get
            {
                Fx.Assert(this.rootProperties != null || System.Diagnostics.Debugger.IsAttached, "only callable on the root");
                return this.rootProperties.RequiredExtensionTypesCount;
            }
        }

        internal int DefaultExtensionsCount
        {
            get
            {
                Fx.Assert(this.rootProperties != null || System.Diagnostics.Debugger.IsAttached, "only callable on the root");
                return this.rootProperties.DefaultExtensionsCount;
            }
        }

        internal bool GetActivityExtensionInformation(out Dictionary<Type, WorkflowInstanceExtensionProvider> activityExtensionProviders, out HashSet<Type> requiredActivityExtensionTypes)
        {
            Fx.Assert(this.rootProperties != null, "only callable on the root");
            return this.rootProperties.GetActivityExtensionInformation(out activityExtensionProviders, out requiredActivityExtensionTypes);
        }

        internal virtual bool IsResultArgument(RuntimeArgument argument)
        {
            return false;
        }

        internal bool CanBeScheduledBy(Activity parent)
        {
            // fast path if we're the sole (or first) child
            if (object.ReferenceEquals(parent, this.Parent))
            {
                return this.relationshipToParent == RelationshipType.ImplementationChild || this.relationshipToParent == RelationshipType.Child;
            }
            else
            {
                return parent.Children.Contains(this) || parent.ImplementationChildren.Contains(this);
            }
        }

        internal void ClearIdInfo()
        {
            if (this.ParentOf != null)
            {
                this.ParentOf.Dispose();
                this.ParentOf = null;
            }

            this.id = 0;
            this.qualifiedId = null;
        }

        // We use these Set methods rather than a setter on the property since
        // we don't want to make it seem like setting these collections is the
        // "normal" thing to do.  Only OnInternalCacheMetadata implementations
        // should call these methods.
        internal void SetChildrenCollection(Collection<Activity> children)
        {
            this.children = children;
        }

        internal void AddChild(Activity child)
        {
            if (this.children == null)
            {
                this.children = new Collection<Activity>();
            }

            this.children.Add(child);
        }

        internal void SetImplementationChildrenCollection(Collection<Activity> implementationChildren)
        {
            this.implementationChildren = implementationChildren;
        }

        internal void AddImplementationChild(Activity implementationChild)
        {
            if (this.implementationChildren == null)
            {
                this.implementationChildren = new Collection<Activity>();
            }

            this.implementationChildren.Add(implementationChild);
        }

        internal void SetImportedChildrenCollection(Collection<Activity> importedChildren)
        {
            this.importedChildren = importedChildren;
        }

        internal void AddImportedChild(Activity importedChild)
        {
            if (this.importedChildren == null)
            {
                this.importedChildren = new Collection<Activity>();
            }

            this.importedChildren.Add(importedChild);
        }

        internal void SetDelegatesCollection(Collection<ActivityDelegate> delegates)
        {
            this.delegates = delegates;
        }

        internal void AddDelegate(ActivityDelegate activityDelegate)
        {
            if (this.delegates == null)
            {
                this.delegates = new Collection<ActivityDelegate>();
            }

            this.delegates.Add(activityDelegate);
        }

        internal void SetImplementationDelegatesCollection(Collection<ActivityDelegate> implementationDelegates)
        {
            this.implementationDelegates = implementationDelegates;
        }

        internal void AddImplementationDelegate(ActivityDelegate implementationDelegate)
        {
            if (this.implementationDelegates == null)
            {
                this.implementationDelegates = new Collection<ActivityDelegate>();
            }

            this.implementationDelegates.Add(implementationDelegate);
        }

        internal void SetImportedDelegatesCollection(Collection<ActivityDelegate> importedDelegates)
        {
            this.importedDelegates = importedDelegates;
        }

        internal void AddImportedDelegate(ActivityDelegate importedDelegate)
        {
            if (this.importedDelegates == null)
            {
                this.importedDelegates = new Collection<ActivityDelegate>();
            }

            this.importedDelegates.Add(importedDelegate);
        }

        internal void SetVariablesCollection(Collection<Variable> variables)
        {
            this.variables = variables;
        }

        internal void AddVariable(Variable variable)
        {
            if (this.variables == null)
            {
                this.variables = new Collection<Variable>();
            }

            this.variables.Add(variable);
        }

        internal void SetImplementationVariablesCollection(Collection<Variable> implementationVariables)
        {
            this.implementationVariables = implementationVariables;
        }

        internal void AddImplementationVariable(Variable implementationVariable)
        {
            if (this.implementationVariables == null)
            {
                this.implementationVariables = new Collection<Variable>();
            }

            this.implementationVariables.Add(implementationVariable);
        }

        internal void SetArgumentsCollection(Collection<RuntimeArgument> arguments, bool createEmptyBindings)
        {
            this.arguments = arguments;

            // Arguments should always be "as bound as possible"
            if (this.arguments != null && this.arguments.Count > 0)
            {
                for (int i = 0; i < this.arguments.Count; i++)
                {
                    RuntimeArgument argument = this.arguments[i];

                    argument.SetupBinding(this, createEmptyBindings);
                }

                this.arguments.QuickSort(RuntimeArgument.EvaluationOrderComparer);
            }
        }

        internal void AddArgument(RuntimeArgument argument, bool createEmptyBindings)
        {
            if (this.arguments == null)
            {
                this.arguments = new Collection<RuntimeArgument>();
            }

            argument.SetupBinding(this, createEmptyBindings);
            
            int insertionIndex = this.arguments.BinarySearch(argument, RuntimeArgument.EvaluationOrderComparer);
            if (insertionIndex < 0)
            {
                this.arguments.Insert(~insertionIndex, argument);
            }
            else
            {
                this.arguments.Insert(insertionIndex, argument);
            }
        }

        internal void SetTempValidationErrorCollection(IList<ValidationError> validationErrors)
        {
            this.tempValidationErrors = validationErrors;
        }

        internal void TransferTempValidationErrors(ref IList<ValidationError> newList)
        {
            if (this.tempValidationErrors != null)
            {
                for (int i = 0; i < this.tempValidationErrors.Count; i++)
                {
                    ActivityUtilities.Add(ref newList, this.tempValidationErrors[i]);
                }
            }
            this.tempValidationErrors = null;

        }

        internal void AddTempValidationError(ValidationError validationError)
        {
            if (this.tempValidationErrors == null)
            {
                this.tempValidationErrors = new Collection<ValidationError>();
            }

            this.tempValidationErrors.Add(validationError);
        }

        internal RuntimeArgument AddTempAutoGeneratedArgument(Type argumentType, ArgumentDirection direction)
        {
            if (this.tempAutoGeneratedArguments == null)
            {
                this.tempAutoGeneratedArguments = new Collection<RuntimeArgument>();
            }

            string name = generatedArgumentPrefix + this.tempAutoGeneratedArguments.Count.ToString(CultureInfo.InvariantCulture);
            RuntimeArgument argument = new RuntimeArgument(name, argumentType, direction);
            this.tempAutoGeneratedArguments.Add(argument);
            return argument;
        }

        internal void ResetTempAutoGeneratedArguments()
        {
            this.tempAutoGeneratedArguments = null;
        }

        internal virtual IList<Constraint> InternalGetConstraints()
        {
            if (this.constraints != null && this.constraints.Count > 0)
            {
                return this.constraints;
            }
            else
            {
                return Activity.EmptyConstraints;
            }
        }

        internal static bool NullCheck<T>(T obj)
        {
            return (obj == null);
        }

        public override string ToString()
        {
            return string.Format(CultureInfo.CurrentCulture, "{0}: {1}", this.Id, this.DisplayName);
        }

        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool ShouldSerializeDisplayName()
        {
            return this.isDisplayNameSet;
        }

        // subclasses are responsible for creating/disposing the necessary contexts
        internal virtual void InternalAbort(ActivityInstance instance, ActivityExecutor executor, Exception terminationReason)
        {
        }

        // subclasses are responsible for creating/disposing the necessary contexts
        internal virtual void InternalExecute(ActivityInstance instance, ActivityExecutor executor, BookmarkManager bookmarkManager)
        {
            if (this.runtimeImplementation != null)
            {
                executor.ScheduleActivity(this.runtimeImplementation, instance, null, null, null);
            }
        }

        // subclasses are responsible for creating/disposing the necessary contexts. This implementation
        // covers Activity, Activity<T>, DynamicActivity, DynamicActivity<T>
        internal virtual void InternalCancel(ActivityInstance instance, ActivityExecutor executor, BookmarkManager bookmarkManager)
        {
            NativeActivityContext context = executor.NativeActivityContextPool.Acquire();
            try
            {
                context.Initialize(instance, executor, bookmarkManager);
                context.Cancel();
            }
            finally
            {
                context.Dispose();
                executor.NativeActivityContextPool.Release(context);
            }
        }

        internal bool IsSingletonActivityDeclared(string name)
        {
            if (this.rootActivity == null || this.rootActivity.rootProperties == null)
            {
                return false;
            }
            else
            {
                return this.rootActivity.rootProperties.IsSingletonActivityDeclared(name);
            }
        }

        internal void DeclareSingletonActivity(string name, Activity activity)
        {
            if (this.rootActivity != null && this.rootActivity.rootProperties != null)
            {
                this.rootActivity.rootProperties.DeclareSingletonActivity(name, activity);
            }
        }

        internal Activity GetSingletonActivity(string name)
        {
            if (this.rootActivity != null && this.rootActivity.rootProperties != null)
            {
                return this.rootActivity.rootProperties.GetSingletonActivity(name);
            }

            return null;
        }
        
        internal void ClearCachedInformation()
        {
            ClearCachedMetadata();
            this.isMetadataCached = CacheStates.Uncached;
        }

        internal void InitializeAsRoot(LocationReferenceEnvironment hostEnvironment)
        {
            // We're being treated as the root of the workflow
            this.Parent = null;
            this.ParentOf = null;

            Interlocked.CompareExchange(ref nextCacheId, 1, int.MaxValue);
            this.cacheId = Interlocked.Increment(ref nextCacheId);

            ClearCachedInformation();

            this.MemberOf = new IdSpace();
            this.rootProperties = new RootProperties();
            this.rootProperties.HostEnvironment = hostEnvironment;
            this.rootActivity = this;
        }

        internal LocationReferenceEnvironment GetParentEnvironment()
        {
            LocationReferenceEnvironment parentEnvironment = null;

            if (this.Parent == null)
            {
                Fx.Assert(this.rootProperties != null, "Root properties must be available now.");

                parentEnvironment = new ActivityLocationReferenceEnvironment(this.rootProperties.HostEnvironment) { InternalRoot = this };
            }
            else
            {
                switch (this.relationshipToParent)
                {
                    case RelationshipType.ArgumentExpression:
                        parentEnvironment = this.Parent.PublicEnvironment.Parent;

                        if (parentEnvironment == null)
                        {
                            parentEnvironment = this.RootActivity.rootProperties.HostEnvironment;
                        }
                        break;
                    case RelationshipType.DelegateHandler:
                        Fx.Assert(this.HandlerOf != null, "Must have the parent delegate set");

                        parentEnvironment = this.HandlerOf.Environment;
                        break;
                    case RelationshipType.Child:
                    case RelationshipType.ImportedChild:
                    case RelationshipType.VariableDefault:
                        parentEnvironment = this.Parent.PublicEnvironment;
                        break;
                    case RelationshipType.ImplementationChild:
                        parentEnvironment = this.Parent.ImplementationEnvironment;
                        break;
                }
            }

            return parentEnvironment;
        }

        internal bool InitializeRelationship(ActivityDelegate activityDelegate, ActivityCollectionType collectionType, ref IList<ValidationError> validationErrors)
        {
            if (this.cacheId == activityDelegate.Owner.CacheId)
            {
                // This means that we already have a parent and a delegate is trying to initialize
                // a relationship.  Delegate handlers MUST be declared.

                ActivityUtilities.Add(ref validationErrors, new ValidationError(SR.ActivityDelegateHandlersMustBeDeclarations(this.DisplayName, activityDelegate.Owner.DisplayName, this.Parent.DisplayName), false, activityDelegate.Owner));

                return false;
            }

            if (InitializeRelationship(activityDelegate.Owner, collectionType != ActivityCollectionType.Implementation, RelationshipType.DelegateHandler, ref validationErrors))
            {
                this.HandlerOf = activityDelegate;

                return true;
            }

            return false;
        }

        internal bool InitializeRelationship(RuntimeArgument argument, ref IList<ValidationError> validationErrors)
        {
            return InitializeRelationship(argument.Owner, true, RelationshipType.ArgumentExpression, ref validationErrors);
        }

        internal bool InitializeRelationship(Variable variable, bool isPublic, ref IList<ValidationError> validationErrors)
        {
            return InitializeRelationship(variable.Owner, isPublic, RelationshipType.VariableDefault, ref validationErrors);
        }

        internal bool InitializeRelationship(Activity parent, ActivityCollectionType collectionType, ref IList<ValidationError> validationErrors)
        {
            RelationshipType relationshipType = RelationshipType.Child;
            if (collectionType == ActivityCollectionType.Imports)
            {
                relationshipType = RelationshipType.ImportedChild;
            }
            else if (collectionType == ActivityCollectionType.Implementation)
            {
                relationshipType = RelationshipType.ImplementationChild;
            }

            return InitializeRelationship(parent, collectionType != ActivityCollectionType.Implementation, relationshipType, ref validationErrors);
        }

        bool InitializeRelationship(Activity parent, bool isPublic, RelationshipType relationship, ref IList<ValidationError> validationErrors)
        {
            if (this.cacheId == parent.cacheId)
            {
                // This means that we've already encountered a parent in the tree

                // Validate that it is visible.

                // In order to see the activity the new parent must be
                // in the implementation IdSpace of an activity which has
                // a public reference to it.
                Activity referenceTarget = parent.MemberOf.Owner;

                if (object.ReferenceEquals(this, parent))
                {
                    ActivityUtilities.Add(ref validationErrors, new ValidationError(SR.ActivityCannotReferenceItself(this.DisplayName), parent));

                    return false;
                }
                else if (this.Parent == null)
                {
                    ActivityUtilities.Add(ref validationErrors, new ValidationError(SR.RootActivityCannotBeReferenced(this.DisplayName, parent.DisplayName), parent));

                    return false;
                }
                else if (referenceTarget == null)
                {
                    ActivityUtilities.Add(ref validationErrors, new ValidationError(SR.ActivityCannotBeReferencedWithoutTarget(this.DisplayName, parent.DisplayName, this.Parent.DisplayName), parent));

                    return false;
                }
                else if (!referenceTarget.Children.Contains(this) && !referenceTarget.ImportedChildren.Contains(this))
                {
                    ActivityUtilities.Add(ref validationErrors, new ValidationError(SR.ActivityCannotBeReferenced(this.DisplayName, parent.DisplayName, referenceTarget.DisplayName, this.Parent.DisplayName), false, parent));

                    return false;
                }

                // This is a valid reference so we want to allow
                // normal processing to proceed.
                return true;
            }

            this.Parent = parent;
            this.HandlerOf = null;
            this.rootActivity = parent.RootActivity;
            this.cacheId = parent.cacheId;
            this.isMetadataCached = CacheStates.Uncached;            
            ClearCachedMetadata();
            this.relationshipToParent = relationship;

            if (isPublic)
            {
                this.MemberOf = parent.MemberOf;
            }
            else
            {
                if (parent.ParentOf == null)
                {
                    parent.ParentOf = new IdSpace(parent.MemberOf, parent.InternalId);
                }

                this.MemberOf = parent.ParentOf;
            }

            return true;
        }

        void ClearCachedMetadata()
        {
            this.symbolCount = 0;

            this.arguments = null;

            this.children = null;
            this.implementationChildren = null;
            this.importedChildren = null;

            this.delegates = null;
            this.implementationDelegates = null;
            this.importedDelegates = null;

            this.variables = null;
            this.implementationVariables = null;
        }

        internal void InternalCacheMetadata(bool createEmptyBindings, ref IList<ValidationError> validationErrors)
        {
            OnInternalCacheMetadata(createEmptyBindings);

            if (this.tempAutoGeneratedArguments != null)
            {
                Fx.Assert(this.tempAutoGeneratedArguments.Count > 0, "We should only have a non-null value here if we generated an argument");
                if (!this.SkipArgumentResolution)
                {
                    ActivityUtilities.Add(ref validationErrors, new ValidationError(
                        SR.PublicReferencesOnActivityRequiringArgumentResolution(this.DisplayName), false, this));
                }

                if (this.arguments == null)
                {
                    this.arguments = this.tempAutoGeneratedArguments;
                }
                else
                {
                    for (int i = 0; i < this.tempAutoGeneratedArguments.Count; i++)
                    {
                        this.arguments.Add(this.tempAutoGeneratedArguments[i]);
                    }
                }

                this.tempAutoGeneratedArguments = null;
            }

            if (this.arguments != null && this.arguments.Count > 1)
            {
                ActivityValidationServices.ValidateEvaluationOrder(this.arguments, this, ref this.tempValidationErrors);
            }

            if (this.tempValidationErrors != null)
            {
                if (validationErrors == null)
                {
                    validationErrors = new List<ValidationError>();
                }

                for (int i = 0; i < this.tempValidationErrors.Count; i++)
                {
                    ValidationError validationError = this.tempValidationErrors[i];

                    validationError.Source = this;
                    validationError.Id = this.Id;

                    validationErrors.Add(validationError);
                }

                this.tempValidationErrors = null;
            }

            if (this.arguments == null)
            {
                this.arguments = emptyArguments;
            }
            else
            {
                this.symbolCount += this.arguments.Count;
            }

            if (this.variables == null)
            {
                this.variables = emptyVariables;
            }
            else
            {
                this.symbolCount += this.variables.Count;
            }

            if (this.implementationVariables == null)
            {
                this.implementationVariables = emptyVariables;
            }
            else
            {
                this.symbolCount += this.implementationVariables.Count;
            }

            if (this.children == null)
            {
                this.children = emptyChildren;
            }

            if (this.importedChildren == null)
            {
                this.importedChildren = emptyChildren;
            }

            if (this.implementationChildren == null)
            {
                this.implementationChildren = emptyChildren;
            }

            if (this.delegates == null)
            {
                this.delegates = emptyDelegates;
            }

            if (this.importedDelegates == null)
            {
                this.importedDelegates = emptyDelegates;
            }

            if (this.implementationDelegates == null)
            {
                this.implementationDelegates = emptyDelegates;
            }

            this.isMetadataCached = CacheStates.Partial;
        }

        // Note that this is relative to the type of walk we've done.  If we
        // skipped implementation then we can still be "Cached" even though
        // we never ignored the implementation.
        internal void SetCached(bool isSkippingPrivateChildren)
        {
            if (isSkippingPrivateChildren)
            {
                this.isMetadataCached = CacheStates.Partial;
            }
            else 
            {
                this.isMetadataCached = CacheStates.Full;
            }
        }

        internal void SetRuntimeReady()
        {
            this.isMetadataCached |= CacheStates.RuntimeReady;
        }

        internal virtual void OnInternalCacheMetadata(bool createEmptyBindings)
        {
            // By running CacheMetadata first we allow the user
            // to set their Implementation during CacheMetadata.
            ActivityMetadata metadata = new ActivityMetadata(this, GetParentEnvironment(), createEmptyBindings);
            CacheMetadata(metadata);
            metadata.Dispose();

            if (this.Implementation != null)
            {
                this.runtimeImplementation = this.Implementation();
            }
            else
            {
                this.runtimeImplementation = null;
            }

            if (this.runtimeImplementation != null)
            {
                SetImplementationChildrenCollection(new Collection<Activity>
                {
                    this.runtimeImplementation
                });
            }
        }

        protected virtual void CacheMetadata(ActivityMetadata metadata)
        {
            ReflectedInformation information = new ReflectedInformation(this);

            SetImportedChildrenCollection(information.GetChildren());
            SetVariablesCollection(information.GetVariables());
            SetImportedDelegatesCollection(information.GetDelegates());
            SetArgumentsCollection(information.GetArguments(), metadata.CreateEmptyBindings);
        }

        internal virtual void OnInternalCreateDynamicUpdateMap(DynamicUpdateMapBuilder.Finalizer finalizer,
            DynamicUpdateMapBuilder.IDefinitionMatcher matcher, Activity originalActivity)
        {
            UpdateMapMetadata metadata = new UpdateMapMetadata(finalizer, matcher, this);
            try
            {
                OnCreateDynamicUpdateMap(metadata, originalActivity);
            }
            finally
            {
                metadata.Dispose();
            }
        }

        protected virtual void OnCreateDynamicUpdateMap(UpdateMapMetadata metadata, Activity originalActivity)
        {
        }

        internal void AddDefaultExtensionProvider<T>(Func<T> extensionProvider)
            where T : class
        {
            Fx.Assert(extensionProvider != null, "caller must verify");
            Fx.Assert(this.rootActivity != null && this.rootActivity.rootProperties != null, "need a valid root");
            this.rootActivity.rootProperties.AddDefaultExtensionProvider(extensionProvider);
        }

        internal void RequireExtension(Type extensionType)
        {
            Fx.Assert(extensionType != null && !extensionType.IsValueType, "caller should verify we have a valid reference type");
            Fx.Assert(this.rootActivity != null && this.rootActivity.rootProperties != null, "need a valid root");
            this.rootActivity.rootProperties.RequireExtension(extensionType);
        }

        // information used by root activities
        class RootProperties
        {
            Dictionary<string, Activity> singletonActivityNames;
            Dictionary<Type, WorkflowInstanceExtensionProvider> activityExtensionProviders;
            HashSet<Type> requiredExtensionTypes;

            public RootProperties()
            {
            }

            public bool HasBeenAssociatedWithAnInstance
            {
                get;
                set;
            }

            public LocationReferenceEnvironment HostEnvironment
            {
                get;
                set;
            }

            public Dictionary<string, List<RuntimeArgument>> OverloadGroups
            {
                get;
                set;
            }

            public List<RuntimeArgument> RequiredArgumentsNotInOverloadGroups
            {
                get;
                set;
            }

            public ValidationHelper.OverloadGroupEquivalenceInfo EquivalenceInfo
            {
                get;
                set;
            }

            public int DefaultExtensionsCount
            {
                get
                {
                    if (this.activityExtensionProviders != null)
                    {
                        return this.activityExtensionProviders.Count;
                    }
                    else
                    {
                        return 0;
                    }
                }
            }

            public int RequiredExtensionTypesCount
            {
                get
                {
                    if (this.requiredExtensionTypes != null)
                    {
                        return this.requiredExtensionTypes.Count;
                    }
                    else
                    {
                        return 0;
                    }
                }
            }

            public bool GetActivityExtensionInformation(out Dictionary<Type, WorkflowInstanceExtensionProvider> activityExtensionProviders, out HashSet<Type> requiredActivityExtensionTypes)
            {
                activityExtensionProviders = this.activityExtensionProviders;
                requiredActivityExtensionTypes = this.requiredExtensionTypes;
                return activityExtensionProviders != null || (requiredExtensionTypes != null && requiredExtensionTypes.Count > 0);
            }

            public void AddDefaultExtensionProvider<T>(Func<T> extensionProvider)
                where T : class
            {
                Type key = typeof(T);
                if (this.activityExtensionProviders == null)
                {
                    this.activityExtensionProviders = new Dictionary<Type, WorkflowInstanceExtensionProvider>();
                }
                else
                {
                    if (this.activityExtensionProviders.ContainsKey(key))
                    {
                        return; // already have a provider of this type
                    }
                }

                this.activityExtensionProviders.Add(key, new WorkflowInstanceExtensionProvider<T>(extensionProvider));

                // if we're providing an extension that exactly matches a required type, simplify further bookkeeping
                if (this.requiredExtensionTypes != null)
                {
                    this.requiredExtensionTypes.Remove(key);
                }
            }

            public void RequireExtension(Type extensionType)
            {
                // if we're providing an extension that exactly matches a required type, don't bother with further bookkeeping
                if (this.activityExtensionProviders != null && this.activityExtensionProviders.ContainsKey(extensionType))
                {
                    return;
                }

                if (this.requiredExtensionTypes == null)
                {
                    this.requiredExtensionTypes = new HashSet<Type>();
                }
                this.requiredExtensionTypes.Add(extensionType);
            }

            public bool IsSingletonActivityDeclared(string name)
            {
                if (this.singletonActivityNames == null)
                {
                    return false;
                }
                else
                {
                    return this.singletonActivityNames.ContainsKey(name);
                }
            }

            public void DeclareSingletonActivity(string name, Activity activity)
            {
                if (this.singletonActivityNames == null)
                {
                    this.singletonActivityNames = new Dictionary<string, Activity>(1);
                }

                this.singletonActivityNames.Add(name, activity);
            }

            public Activity GetSingletonActivity(string name)
            {
                Activity result = null;
                if (this.singletonActivityNames != null)
                {
                    this.singletonActivityNames.TryGetValue(name, out result);
                }

                return result;
            }
        }

        internal class ReflectedInformation
        {
            Activity parent;

            Collection<RuntimeArgument> arguments;
            Collection<Variable> variables;
            Collection<Activity> children;
            Collection<ActivityDelegate> delegates;

            static Type DictionaryArgumentHelperType = typeof(DictionaryArgumentHelper<>);
            static Type OverloadGroupAttributeType = typeof(OverloadGroupAttribute);

            public ReflectedInformation(Activity owner)
                : this(owner, ReflectedType.All)
            {
            }

            ReflectedInformation(Activity activity, ReflectedType reflectType)
            {
                this.parent = activity;

                // reflect over our activity and gather relevant pieces of the system so that the developer
                // doesn't need to worry about "zipping up" his model to the constructs necessary for the
                // runtime to function correctly
                foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(activity))
                {
                    ArgumentDirection direction;
                    Type argumentType;
                    if ((reflectType & ReflectedType.Argument) == ReflectedType.Argument &&
                        ActivityUtilities.TryGetArgumentDirectionAndType(propertyDescriptor.PropertyType, out direction, out argumentType))
                    {
                        // We only do our magic for generic argument types.  If the property is a non-generic
                        // argument type then that means the type of the RuntimeArgument should be based on
                        // the type of the argument bound to it.  The activity author is responsible for dealing
                        // with these dynamic typing cases.
                        if (propertyDescriptor.PropertyType.IsGenericType)
                        {
                            bool isRequired = GetIsArgumentRequired(propertyDescriptor);
                            List<string> overloadGroupNames = GetOverloadGroupNames(propertyDescriptor);
                            RuntimeArgument argument = new RuntimeArgument(propertyDescriptor.Name, argumentType, direction, isRequired, overloadGroupNames, propertyDescriptor, activity);
                            Add<RuntimeArgument>(ref this.arguments, argument);
                        }
                    }
                    else if ((reflectType & ReflectedType.Variable) == ReflectedType.Variable &&
                        ActivityUtilities.IsVariableType(propertyDescriptor.PropertyType))
                    {
                        Variable variable = propertyDescriptor.GetValue(activity) as Variable;
                        if (variable != null)
                        {
                            Add<Variable>(ref this.variables, variable);
                        }
                    }
                    else if ((reflectType & ReflectedType.Child) == ReflectedType.Child &&
                        ActivityUtilities.IsActivityType(propertyDescriptor.PropertyType))
                    {
                        Activity workflowElement = propertyDescriptor.GetValue(activity) as Activity;
                        Add<Activity>(ref this.children, workflowElement);
                    }
                    else if ((reflectType & ReflectedType.ActivityDelegate) == ReflectedType.ActivityDelegate &&
                        ActivityUtilities.IsActivityDelegateType(propertyDescriptor.PropertyType))
                    {
                        ActivityDelegate activityDelegate = propertyDescriptor.GetValue(activity) as ActivityDelegate;
                        Add<ActivityDelegate>(ref this.delegates, activityDelegate);
                    }
                    else
                    {
                        Type innerType;
                        bool foundMatch = false;
                        if ((reflectType & ReflectedType.Argument) == ReflectedType.Argument)
                        {
                            object property = propertyDescriptor.GetValue(activity);
                            if (property != null)
                            {
                                IList<RuntimeArgument> runtimeArguments = DictionaryArgumentHelper.TryGetRuntimeArguments(property, propertyDescriptor.Name);
                                if (runtimeArguments != null)
                                {
                                    this.AddCollection(ref this.arguments, runtimeArguments);
                                    foundMatch = true;
                                }
                                else if (ActivityUtilities.IsArgumentDictionaryType(propertyDescriptor.PropertyType, out innerType))
                                {
                                    Type concreteHelperType = DictionaryArgumentHelperType.MakeGenericType(innerType);
                                    DictionaryArgumentHelper helper = Activator.CreateInstance(concreteHelperType, new object[] { property, propertyDescriptor.Name }) as DictionaryArgumentHelper;
                                    this.AddCollection(ref this.arguments, helper.RuntimeArguments);
                                    foundMatch = true;
                                }
                            }
                        }

                        if (!foundMatch && ActivityUtilities.IsKnownCollectionType(propertyDescriptor.PropertyType, out innerType))
                        {
                            if ((reflectType & ReflectedType.Variable) == ReflectedType.Variable &&
                                ActivityUtilities.IsVariableType(innerType))
                            {
                                IEnumerable enumerable = propertyDescriptor.GetValue(activity) as IEnumerable;

                                AddCollection(ref this.variables, enumerable);
                            }
                            else if ((reflectType & ReflectedType.Child) == ReflectedType.Child &&
                                ActivityUtilities.IsActivityType(innerType, false))
                            {
                                IEnumerable enumerable = propertyDescriptor.GetValue(activity) as IEnumerable;

                                AddCollection(ref this.children, enumerable);
                            }
                            else if ((reflectType & ReflectedType.ActivityDelegate) == ReflectedType.ActivityDelegate &&
                                ActivityUtilities.IsActivityDelegateType(innerType))
                            {
                                IEnumerable enumerable = propertyDescriptor.GetValue(activity) as IEnumerable;

                                AddCollection(ref this.delegates, enumerable);
                            }
                        }
                    }
                }
            }

            public static Collection<RuntimeArgument> GetArguments(Activity parent)
            {
                Collection<RuntimeArgument> arguments = null;

                if (parent != null)
                {
                    arguments = new ReflectedInformation(parent, ReflectedType.Argument).GetArguments();
                }

                if (arguments == null)
                {
                    arguments = new Collection<RuntimeArgument>();
                }

                return arguments;
            }

            public static Collection<Variable> GetVariables(Activity parent)
            {
                Collection<Variable> variables = null;

                if (parent != null)
                {
                    variables = new ReflectedInformation(parent, ReflectedType.Variable).GetVariables();
                }

                if (variables == null)
                {
                    variables = new Collection<Variable>();
                }

                return variables;
            }

            public static Collection<Activity> GetChildren(Activity parent)
            {
                Collection<Activity> children = null;

                if (parent != null)
                {
                    children = new ReflectedInformation(parent, ReflectedType.Child).GetChildren();
                }

                if (children == null)
                {
                    children = new Collection<Activity>();
                }

                return children;
            }

            public static Collection<ActivityDelegate> GetDelegates(Activity parent)
            {
                Collection<ActivityDelegate> delegates = null;

                if (parent != null)
                {
                    delegates = new ReflectedInformation(parent, ReflectedType.ActivityDelegate).GetDelegates();
                }

                if (delegates == null)
                {
                    delegates = new Collection<ActivityDelegate>();
                }

                return delegates;
            }

            public Collection<RuntimeArgument> GetArguments()
            {
                return this.arguments;
            }

            public Collection<Variable> GetVariables()
            {
                return this.variables;
            }

            public Collection<Activity> GetChildren()
            {
                return this.children;
            }

            public Collection<ActivityDelegate> GetDelegates()
            {
                return this.delegates;
            }

            void AddCollection<T>(ref Collection<T> list, IEnumerable enumerable)
                where T : class
            {
                if (enumerable != null)
                {
                    foreach (object obj in enumerable)
                    {
                        if (obj != null && obj is T)
                        {
                            Add<T>(ref list, (T)obj);
                        }
                    }
                }
            }

            void Add<T>(ref Collection<T> list, T data)
            {
                if (data != null)
                {
                    if (list == null)
                    {
                        list = new Collection<T>();
                    }
                    list.Add(data);
                }
            }

            bool GetIsArgumentRequired(PropertyDescriptor propertyDescriptor)
            {
                return propertyDescriptor.Attributes[typeof(RequiredArgumentAttribute)] != null;
            }

            List<string> GetOverloadGroupNames(PropertyDescriptor propertyDescriptor)
            {
                List<string> overloadGroupNames = new List<string>(0);
                AttributeCollection propertyAttributes = propertyDescriptor.Attributes;
                for (int i = 0; i < propertyAttributes.Count; i++)
                {
                    Attribute attribute = propertyAttributes[i];
                    if (ReflectedInformation.OverloadGroupAttributeType.IsAssignableFrom(attribute.GetType()))
                    {
                        overloadGroupNames.Add(((OverloadGroupAttribute)attribute).GroupName);
                    }
                }
                return overloadGroupNames;
            }

            [Flags]
            enum ReflectedType
            {
                Argument = 0X1,
                Variable = 0X2,
                Child = 0X4,
                ActivityDelegate = 0X8,
                All = 0XF
            }

            class DictionaryArgumentHelper
            {
                protected DictionaryArgumentHelper()
                {
                }

                public IList<RuntimeArgument> RuntimeArguments
                {
                    get;
                    protected set;
                }

                public static IList<RuntimeArgument> TryGetRuntimeArguments(object propertyValue, string propertyName)
                {
                    // special case each of the non-generic argument types to avoid reflection costs

                    IEnumerable<KeyValuePair<string, Argument>> argumentEnumerable = propertyValue as IEnumerable<KeyValuePair<string, Argument>>;
                    if (argumentEnumerable != null)
                    {
                        return GetRuntimeArguments(argumentEnumerable, propertyName);
                    }

                    IEnumerable<KeyValuePair<string, InArgument>> inArgumentEnumerable = propertyValue as IEnumerable<KeyValuePair<string, InArgument>>;
                    if (inArgumentEnumerable != null)
                    {
                        return GetRuntimeArguments(inArgumentEnumerable, propertyName);
                    }

                    IEnumerable<KeyValuePair<string, OutArgument>> outArgumentEnumerable = propertyValue as IEnumerable<KeyValuePair<string, OutArgument>>;
                    if (outArgumentEnumerable != null)
                    {
                        return GetRuntimeArguments(outArgumentEnumerable, propertyName);
                    }

                    IEnumerable<KeyValuePair<string, InOutArgument>> inOutArgumentEnumerable = propertyValue as IEnumerable<KeyValuePair<string, InOutArgument>>;
                    if (inOutArgumentEnumerable != null)
                    {
                        return GetRuntimeArguments(inOutArgumentEnumerable, propertyName);
                    }

                    return null;
                }

                protected static IList<RuntimeArgument> GetRuntimeArguments<T>(IEnumerable<KeyValuePair<string, T>> argumentDictionary, string propertyName) where T : Argument
                {
                    IList<RuntimeArgument> runtimeArguments = new List<RuntimeArgument>();

                    foreach (KeyValuePair<string, T> pair in argumentDictionary)
                    {
                        string key = pair.Key;
                        Argument value = pair.Value;

                        if (value == null)
                        {
                            string argName = (key == null) ? "<null>" : key;
                            throw FxTrace.Exception.AsError(new ValidationException(SR.MissingArgument(argName, propertyName)));
                        }
                        if (string.IsNullOrEmpty(key))
                        {
                            throw FxTrace.Exception.AsError(new ValidationException(SR.MissingNameProperty(value.ArgumentType)));
                        }

                        RuntimeArgument runtimeArgument = new RuntimeArgument(key, value.ArgumentType, value.Direction, false, null, value);
                        runtimeArguments.Add(runtimeArgument);
                    }

                    return runtimeArguments;
                }
            }

            class DictionaryArgumentHelper<T> : DictionaryArgumentHelper where T : Argument
            {
                public DictionaryArgumentHelper(object propertyValue, string propertyName)
                    : base()
                {
                    IEnumerable<KeyValuePair<string, T>> argumentDictionary = propertyValue as IEnumerable<KeyValuePair<string, T>>;

                    this.RuntimeArguments = GetRuntimeArguments(argumentDictionary, propertyName);
                }
            }

        }

        internal enum RelationshipType : byte
        {
            Child = 0x00,
            ImportedChild = 0x01,
            ImplementationChild = 0x02,
            DelegateHandler = 0x03,
            ArgumentExpression = 0x04,
            VariableDefault = 0x05
        }

        enum CacheStates : byte
        {
            // We don't have valid cached data
            Uncached = 0x00,

            // The next two states are mutually exclusive:

            // The activity has its own metadata cached, or private implementation are skipped
            Partial = 0x01,

            // The activity has its own metadata and its private implementation cached
            // We can make use of the roll-up metadata (like
            // SubtreeHasConstraints).
            Full = 0x02,

            // The next state can be ORed with the last two:

            // The cached data is ready for runtime use
            RuntimeReady = 0x04
        }
    }

    [TypeConverter(typeof(ActivityWithResultConverter))]
    [ValueSerializer(typeof(ActivityWithResultValueSerializer))]
    public abstract class Activity<TResult> : ActivityWithResult
    {
        // alternatives are extended through DynamicActivity<TResult>, CodeActivity<TResult>, and NativeActivity<TResult>
        protected Activity()
            : base()
        {
        }

        [DefaultValue(null)]
        public new OutArgument<TResult> Result
        {
            get;
            set;
        }

        internal override Type InternalResultType
        {
            get
            {
                return typeof(TResult);
            }
        }

        internal override OutArgument ResultCore
        {
            get
            {
                return this.Result;
            }
            set
            {
                this.Result = value as OutArgument<TResult>;

                if (this.Result == null && value != null)
                {
                    throw FxTrace.Exception.Argument("value", SR.ResultArgumentMustBeSpecificType(typeof(TResult)));
                }
            }
        }

        public static implicit operator Activity<TResult>(TResult constValue)
        {
            return FromValue(constValue);
        }

        public static implicit operator Activity<TResult>(Variable variable)
        {
            return FromVariable(variable);
        }

        public static implicit operator Activity<TResult>(Variable<TResult> variable)
        {
            return FromVariable(variable);
        }

        public static Activity<TResult> FromValue(TResult constValue)
        {
            return new Literal<TResult> { Value = constValue };
        }

        public static Activity<TResult> FromVariable(Variable variable)
        {
            if (variable == null)
            {
                throw FxTrace.Exception.ArgumentNull("variable");
            }

            if (TypeHelper.AreTypesCompatible(variable.Type, typeof(TResult)))
            {
                return new VariableValue<TResult> { Variable = variable };
            }
            else
            {
                Type locationGenericType;
                if (ActivityUtilities.IsLocationGenericType(typeof(TResult), out locationGenericType))
                {
                    if (locationGenericType == variable.Type)
                    {
                        return (Activity<TResult>)ActivityUtilities.CreateVariableReference(variable);
                    }
                }
            }

            throw FxTrace.Exception.Argument("variable", SR.ConvertVariableToValueExpressionFailed(variable.GetType().FullName, typeof(Activity<TResult>).FullName));
        }

        [SuppressMessage(FxCop.Category.Design, FxCop.Rule.ConsiderPassingBaseTypesAsParameters,
            Justification = "Generic needed for type inference")]
        public static Activity<TResult> FromVariable(Variable<TResult> variable)
        {
            if (variable == null)
            {
                throw FxTrace.Exception.ArgumentNull("variable");
            }

            return new VariableValue<TResult>(variable);
        }

        internal override bool IsResultArgument(RuntimeArgument argument)
        {
            return object.ReferenceEquals(argument, this.ResultRuntimeArgument);
        }

        internal sealed override void OnInternalCacheMetadata(bool createEmptyBindings)
        {
            OnInternalCacheMetadataExceptResult(createEmptyBindings);

            bool foundResult = false;

            // This could be null at this point
            IList<RuntimeArgument> runtimeArguments = this.RuntimeArguments;

            int runtimeArgumentCount = 0;

            if (runtimeArguments != null)
            {
                runtimeArgumentCount = runtimeArguments.Count;

                for (int i = 0; i < runtimeArgumentCount; i++)
                {
                    RuntimeArgument argument = runtimeArguments[i];

                    if (argument.Name == "Result")
                    {
                        foundResult = true;

                        if (argument.Type != typeof(TResult) || argument.Direction != ArgumentDirection.Out)
                        {
                            // The user supplied "Result" is incorrect so we
                            // log a violation.
                            AddTempValidationError(new ValidationError(SR.ResultArgumentHasRequiredTypeAndDirection(typeof(TResult), argument.Direction, argument.Type)));
                        }
                        else if (!IsBoundArgumentCorrect(argument, createEmptyBindings))
                        {
                            // The user supplied "Result" is not bound to the correct
                            // argument object.
                            AddTempValidationError(new ValidationError(SR.ResultArgumentMustBeBoundToResultProperty));
                        }
                        else
                        {
                            // The user supplied "Result" is correct so we
                            // cache it.
                            this.ResultRuntimeArgument = argument;
                        }

                        break;
                    }
                }
            }

            if (!foundResult)
            {
                this.ResultRuntimeArgument = new RuntimeArgument("Result", typeof(TResult), ArgumentDirection.Out);

                if (this.Result == null)
                {
                    if (createEmptyBindings)
                    {
                        this.Result = new OutArgument<TResult>();
                        Argument.Bind(this.Result, this.ResultRuntimeArgument);
                    }
                    else
                    {
                        OutArgument<TResult> tempArgument = new OutArgument<TResult>();
                        Argument.Bind(tempArgument, this.ResultRuntimeArgument);
                    }
                }
                else
                {
                    Argument.Bind(this.Result, this.ResultRuntimeArgument);
                }


                AddArgument(this.ResultRuntimeArgument, createEmptyBindings);
            }
        }

        bool IsBoundArgumentCorrect(RuntimeArgument argument, bool createEmptyBindings)
        {
            if (createEmptyBindings)
            {
                // We must match if we've gone through
                // RuntimeArgument.SetupBinding with
                // createEmptyBindings == true.
                return object.ReferenceEquals(argument.BoundArgument, this.Result);
            }
            else
            {
                // Otherwise, if the Result is null then
                // SetupBinding has created a default
                // BoundArgument which is fine.  If it
                // is non-null then it had better match.
                return this.Result == null || object.ReferenceEquals(argument.BoundArgument, this.Result);
            }
        }

        internal virtual void OnInternalCacheMetadataExceptResult(bool createEmptyBindings)
        {
            // default to Activity's behavior
            base.OnInternalCacheMetadata(createEmptyBindings);
        }

        internal override object InternalExecuteInResolutionContextUntyped(CodeActivityContext resolutionContext)
        {
            return InternalExecuteInResolutionContext(resolutionContext);
        }

        internal virtual TResult InternalExecuteInResolutionContext(CodeActivityContext resolutionContext)
        {
            throw Fx.AssertAndThrow("This should only be called on CodeActivity<T>");
        }
    }
}


