/// <summary>
 /// This is used by the incremental analysis when a caller is removed of modified
 /// </summary>
 /// <param name="context"></param>
 public void RemoveFromCallers(CallContext context)
 {
     //callers = callers.Remove(context);
     callers.Remove(context);
 }
 /// <summary>
 /// Return an EntityProccesor. It is the class that performs the analysis using the data in the entity
 /// </summary>
 /// <param name="dispatcher"></param>
 /// <returns></returns>
 //public IEntityProcessor GetEntityProcessor(IDispatcher dispatcher)
 //{
 //    //if (this.EntityProcessor == null)
 //    //{
 //    //    EntityProcessor = new MethodEntityProcessor(this, dispatcher, true);
 //    //}
 //    //return EntityProcessor;
 //    return new MethodEntityProcessor(this, dispatcher, true);
 //}
 public void AddToCallers(CallContext context)
 {
     //callers = callers.Add(context);
     callers.Add(context);
 }
        /// <summary>
        /// Async versions of DispachReturnMessage
        /// </summary>
        /// <param name="context"></param>
        /// <param name="returnVariable"></param>
        /// <param name="propKind"></param>
        /// <returns></returns>
        internal async Task DispachReturnMessageAsync(CallContext context, VariableNode returnVariable, PropagationKind propKind)
		{
			var caller = context.Caller;
			var lhs = context.CallLHS;
			var types = returnVariable != null ?
				GetTypes(returnVariable, propKind) : 
				new HashSet<TypeDescriptor>();

			// Diego TO-DO, different treatment for adding and removal
			if (propKind == PropagationKind.ADD_TYPES && types.Count() == 0 && returnVariable != null)
			{
				var instTypes = new HashSet<TypeDescriptor>();

				foreach (var iType in this.MethodEntity.InstantiatedTypes)
				{
					var isSubtype = await codeProvider.IsSubtypeAsync(iType,returnVariable.Type);
					
					if (isSubtype)
					{
						instTypes.Add(iType);
					}
				}

				//instTypes.UnionWith(
				//	this.MethodEntity.InstantiatedTypes
				//		.Where(iType => codeProvider.IsSubtypeAsync(iType,returnVariable.Type).Result));
                ////    .Where(iType => iType.IsSubtype(returnVariable.Type)));
				foreach (var t in instTypes)
				{
					types.Add(t);
				}
			}

			// Jump to caller
			var destination = EntityFactory.Create(caller,this.dispatcher);
			var retMessageInfo = new ReturnMessageInfo(
				lhs,
				types,
				propKind, this.MethodEntity.InstantiatedTypes,
				context.Invocation);
			var returnMessage = new ReturnMessage(this.MethodEntity.EntityDescriptor, retMessageInfo);
			//if (lhs != null)
			{
				await this.SendMessageAsync(destination, returnMessage);
			}
			//else
			//{
			//    return new Task(() => { });
			//}
		}
		private async Task HandleCallEventAsync(CallMessageInfo callMessage)
		{
            if (MethodEntity.CanBeAnalized)
            {
                Contract.Assert(callMessage.ArgumentValues.Count() == this.MethodEntity.ParameterNodes.Count());

                if (this.Verbose)
                {
                    Logger.Instance.Log("MethodEntityProcessor", "HandleCallEventAsync", "Reached {0} via call {1}", this.MethodEntity.MethodDescriptor, callMessage);
                }
                // This is the node in the caller where info of ret-value should go
                var lhs = callMessage.LHS;
                // Save caller info
                var callContext = new CallContext(callMessage.Caller, callMessage.LHS, callMessage.CallNode);
                /// Loop detected due to recursion
                //if (MethodEntity.NodesProcessing.Contains(callMessage.CallNode))
                //if (MethodEntity.NodesProcessing.Contains(callContext))
                //{
                //    if (this.Verbose)
                //    {
                //        Logger.Instance.Log(string.Format("Recursion loop {0} ", this.Method.ToString()));
                //    }
                //    //lock (this.MethodEntity)
                //    //{
                //    //    MethodEntity.NodesProcessing.Remove(callContext);
                //    //    //MethodEntity.NodesProcessing.Remove(callMessage.CallNode);
                //    //}
                //    //EndOfPropagationEventAsync(callMessage.PropagationKind);
                //    return new Task(() => { });
                //}

                // Just a test to try to block the Entity to a single simultaneous caller 
                //while (this.MethodEntity.CurrentContext != null)
                //{
                //    Logger.Instance.Log(string.Format("Waiting: {0} to finish", this.Method));
                //    Thread.Sleep(10);
                //}

                //lock (this.MethodEntity)
                {
                    //this.MethodEntity.CurrentContext = callContext;

                    // Here is when I register the caller
                    this.MethodEntity.AddToCallers(callContext);

                    if (this.MethodEntity.ThisRef != null)
                    {
                        await this.MethodEntity.PropGraph.DiffPropAsync(Demarshaler.Demarshal(callMessage.Receivers), this.MethodEntity.ThisRef, callMessage.PropagationKind);
                    }
                    var pairIterator = new PairIterator<PropGraphNodeDescriptor, ISet<TypeDescriptor>>
                        (this.MethodEntity.ParameterNodes, callMessage.ArgumentValues);
                    foreach (var pair in pairIterator)
                    {
                        var parameterNode = pair.Item1;
                        //PropGraph.Add(pn, argumentValues[i]);
                        if (parameterNode != null)
                        {
                            await this.MethodEntity.PropGraph.DiffPropAsync(
                                Demarshaler.Demarshal(pair.Item2),
                                parameterNode, callMessage.PropagationKind);
                        }
                    }
                }

                switch (callMessage.PropagationKind)
                {
                    case PropagationKind.ADD_TYPES:
                        await PropagateAsync();
                        break;
                    case PropagationKind.REMOVE_TYPES:
                        await PropagateDeleteAsync();
                        break;
                }
            }
            else 
            {
                await EndOfPropagationEventAsync(callMessage.PropagationKind,false);
            }
		}
        private void HandleCallEvent(CallMessageInfo callMessage)
        {
            if (MethodEntity.CanBeAnalized)
            {
                Contract.Assert(callMessage.ArgumentValues.Count() == this.MethodEntity.ParameterNodes.Count());
                if (this.Verbose)
                {
                    Logger.Instance.Log("MethodEntityProcessor", "HandleCallEvent", "Reached {0} via call", this.MethodEntity.MethodDescriptor);
                }

                // This tries to check that the invocation is repeated (didn't work: need to check)
                //if (MethodEntity.Callers.Where(cs => cs.Invocation.Equals(callMessage.CallNode)).Count()>0)
                // This check if the method is already in caller list
                if (MethodEntity.Callers.Where(cs => cs.Caller.Equals(callMessage.Caller)).Count() > 0)
                {
                    if (this.Verbose)
                    {
                        Logger.Instance.Log("MethodEntityProcessor", "HandleCallEvent", "Recursion loop {0} ", this.MethodEntity.MethodDescriptor);
                    }
                    EndOfPropagationEvent(callMessage.PropagationKind, false);
                    return;
                }

                // Save caller info
                var context = new CallContext(callMessage.Caller, callMessage.LHS, callMessage.CallNode);
                this.MethodEntity.AddToCallers(context);

                // Propagate type info in method
                //PropGraph.Add(thisRef, receivers);
                if (this.MethodEntity.ThisRef != null)
                {
                    this.MethodEntity.PropGraph.DiffProp(Demarshaler.Demarshal(callMessage.Receivers), this.MethodEntity.ThisRef, callMessage.PropagationKind);
                }

                var pairEnumerable = new PairIterator<PropGraphNodeDescriptor, ISet<TypeDescriptor>>(
                    this.MethodEntity.ParameterNodes, callMessage.ArgumentValues);

                foreach (var pair in pairEnumerable)
                {
                    var parameterNode = pair.Item1;
                    //PropGraph.Add(pn, argumentValues[i]);
                    if (parameterNode != null)
                    {
                        this.MethodEntity.PropGraph.DiffProp(
                            Demarshaler.Demarshal(pair.Item2),
                            parameterNode, callMessage.PropagationKind);
                    }
                }

                if (callMessage.PropagationKind == PropagationKind.ADD_TYPES)
                {
                    Propagate();
                }
                if (callMessage.PropagationKind == PropagationKind.REMOVE_TYPES)
                {
                    PropagateDelete();
                }
            }
            else
            {
                EndOfPropagationEvent(callMessage.PropagationKind, false);
            }
        }