public static bool SetupRequired(PrinterConfig printer)
 {
     return(LevelingValidation.NeedsToBeRun(printer) ||          // PrintLevelingWizard
            ProbeCalibrationWizard.NeedsToBeRun(printer) ||
            FilamentSetupWizard.SetupRequired(printer));
 }
        /// <summary>
        /// Validates the printer settings satisfy all requirements
        /// </summary>
        /// <param name="printer">The printer to validate</param>
        /// <returns>A list of all warnings and errors</returns>
        public static List <ValidationError> ValidateSettings(this PrinterConfig printer, SettingsContext settings = null)
        {
            if (settings == null)
            {
                settings = new SettingsContext(printer, null, NamedSettingsLayers.All);
            }

            var errors = new List <ValidationError>();

            var extruderCount = settings.GetValue <int>(SettingsKey.extruder_count);

            // Check to see if supports are required
            var supportGenerator = new SupportGenerator(printer.Bed.Scene, .05);

            if (supportGenerator.RequiresSupport())
            {
                errors.Add(new ValidationError("UnsupportedParts")
                {
                    Error      = "Possible Unsupported Parts Detected".Localize(),
                    Details    = "Some parts may require support structures to print correctly".Localize(),
                    ErrorLevel = ValidationErrorLevel.Warning,
                    FixAction  = new NamedAction()
                    {
                        Title  = "Generate Supports".Localize(),
                        Action = () =>
                        {
                            // Find and InvokeClick on the Generate Supports toolbar button
                            var sharedParent = ApplicationController.Instance.DragDropData.View3DWidget.Parents <GuiWidget>().FirstOrDefault(w => w.Name == "View3DContainerParent");
                            if (sharedParent != null)
                            {
                                var supportsPopup = sharedParent.FindDescendant("Support SplitButton");
                                supportsPopup.InvokeClick();
                            }
                        }
                    }
                });
            }

            if (FilamentSetupWizard.SetupRequired(printer))
            {
                errors.Add(new ValidationError("FilamentSetup")
                {
                    Error      = "Unknown filament loaded".Localize(),
                    Details    = "Set active material to continue".Localize(),
                    ErrorLevel = ValidationErrorLevel.Warning,
                    FixAction  = new NamedAction()
                    {
                        Title  = "Load Filament".Localize(),
                        Action = () =>
                        {
                            UiThread.RunOnIdle(() =>
                            {
                                DialogWindow.Show(
                                    new FilamentSetupWizard(printer, AppContext.Theme),
                                    advanceToIncompleteStage: true);
                            });
                        }
                    }
                });
            }

            try
            {
                if (settings.GetValue <bool>(SettingsKey.validate_layer_height))
                {
                    if (settings.GetValue <double>(SettingsKey.layer_height) > settings.GetValue <double>(SettingsKey.nozzle_diameter))
                    {
                        errors.Add(
                            new SettingsValidationError(SettingsKey.layer_height)
                        {
                            Error = "{0} must be less than or equal to the {1}.".Localize().FormatWith(
                                GetSettingsName(SettingsKey.layer_height),
                                GetSettingsName(SettingsKey.nozzle_diameter)),
                            ValueDetails = "{0} = {1}\n{2} = {3}".FormatWith(
                                GetSettingsName(SettingsKey.layer_height),
                                settings.GetValue <double>(SettingsKey.layer_height),
                                GetSettingsName(SettingsKey.nozzle_diameter),
                                settings.GetValue <double>(SettingsKey.nozzle_diameter)),
                        });
                    }
                    else if (settings.GetValue <double>(SettingsKey.layer_height) <= 0)
                    {
                        errors.Add(
                            new SettingsValidationError(SettingsKey.layer_height)
                        {
                            Error = "{0} must be greater than 0.".Localize().FormatWith(GetSettingsName(SettingsKey.layer_height)),
                        });
                    }

                    if (settings.GetValue <double>(SettingsKey.first_layer_height) > settings.GetValue <double>(SettingsKey.nozzle_diameter))
                    {
                        errors.Add(
                            new SettingsValidationError(SettingsKey.first_layer_height)
                        {
                            Error = "{0} must be less than or equal to the {1}.".Localize().FormatWith(
                                GetSettingsName(SettingsKey.first_layer_height),
                                GetSettingsName(SettingsKey.nozzle_diameter)),
                            ValueDetails = "{0} = {1}\n{2} = {3}".FormatWith(
                                GetSettingsName(SettingsKey.first_layer_height),
                                settings.GetValue <double>(SettingsKey.first_layer_height),
                                GetSettingsName(SettingsKey.nozzle_diameter),
                                settings.GetValue <double>(SettingsKey.nozzle_diameter)),
                        });
                    }
                }

                string[] startGCode = settings.GetValue(SettingsKey.start_gcode).Replace("\\n", "\n").Split('\n');

                // Print recovery is incompatible with firmware leveling - ensure not enabled in startGCode
                if (settings.GetValue <bool>(SettingsKey.recover_is_enabled))
                {
                    // Ensure we don't have hardware leveling commands in the start gcode.
                    foreach (string startGCodeLine in startGCode)
                    {
                        if (startGCodeLine.StartsWith("G29"))
                        {
                            errors.Add(
                                new SettingsValidationError(SettingsKey.start_gcode)
                            {
                                Error   = "Start G-Code cannot contain G29 if Print Recovery is enabled.".Localize(),
                                Details = "Your Start G-Code should not contain a G29 if you are planning on using Print Recovery. Change your start G-Code or turn off Print Recovery.".Localize(),
                            });
                        }

                        if (startGCodeLine.StartsWith("G30"))
                        {
                            errors.Add(
                                new SettingsValidationError(SettingsKey.start_gcode)
                            {
                                Error   = "Start G-Code cannot contain G30 if Print Leveling is enabled.".Localize(),
                                Details = "Your Start G-Code should not contain a G30 if you are planning on using Print Recovery. Change your start G-Code or turn off Print Recovery.".Localize(),
                            });
                        }
                    }
                }

                var levelingEnabled  = printer.Settings.GetValue <bool>(SettingsKey.print_leveling_enabled);
                var levelingRequired = printer.Settings.GetValue <bool>(SettingsKey.print_leveling_required_to_print);

                if (levelingEnabled || levelingRequired)
                {
                    // Ensure we don't have hardware leveling commands in the start gcode.
                    foreach (string startGCodeLine in startGCode)
                    {
                        if (startGCodeLine.StartsWith("G29"))
                        {
                            errors.Add(
                                new SettingsValidationError(SettingsKey.start_gcode)
                            {
                                Error   = "Start G-Code cannot contain G29 if Print Leveling is enabled.".Localize(),
                                Details = "Your Start G-Code should not contain a G29 if you are planning on using print leveling. Change your start G-Code or turn off print leveling.".Localize(),
                            });
                        }

                        if (startGCodeLine.StartsWith("G30"))
                        {
                            errors.Add(
                                new SettingsValidationError(SettingsKey.start_gcode)
                            {
                                Error   = "Start G-Code cannot contain G30 if Print Leveling is enabled.".Localize(),
                                Details = "Your Start G-Code should not contain a G30 if you are planning on using print leveling. Change your start G-Code or turn off print leveling.".Localize(),
                            });
                        }
                    }

                    bool heatedBed = printer.Settings.GetValue <bool>(SettingsKey.has_heated_bed);

                    double bedTemperature = printer.Settings.GetValue <double>(SettingsKey.bed_temperature);

                    PrintLevelingData levelingData = printer.Settings.Helpers.PrintLevelingData;

                    if (heatedBed &&
                        !levelingData.IssuedLevelingTempWarning &&
                        Math.Abs(bedTemperature - levelingData.BedTemperature) > 10)
                    {
                        errors.Add(
                            new ValidationError("BedLevelingTemperature")
                        {
                            Error   = "Bed Leveling Temperature".Localize(),
                            Details = string.Format(
                                "Bed Leveling data created at {0}°C versus current {1}°C".Localize(),
                                levelingData.BedTemperature,
                                bedTemperature),
                            ErrorLevel = ValidationErrorLevel.Warning,
                            FixAction  = new NamedAction()
                            {
                                Title  = "Recalibrate",
                                Action = () =>
                                {
                                    UiThread.RunOnIdle(() =>
                                    {
                                        DialogWindow.Show(new PrintLevelingWizard(printer));
                                    });
                                },
                                IsEnabled = () => printer.Connection.IsConnected
                            }
                        });
                    }
                }

                // Make sure the z offsets are not too big
                if (Math.Abs(settings.GetValue <double>(SettingsKey.baby_step_z_offset)) > 2)
                {
                    // Static path generation for non-SliceSettings value
                    var location = "Location".Localize() + ":"
                                   + "\n" + "Controls".Localize()
                                   + "\n  • " + "Movement".Localize()
                                   + "\n    • " + "Z Offset".Localize();

                    errors.Add(
                        new ValidationError("ZOffset0TooLarge")
                    {
                        Error   = "Z Offset is too large.".Localize(),
                        Details = string.Format(
                            "{0}\n\n{1}",
                            "The Z Offset for your printer, sometimes called Baby Stepping, is greater than 2mm and invalid. Clear the value and re-level the bed.".Localize(),
                            location)
                    });
                }

                if (settings.GetValue <double>(SettingsKey.first_layer_extrusion_width) > settings.GetValue <double>(SettingsKey.nozzle_diameter) * 4)
                {
                    errors.Add(
                        new SettingsValidationError(SettingsKey.first_layer_extrusion_width)
                    {
                        Error = "{0} must be less than or equal to the {1} * 4.".Localize().FormatWith(
                            GetSettingsName(SettingsKey.first_layer_extrusion_width),
                            GetSettingsName(SettingsKey.nozzle_diameter)),
                        ValueDetails = "{0} = {1}\n{2} = {3}".FormatWith(
                            GetSettingsName(SettingsKey.first_layer_extrusion_width),
                            settings.GetValue <double>(SettingsKey.first_layer_extrusion_width),
                            GetSettingsName(SettingsKey.nozzle_diameter),
                            settings.GetValue <double>(SettingsKey.nozzle_diameter))
                    });
                }

                if (settings.GetValue <double>(SettingsKey.first_layer_extrusion_width) <= 0)
                {
                    errors.Add(
                        new SettingsValidationError(SettingsKey.first_layer_extrusion_width)
                    {
                        Error = "{0} must be greater than 0.".Localize().FormatWith(
                            GetSettingsName(SettingsKey.first_layer_extrusion_width)),
                        ValueDetails = "{0} = {1}".FormatWith(
                            GetSettingsName(SettingsKey.first_layer_extrusion_width),
                            settings.GetValue <double>(SettingsKey.first_layer_extrusion_width)),
                    });
                }

                if (settings.GetValue <double>(SettingsKey.external_perimeter_extrusion_width) > settings.GetValue <double>(SettingsKey.nozzle_diameter) * 4)
                {
                    errors.Add(
                        new SettingsValidationError(SettingsKey.external_perimeter_extrusion_width)
                    {
                        Error = "{0} must be less than or equal to the {1} * 4.".Localize().FormatWith(
                            GetSettingsName(SettingsKey.external_perimeter_extrusion_width),
                            GetSettingsName(SettingsKey.nozzle_diameter)),
                        ValueDetails = "{0} = {1}\n{2} = {3}".FormatWith(
                            GetSettingsName(SettingsKey.external_perimeter_extrusion_width),
                            settings.GetValue <double>(SettingsKey.external_perimeter_extrusion_width),
                            GetSettingsName(SettingsKey.nozzle_diameter),
                            settings.GetValue <double>(SettingsKey.nozzle_diameter)),
                    });
                }

                if (settings.GetValue <double>(SettingsKey.external_perimeter_extrusion_width) <= 0)
                {
                    errors.Add(
                        new SettingsValidationError(SettingsKey.external_perimeter_extrusion_width)
                    {
                        Error = "{0} must be greater than 0.".Localize().FormatWith(
                            GetSettingsName(SettingsKey.external_perimeter_extrusion_width)),
                        ValueDetails = "{0} = {1}".FormatWith(
                            GetSettingsName(SettingsKey.external_perimeter_extrusion_width),
                            settings.GetValue <double>(SettingsKey.external_perimeter_extrusion_width)),
                    });
                }

                if (settings.GetValue <double>(SettingsKey.min_fan_speed) > 100)
                {
                    errors.Add(
                        new SettingsValidationError(SettingsKey.min_fan_speed)
                    {
                        Error = "The {0} can only go as high as 100%.".Localize().FormatWith(
                            GetSettingsName(SettingsKey.min_fan_speed)),
                        ValueDetails = "It is currently set to {0}.".Localize().FormatWith(
                            settings.GetValue <double>(SettingsKey.min_fan_speed)),
                    });
                }

                if (settings.GetValue <double>(SettingsKey.max_fan_speed) > 100)
                {
                    errors.Add(
                        new SettingsValidationError(SettingsKey.max_fan_speed)
                    {
                        Error = "The {0} can only go as high as 100%.".Localize().FormatWith(
                            GetSettingsName(SettingsKey.max_fan_speed)),
                        ValueDetails = "It is currently set to {0}.".Localize().FormatWith(
                            settings.GetValue <double>(SettingsKey.max_fan_speed)),
                    });
                }

                if (extruderCount < 1)
                {
                    errors.Add(
                        new SettingsValidationError(SettingsKey.extruder_count)
                    {
                        Error = "The {0} must be at least 1.".Localize().FormatWith(
                            GetSettingsName(SettingsKey.extruder_count)),
                        ValueDetails = "It is currently set to {0}.".Localize().FormatWith(extruderCount),
                    });
                }

                if (settings.GetValue <double>(SettingsKey.fill_density) < 0 || settings.GetValue <double>(SettingsKey.fill_density) > 1)
                {
                    errors.Add(
                        new SettingsValidationError(SettingsKey.fill_density)
                    {
                        Error = "The {0} must be between 0 and 1.".Localize().FormatWith(
                            GetSettingsName(SettingsKey.fill_density)),
                        ValueDetails = "It is currently set to {0}.".Localize().FormatWith(
                            settings.GetValue <double>(SettingsKey.fill_density)),
                    });
                }

                // marlin firmware can only take a max of 128 bytes in a single instruction, make sure no lines are longer than that
                ValidateGCodeLinesShortEnough(SettingsKey.cancel_gcode, printer, errors);
                ValidateGCodeLinesShortEnough(SettingsKey.connect_gcode, printer, errors);
                ValidateGCodeLinesShortEnough(SettingsKey.end_gcode, printer, errors);
                ValidateGCodeLinesShortEnough(SettingsKey.layer_gcode, printer, errors);
                ValidateGCodeLinesShortEnough(SettingsKey.pause_gcode, printer, errors);
                ValidateGCodeLinesShortEnough(SettingsKey.resume_gcode, printer, errors);
                ValidateGCodeLinesShortEnough(SettingsKey.start_gcode, printer, errors);

                // If the given speed is part of the current slice engine then check that it is greater than 0.
                ValidateGoodSpeedSettingGreaterThan0(SettingsKey.bridge_speed, printer, errors);
                ValidateGoodSpeedSettingGreaterThan0(SettingsKey.air_gap_speed, printer, errors);
                ValidateGoodSpeedSettingGreaterThan0(SettingsKey.external_perimeter_speed, printer, errors);
                ValidateGoodSpeedSettingGreaterThan0(SettingsKey.first_layer_speed, printer, errors);
                ValidateGoodSpeedSettingGreaterThan0(SettingsKey.infill_speed, printer, errors);
                ValidateGoodSpeedSettingGreaterThan0(SettingsKey.perimeter_speed, printer, errors);
                ValidateGoodSpeedSettingGreaterThan0(SettingsKey.small_perimeter_speed, printer, errors);
                ValidateGoodSpeedSettingGreaterThan0(SettingsKey.solid_infill_speed, printer, errors);
                ValidateGoodSpeedSettingGreaterThan0(SettingsKey.support_material_speed, printer, errors);
                ValidateGoodSpeedSettingGreaterThan0(SettingsKey.top_solid_infill_speed, printer, errors);
                ValidateGoodSpeedSettingGreaterThan0(SettingsKey.travel_speed, printer, errors);
                ValidateGoodSpeedSettingGreaterThan0(SettingsKey.retract_speed, printer, errors);
            }
            catch (Exception e)
            {
                errors.Add(
                    new ValidationError("ExceptionDuringSliceSettingsValidation")
                {
                    Error   = "Unexpected error validating settings".Localize(),
                    Details = e.Message
                });
            }

            return(errors);
        }