public void PickAllOneByOne()
        {
            var list   = new List <int>(new int[] { 50, 51, 52, 53, 54 });
            var picker = new ListElementPicker <int>(list);

            Assert.AreEqual(50, picker.Current);

            Assert.AreEqual(50, picker.Pick());
            Assert.AreEqual(51, picker.Current);
            Assert.AreEqual(1, picker.CurrentIndex);
            Assert.AreEqual(0, picker.CurrentIndexOfUnpicked);
            Assert.IsTrue(picker.IsCurrentUnpicked());

            Assert.AreEqual(51, picker.Pick());
            Assert.AreEqual(52, picker.Current);
            Assert.AreEqual(2, picker.CurrentIndex);
            Assert.AreEqual(0, picker.CurrentIndexOfUnpicked);
            Assert.IsTrue(picker.IsCurrentUnpicked());

            Assert.AreEqual(52, picker.Pick());
            Assert.AreEqual(53, picker.Current);
            Assert.AreEqual(3, picker.CurrentIndex);
            Assert.AreEqual(0, picker.CurrentIndexOfUnpicked);
            Assert.IsTrue(picker.IsCurrentUnpicked());

            Assert.AreEqual(53, picker.Pick());
            Assert.AreEqual(54, picker.Current);
            Assert.AreEqual(4, picker.CurrentIndex);
            Assert.AreEqual(0, picker.CurrentIndexOfUnpicked);
            Assert.IsTrue(picker.IsCurrentUnpicked());

            Assert.AreEqual(54, picker.Pick());
            Assert.AreEqual(-1, picker.CurrentIndex);
            Assert.AreEqual(-1, picker.CurrentIndexOfUnpicked);
            Assert.IsFalse(picker.IsCurrentUnpicked());
        }
        internal static void InsertServiceInList(List <IService> list, IService service, bool forceReplace)
        {
            var allServices = new List <IService>(list);

            if (allServices.Select(s => s.ServiceType).Contains(service.ServiceType))
            {
                // Service type already registered. So what now?
                int index = allServices.FindIndex(s => s.ServiceType == service.ServiceType);
                if (Object.ReferenceEquals(allServices[index], service))
                {
                    throw new Exception("Service already registered.");
                }
                if (forceReplace)
                {
                    allServices[index] = service;
                }
                else
                {
                    throw new Exception($"Service of the same service type already registered ({allServices[index].ServiceObject.GetType().FullName}).");
                }
            }
            else
            {
                allServices.Add(service);
            }
            var servicePicker = new ListElementPicker <IService>(allServices);
            var newList       = new List <IService>();

            int lastCount = -1;

            while (newList.Count != lastCount)
            {
                lastCount = newList.Count;
                foreach (var s in servicePicker.ListUnpicked())
                {
                    if (s.Dependencies.Count() == 0)
                    {
                        newList.Add(servicePicker.Pick());
                    }
                    else
                    {
                        var allDepsFound = true;
                        foreach (var d in s.Dependencies)
                        {
                            if (!newList.Exists(x => x.ServiceType == d))
                            {
                                allDepsFound = false;
                                break;
                            }
                        }
                        if (allDepsFound)
                        {
                            newList.Add(servicePicker.Pick());
                        }
                    }
                }
            }

            newList.AddRange(servicePicker.ListUnpicked());     // Insert those services with unresolved dependencies
            list.Clear();
            list.AddRange(newList);
        }
        internal int CheckMethodArguments(
            ref MethodInfo method,
            bool isProcedure, bool firstParIsThis,
            Expression instance,
            Expression contextReference,
            Expression extensionInstance,
            List <SBExpressionData> arguments,
            List <SBExpressionData> suggestedAssignmentsOut,
            PropertyBlock propertyBlock = null)
        {
            ListElementPicker <SBExpressionData> argPicker = new ListElementPicker <SBExpressionData>(arguments);
            ArgumentInsertState state = ArgumentInsertState.Initial;
            bool thisParameterHandled = false;
            int  matchScore           = 1000;

            if (isProcedure)
            {
                state = ArgumentInsertState.ProcedureContext;
            }
            else if (extensionInstance != null)
            {
                state = ArgumentInsertState.ExtensionInstance;
            }

            // Construct method if generic and extension method.
            if (method.IsGenericMethodDefinition && extensionInstance != null)
            {
                // NOTE: This has to be improved with checks to avoid mistakes.
                var t = extensionInstance.Type;
                if (extensionInstance.Type.IsConstructedGenericType)
                {
                    t = t.GetGenericArguments()[0];
                }
                method = method.MakeGenericMethod(t);
            }

            var parameters = method.GetParameters();

            // Run through all the parameters; all must be assigned.
            for (int i = 0; i < parameters.Length; i++)
            {
                var p = parameters[i];
                if (state == ArgumentInsertState.ExtensionInstance)
                {
                    suggestedAssignmentsOut.Add(new SBExpressionData(extensionInstance));
                    state = ArgumentInsertState.Initial;
                    continue;
                }
                else if (state == ArgumentInsertState.ProcedureContext)
                {
                    suggestedAssignmentsOut.Add(new SBExpressionData(contextReference));
                    state = ArgumentInsertState.Initial;
                    continue;
                }
                else if (state == ArgumentInsertState.Initial)
                {
                    if (ImplicitAttribute.IsImplicit(p))
                    {
                        if (p.ParameterType.IsAssignableFrom(typeof(ICallContext)))
                        {
                            if (isProcedure)
                            {
                                suggestedAssignmentsOut.Add(new SBExpressionData(m_currentProcedure.ContextReferenceInternal));
                            }
                            else
                            {
                                suggestedAssignmentsOut.Add(new SBExpressionData(m_currentProcedure.ContextReference));
                            }
                            continue;
                        }
                        else
                        {
                            // TODO: Parsing error.
                            throw new NotImplementedException();
                        }
                    }
                    else if (!thisParameterHandled && firstParIsThis)
                    {
                        state = ArgumentInsertState.ThisReference;
                    }
                    else
                    {
                        state = ArgumentInsertState.Mandatory;
                    }
                }

                if (state != ArgumentInsertState.ThisReference)
                {
                    if (argPicker.UnpickedCount == 0 && state != ArgumentInsertState.ThisReference)
                    {
                        state = ArgumentInsertState.NoArguments;
                    }
                    else if (p.IsDefined(typeof(ParamArrayAttribute)))
                    {
                        state = ArgumentInsertState.Params;
                    }
                }

                if (state == ArgumentInsertState.Mandatory)
                {
                    if (p.GetType() == typeof(ArgumentList))
                    {
                        state = ArgumentInsertState.ArgumentList;
                    }
                    else
                    {
                        if (argPicker.Current.IsNamed)
                        {
                            if (argPicker.AllBeforeCurrentArePickedAndOthersUnpicked())
                            {
                                state = ArgumentInsertState.Named;
                            }
                            else
                            {
                                // TODO: report error; unexpected that args before current are not picked.
                                return(0);
                            }
                        }
                        else if (IsParameterAssignableFromArgument(p, argPicker.Current))
                        {
                            var a = argPicker.Pick();
                            if (p.ParameterType == typeof(object))
                            {
                                a           = a.NewExpressionCode(Expression.Convert(a.ExpressionCode, typeof(object)));
                                matchScore -= 10;    // Matching an object parameter is not as good as matching the exact same type.
                            }
                            suggestedAssignmentsOut.Add(a);
                            continue;   // next parameter
                        }
                        else if (argPicker.Current.DataType.Type == typeof(Int64) && p.ParameterType.IsPrimitiveNarrowableIntType())
                        {
                            suggestedAssignmentsOut.Add(new SBExpressionData(Expression.Convert(argPicker.Pick().ExpressionCode, p.ParameterType)));
                            continue;   // next parameter
                        }
                        else if (argPicker.Current.DataType.Type == typeof(Double) && p.ParameterType == typeof(Single))
                        {
                            suggestedAssignmentsOut.Add(new SBExpressionData(Expression.Convert(argPicker.Pick().ExpressionCode, p.ParameterType)));
                            matchScore -= 5; // Matching a 'single' is not as good as matching the exact same type.
                            continue;        // next parameter
                        }
                        else
                        {
                            state = ArgumentInsertState.Named;  // Try finding the argument by name, then.
                        }
                    }
                }

                if (state == ArgumentInsertState.ThisReference)
                {
                    thisParameterHandled = true;
                    if (p.ParameterType.IsAssignableFrom(instance.Type))
                    {
                        suggestedAssignmentsOut.Add(new SBExpressionData(instance));
                        state = ArgumentInsertState.Mandatory;  // Not sure about this...
                    }
                    else
                    {
                        return(0);   // Wrong type of 'this' reference
                    }
                }

                if (state == ArgumentInsertState.Named)
                {
                    // Note: Just because the call uses named arguments does not mean that the parameters have default values.

                    if (argPicker.FindUnpicked(a => a.ParameterName == p.Name))
                    {
                        suggestedAssignmentsOut.Add(argPicker.Pick());
                        continue;   // next parameter
                    }
                    else
                    {
                        if (p.HasDefaultValue)
                        {
                            suggestedAssignmentsOut.Add(
                                new SBExpressionData(
                                    Expression.Constant(p.DefaultValue, p.ParameterType)));
                            continue;
                        }
                        else
                        {
                            // TODO: Report argument not found, maybe
                            return(0);
                        }
                    }
                }

                if (state == ArgumentInsertState.Params)
                {
                    if (!p.ParameterType.IsArray)
                    {
                        throw new Exception("Unexpected type of 'params' parameter; not an array.");
                    }
                    var t = p.ParameterType.GetElementType();

                    if (!argPicker.AllBeforeCurrentArePickedAndOthersUnpicked())
                    {
                        // TODO: report error, maybe
                        return(0);
                    }
                    if (argPicker.UnpickedCount == 1 && p.ParameterType.IsAssignableFrom(argPicker.Current.DataType.Type))
                    {
                        suggestedAssignmentsOut.Add(argPicker.Pick());
                        continue;
                    }
                    else
                    {
                        var paramsArgs = new List <Expression>();
                        while (argPicker.UnpickedCount > 0)
                        {
                            if (t == argPicker.Current.DataType.Type)
                            {
                                paramsArgs.Add(argPicker.Pick().ExpressionCode);
                            }
                            else if (t.IsAssignableFrom(argPicker.Current.DataType.Type))
                            {
                                paramsArgs.Add(Expression.Convert(argPicker.Pick().ExpressionCode, t));
                            }
                            else
                            {
                                // TODO: report error, maybe
                                return(0);
                            }
                        }
                        suggestedAssignmentsOut.Add(new SBExpressionData(Expression.NewArrayInit(t, paramsArgs)));
                    }
                    continue;
                }

                if (state == ArgumentInsertState.NoArguments)
                {
                    if (p.IsDefined(typeof(ParamArrayAttribute)))
                    {
                        if (!p.ParameterType.IsArray)
                        {
                            throw new Exception("Unexpected type of 'params' parameter; not an array.");
                        }
                        var t = p.ParameterType.GetElementType();
                        suggestedAssignmentsOut.Add(new SBExpressionData(Expression.NewArrayInit(t)));
                    }
                    else
                    {
                        if (p.HasDefaultValue)
                        {
                            object c = p.DefaultValue;
                            if (c == null && p.ParameterType.IsValueType)
                            {
                                c = Activator.CreateInstance(p.ParameterType);
                            }
                            suggestedAssignmentsOut.Add(
                                new SBExpressionData(
                                    Expression.Constant(c, p.ParameterType)));
                            continue;
                        }
                        else
                        {
                            // TODO: Report argument not found, maybe
                            return(0);
                        }
                    }
                }
            }

            if (suggestedAssignmentsOut.Count >= parameters.Length)
            {
                if (argPicker.PickedCount < arguments.Count)
                {
                    throw new Exception("Not all passed arguments are used!");                                            // TODO: report in a better way.
                }
                return(matchScore);
            }
            return(0);
        }
        public void PickAndSkip()
        {
            var list   = new List <int>(new int[] { 50, 51, 52, 53, 54 });
            var picker = new ListElementPicker <int>(list);

            Assert.AreEqual(50, picker.Current);

            Assert.IsTrue(picker.SkipToNextUnpicked()); // Don't pick, just skip to next.
            Assert.AreEqual(51, picker.Current);
            Assert.AreEqual(1, picker.CurrentIndex);
            Assert.AreEqual(1, picker.CurrentIndexOfUnpicked);
            Assert.IsTrue(picker.IsCurrentUnpicked());

            Assert.AreEqual(51, picker.Pick());
            Assert.AreEqual(52, picker.Current);
            Assert.AreEqual(2, picker.CurrentIndex);
            Assert.AreEqual(1, picker.CurrentIndexOfUnpicked);
            Assert.IsTrue(picker.IsCurrentUnpicked());

            Assert.IsTrue(picker.SkipToNextUnpicked()); // Don't pick, just skip to next.
            Assert.AreEqual(53, picker.Current);
            Assert.AreEqual(3, picker.CurrentIndex);
            Assert.AreEqual(2, picker.CurrentIndexOfUnpicked);
            Assert.IsTrue(picker.IsCurrentUnpicked());

            Assert.AreEqual(53, picker.Pick());
            Assert.AreEqual(54, picker.Current);
            Assert.AreEqual(4, picker.CurrentIndex);
            Assert.AreEqual(2, picker.CurrentIndexOfUnpicked);
            Assert.IsTrue(picker.IsCurrentUnpicked());

            Assert.IsTrue(picker.SelectFirstUnpicked());
            Assert.AreEqual(50, picker.Current);
            Assert.AreEqual(0, picker.CurrentIndex);
            Assert.AreEqual(0, picker.CurrentIndexOfUnpicked);
            Assert.IsTrue(picker.IsCurrentUnpicked());

            Assert.AreEqual(50, picker.Pick());
            Assert.AreEqual(52, picker.Current);
            Assert.AreEqual(2, picker.CurrentIndex);
            Assert.AreEqual(0, picker.CurrentIndexOfUnpicked);
            Assert.IsTrue(picker.IsCurrentUnpicked());

            Assert.AreEqual(52, picker.Pick());
            Assert.AreEqual(54, picker.Current);
            Assert.AreEqual(4, picker.CurrentIndex);
            Assert.AreEqual(0, picker.CurrentIndexOfUnpicked);
            Assert.IsTrue(picker.IsCurrentUnpicked());

            Assert.IsTrue(picker.SelectFirstUnpicked());
            Assert.AreEqual(54, picker.Current);
            Assert.AreEqual(4, picker.CurrentIndex);
            Assert.AreEqual(0, picker.CurrentIndexOfUnpicked);
            Assert.IsTrue(picker.IsCurrentUnpicked());

            Assert.AreEqual(54, picker.Pick());     // Pick last one
            Assert.AreEqual(-1, picker.CurrentIndex);
            Assert.AreEqual(-1, picker.CurrentIndexOfUnpicked);
            Assert.IsFalse(picker.IsCurrentUnpicked());

            // All picked now.
            Assert.IsFalse(picker.SelectFirstUnpicked());
            Assert.AreEqual(-1, picker.CurrentIndex);
            Assert.AreEqual(-1, picker.CurrentIndexOfUnpicked);
            Assert.IsFalse(picker.IsCurrentUnpicked());
        }