/// <summary>
        /// Validates the passed in state with the LayoutEditorPopulation, note that the SampleTypes are as specified in the LayoutEditorPopulation
        /// </summary>
        /// <param name="state"></param>
        /// <param name="layoutEditorPopulation"></param>
        /// <returns></returns>
        public static List<ValidationError> Validate(SingleLayoutEditor state, LayoutEditorPopulation layoutEditorPopulation)
        {
            List<ValidationError> errors = new List<ValidationError>();

            // Check the state is setup correctly
            Debug.Assert(state.NumPositions == state.Width * state.Height);
            Debug.Assert(state.NumPositions == state.LayoutPositions.Count);

            if ((layoutEditorPopulation.Width != state.Width) || (layoutEditorPopulation.Height != state.Height))
            {
                errors.Add(new ValidationError()
                {
                    Type = ValidationError.ErrorType.InvalidLayout,
                    Message = string.Format("The layout dimensions are not correct, they should be {0}x{1}. ", layoutEditorPopulation.Width, layoutEditorPopulation.Height)
                });
                return errors;
            }

            try
            {
                var mergedTypesSingle = layoutEditorPopulation.GetMergedTypesSingle();

                LayoutAnalysis layoutAnalysis = new LayoutAnalysis(state.LayoutPositions, layoutEditorPopulation.SampleTypes,
                    true,   // groupNumberingMustStartFromOne - for types that are NOT merged
                    mergedTypesSingle);

                foreach (Rule rule in layoutEditorPopulation.Rules)
                {
                    TestRule(rule, layoutAnalysis, errors);
                }
            }
            catch (Layout.LayoutAnalysis.InvalidLayoutException invalidLayoutException)
            {
                // Convert the InvalidLayoutException into a message.
                errors.Add(new ValidationError()
                {
                    Type = ValidationError.ErrorType.InvalidLayout,
                    Message = invalidLayoutException.Message
                });
            }

            return errors;
        }
        public static void TestRule(Rule rule, LayoutAnalysis layoutAnalysis, List<ValidationError> errors)
        {
            #region Group Count Tests
            int numGroupsOfType = layoutAnalysis.GetNumGroupsOfType(rule.TypeId);

            // If the rule specifies that both max and min are equal and NumGroups is not set then set NumGroups accordingly.
            if ((rule.MaxNumGroups == rule.MinNumGroups) && (rule.NumGroups == 0))
            {
                rule.NumGroups = rule.MaxNumGroups;
            }

            if (rule.NumGroups != 0)
            {
                if (rule.NumGroups != numGroupsOfType)
                {
                    errors.Add(CreateValidationErrorRule(rule,
                                string.Format("There should be {0} {1} group(s), there are {2} {1} group(s). ",
                                rule.NumGroups, layoutAnalysis.GetSampleTypeNameFromTypeId(rule.TypeId), numGroupsOfType)));
                }
            }
            else
            {
                if ((rule.MinNumGroups != 0) && (numGroupsOfType < rule.MinNumGroups))
                {
                    errors.Add(CreateValidationErrorRule(rule,
                                string.Format("There should be at least {0} {1} group(s), there are {2} {1} group(s). ",
                                rule.MinNumGroups, layoutAnalysis.GetSampleTypeNameFromTypeId(rule.TypeId), numGroupsOfType)));
                }

                if ((rule.MaxNumGroups != 0) && (numGroupsOfType > rule.MaxNumGroups))
                {
                    errors.Add(CreateValidationErrorRule(rule,
                                string.Format("There should be no more than {0} {1} group(s), there are {2} {1} group(s). ",
                                rule.MaxNumGroups, layoutAnalysis.GetSampleTypeNameFromTypeId(rule.TypeId), numGroupsOfType)));
                }
            }
            #endregion

            #region Replicate Count Tests
            // The replicate rules are only applied if there are actually groups of this type.
            if (numGroupsOfType > 0)
            {
                // If the rule specifies that both max and min are equal and NumReplicates is not set then set NumReplicates accordingly.
                if ((rule.MaxNumReplicates == rule.MinNumReplicates) && (rule.NumReplicates == 0))
                {
                    rule.NumReplicates = rule.MaxNumReplicates;
                }

                bool replicatesConsistent = layoutAnalysis.GetReplicatesConsistent(rule.TypeId);

                if ((rule.AllGroupsSameReplicates) && (!replicatesConsistent))
                {
                    errors.Add(CreateValidationErrorRule(rule,
                                string.Format("All {0} groups should have the same number of replicates, this is not the case. ",
                                layoutAnalysis.GetSampleTypeNameFromTypeId(rule.TypeId))));
                }

                if (rule.NumReplicates != 0)
                {
                    // If a specific number of replicates has been specified but there are different replicates in each group then
                    if (!replicatesConsistent)
                    {
                        int firstGroupWithWrongReplicates = layoutAnalysis.GetFirstGroupWhichDoesNotHaveSpecifiedReplicates(rule.TypeId, rule.NumReplicates);
                        int wrongReplicates = layoutAnalysis.GetNumReplicates(rule.TypeId, firstGroupWithWrongReplicates);
                        errors.Add(CreateValidationErrorRule(rule,
                                    string.Format("All {1} groups should have {0} replicates, {2} has {3} replicate(s). ",
                                        rule.NumReplicates,
                                        layoutAnalysis.GetSampleTypeNameFromTypeId(rule.TypeId),
                                        layoutAnalysis.GetGroupNameForDisplay(rule.TypeId, firstGroupWithWrongReplicates),
                                        wrongReplicates)));
                    }
                    else
                    {
                        // All groups are replicate consistent, so just get the first group to check
                        int numReplicates = layoutAnalysis.GetNumReplicates(rule.TypeId, 1);

                        if (rule.NumReplicates != numReplicates)
                        {
                            errors.Add(CreateValidationErrorRule(rule,
                                        string.Format("There should be {0} replicate(s) in each {1} group, each {1} group has {2} replicate(s). ",
                                        rule.NumReplicates, layoutAnalysis.GetSampleTypeNameFromTypeId(rule.TypeId), numReplicates)));
                        }
                    }
                }
                else
                {
                    // If the replicates on the layout are inconsistent AND this is allowed then (with AllGroupsSameReplicates="false")
                    // check that each group follows the min/max rules
                    if (!replicatesConsistent)
                    {
                        if (!rule.AllGroupsSameReplicates)
                        {
                            // Go through each group and check that it matches the min/max rules
                            for (int index = 0; index < numGroupsOfType; index++)
                            {
                                int numReplicates = layoutAnalysis.GetNumReplicatesZeroIndex(rule.TypeId, index);

                                if ((rule.MinNumReplicates != 0) && (numReplicates < rule.MinNumReplicates))
                                {
                                    errors.Add(CreateValidationErrorRule(rule,
                                                string.Format("There should be at least {0} replicate(s) in each {1} group, {3} has {2} replicate(s). ",
                                                    rule.MinNumReplicates,
                                                    layoutAnalysis.GetSampleTypeNameFromTypeId(rule.TypeId),
                                                    numReplicates,
                                                    layoutAnalysis.GetGroupNameForDisplay(rule.TypeId, index + layoutAnalysis.GetLowestGroupNum(rule.TypeId)))));
                                }

                                if ((rule.MaxNumReplicates != 0) && (numReplicates > rule.MaxNumReplicates))
                                {
                                    errors.Add(CreateValidationErrorRule(rule,
                                                string.Format("There should be no more than {0} replicate(s) in each {1} group, {3} has {2} replicate(s). ",
                                                    rule.MaxNumReplicates,
                                                    layoutAnalysis.GetSampleTypeNameFromTypeId(rule.TypeId),
                                                    numReplicates,
                                                    layoutAnalysis.GetGroupNameForDisplay(rule.TypeId, index + layoutAnalysis.GetLowestGroupNum(rule.TypeId)))));
                                }
                            }
                        }
                    }
                    else
                    {
                        // Only get and check replicates if this rule requires it:
                        if ((rule.MinNumReplicates != 0) || (rule.MaxNumReplicates != 0))
                        {
                            // The replicates on the layout are consistent
                            // In this case it does not matter whether AllGroupsSameReplicates is true or false
                            // All groups are replicate consistent, so just get the first group to check
                            int numReplicates = layoutAnalysis.GetNumReplicates(rule.TypeId, 1);

                            if ((rule.MinNumReplicates != 0) && (numReplicates < rule.MinNumReplicates))
                            {
                                errors.Add(CreateValidationErrorRule(rule,
                                            string.Format("There should be at least {0} replicate(s) in each {1} group, there are {2} replicates in all {1} group(s). ",
                                            rule.MinNumReplicates, layoutAnalysis.GetSampleTypeNameFromTypeId(rule.TypeId), numReplicates)));
                            }

                            if ((rule.MaxNumReplicates != 0) && (numReplicates > rule.MaxNumReplicates))
                            {
                                errors.Add(CreateValidationErrorRule(rule,
                                            string.Format("There should be no more than {0} replicate(s) in each {1} group, there are {2} replicates in all {1} group(s). ",
                                            rule.MaxNumReplicates, layoutAnalysis.GetSampleTypeNameFromTypeId(rule.TypeId), numReplicates)));
                            }
                        }
                    }
                }
            }
            #endregion

            if (rule.MatchGroupsInTypeId != 0)
            {
                int numGroupsOfMatchedType = layoutAnalysis.GetNumGroupsOfType(rule.MatchGroupsInTypeId);

                if (numGroupsOfMatchedType != numGroupsOfType)
                {
                    errors.Add(CreateValidationErrorRule(rule,
                                string.Format("The number of {0} groups ({1}) should equal the number of {2} groups ({3}). ",
                                layoutAnalysis.GetSampleTypeNameFromTypeId(rule.TypeId), numGroupsOfType,
                                layoutAnalysis.GetSampleTypeNameFromTypeId(rule.MatchGroupsInTypeId), numGroupsOfMatchedType
                                )));

                }
            }
        }
        private LayoutAnalysis SetupLayoutAnalysisForTestRule(string xmlUserLayout, int width, int height)
        {
            UserLayout userLayout = XmlHelpers.DeserializeXmlString(xmlUserLayout, typeof(UserLayout)) as UserLayout;
            SingleLayoutEditor sle = new SingleLayoutEditor(userLayout.SingleLayoutLight, width, height);

            // Create an LPE from the LayoutEditorPopulation template string - note this has no rules
            // and the dimensions are not setup.  However it should contain all SampleTypes used in all tests
            LayoutEditorPopulation lpe = CreateLayoutEditorPopulation(xmlStringsLayoutEditorPopulation.xmlAllTypesNoDimensionsOrRules);
            lpe.Width = width;
            lpe.Height = height;

            Debug.Assert(lpe.Rules.Count == 0);

            LayoutAnalysis la = new LayoutAnalysis(sle.LayoutPositions, lpe.SampleTypes, true);

            List<ValidationError> errors = LayoutValidation.Validate(sle, lpe);
            Assert.AreEqual(0, errors.Count, "The userlayout generated an InvalidLayout error");

            return new LayoutAnalysis(sle.LayoutPositions, lpe.SampleTypes, true);
        }
 private List<ValidationError> TestRule(LayoutAnalysis layoutAnalysis, string xmlRule)
 {
     Rule rule = CreateRule(xmlRule);
     List<ValidationError> errors = new List<ValidationError>();
     LayoutValidation.TestRule(rule, layoutAnalysis, errors);
     return errors;
 }
        public void TestMethod12x8Unused()
        {
            LayoutAnalysis layoutAnalysis;

            layoutAnalysis = new LayoutAnalysis(CreateLayout12x8Unused(), CreateSampleTypesUnused(), true);
            Assert.AreEqual(96, layoutAnalysis.GetNumWellsOfType(1));   // 96 unused wells

            layoutAnalysis = new LayoutAnalysis(CreateLayout12x8Unused(), CreateSampleTypesTypical(), true);
            Assert.AreEqual(96, layoutAnalysis.GetNumWellsOfType(1));   // 96 unused wells
            Assert.AreEqual(0, layoutAnalysis.GetNumWellsOfType(2));
            Assert.AreEqual(0, layoutAnalysis.GetNumWellsOfType(3));
            Assert.AreEqual(0, layoutAnalysis.GetNumWellsOfType(4));
            Assert.AreEqual(0, layoutAnalysis.GetNumWellsOfType(5));

            layoutAnalysis = new LayoutAnalysis(CreateLayout12x8Unused(), CreateSampleTypesUnusedStandardBlankUnknown(), true);
            Assert.AreEqual(96, layoutAnalysis.GetNumWellsOfType(1));   // 96 unused wells
            Assert.AreEqual(0, layoutAnalysis.GetNumWellsOfType(2));
            Assert.AreEqual(0, layoutAnalysis.GetNumWellsOfType(3));
            Assert.AreEqual(0, layoutAnalysis.GetNumWellsOfType(5));
        }