public MainWindow()
        {
            DataContext = this;
            InitializeComponent();
            var descriptor = DependencyPropertyDescriptor.FromProperty(ArduinoPathProperty, typeof(Control));

            //NOTE this would be a concerning memory leak, if this was not the main window that ran the entire lifetime of the app anyways.
            //essentially, it attaches a reference to this window to the static dependency property, which prevents garbage collection. If this were a helper window,
            //we'd have to wrap this in a weak reference.
            descriptor.AddValueChanged(this, delegate {
                ProgrammerInfo[] programmers         = GetAllProgrammers(ArduinoPath).ToArray();
                ProgrammerInfo oldSelectedProgrammer = SelectedProgrammer;
                Programmers        = programmers;
                SelectedProgrammer = oldSelectedProgrammer != null ? Programmers.FirstOrDefault(p => p.Name == oldSelectedProgrammer.Name) : null;
                UpdateValidationState();
            });


            descriptor = DependencyPropertyDescriptor.FromProperty(SelectedProgrammerProperty, typeof(Control));
            descriptor.AddValueChanged(this, delegate
            {
                UpdateValidationState();
            });


            descriptor = DependencyPropertyDescriptor.FromProperty(SelectedPortProperty, typeof(Control));
            descriptor.AddValueChanged(this, delegate
            {
                UpdateValidationState();
            });
        }
        static List <ProgrammerInfo> GetProgrammersFromFile(string programmersFile)
        {
            List <ProgrammerInfo> retVal = new List <ProgrammerInfo>();
            List <string>         lines  = new List <string>();

            try
            {
                using (var stream = new FileStream(programmersFile, FileMode.Open, FileAccess.Read))
                    using (var sr = new StreamReader(stream))
                    {
                        while (!sr.EndOfStream)
                        {
                            lines.Add(sr.ReadLine().Trim());
                        }
                    }

                var meaningfulLines = (from l in lines where l.Length > 0 && l[0] != '#' select l);

                var programmerLines = (from l in
                                       meaningfulLines
                                       let index = l.IndexOf('.')
                                                   where index > 0
                                                   select l.Substring(0, index)).Distinct();

                foreach (string programmerLine in programmerLines)
                {
                    var programmerSettingsLines = from l in meaningfulLines
                                                  let index = l.IndexOf(".")
                                                              where index > 0 && l.StartsWith(programmerLine)
                                                              select l;

                    bool           usesSerial = programmerSettingsLines.Any(s => s.Contains("serial.port"));
                    ProgrammerInfo info       = new ProgrammerInfo()
                    {
                        Name = programmerLine, UsesSerial = usesSerial
                    };
                    retVal.Add(new ProgrammerInfo()
                    {
                        Name = programmerLine, UsesSerial = usesSerial
                    });
                }
            }
            catch (Exception e)
            {
                Trace.WriteLine("Crash! " + e.ToString());
            }
            return(retVal);
        }
        public static bool ValidateArduinoSetup(string arduinoPath, ProgrammerInfo[] programmers, ProgrammerInfo selectedProgrammer,
                                                string[] ports, string selectedPort, bool forUpload, out string message)
        {
            if (string.IsNullOrWhiteSpace(arduinoPath) || !File.Exists(arduinoPath))
            {
                message = "The path to your Arduino IDE is invalid";
                return(false);
            }
            var ardPathInfo = new FileInfo(arduinoPath);

            if (ardPathInfo.Extension.ToLowerInvariant() != ".exe")
            {
                message = "The path to your Arduino IDE must point to 'Arduino.exe'";
                return(false);
            }

            if (forUpload)
            {
                //not all programmers require ports,
                if (selectedProgrammer == null || string.IsNullOrWhiteSpace(selectedProgrammer.Name) || !programmers.Any(s => s.Name == selectedProgrammer.Name))
                {
                    message = "The Selected";
                    return(false);
                }

                if (string.IsNullOrWhiteSpace(selectedPort) || !ports.Contains(selectedPort))
                {
                }
            }
            message = "";
            return(true);
        }
        static List<ProgrammerInfo> GetProgrammersFromFile(string programmersFile)
        {
            List<ProgrammerInfo> retVal = new List<ProgrammerInfo>();
            List<string> lines = new List<string>();
            try
            {
                using (var stream = new FileStream(programmersFile, FileMode.Open, FileAccess.Read))
                using (var sr = new StreamReader(stream))
                {
                    while (!sr.EndOfStream)
                    {
                        lines.Add(sr.ReadLine().Trim());
                    }
                }

                var meaningfulLines = (from l in lines where l.Length > 0 && l[0] != '#' select l);

                var programmerLines = (from l in
                                           meaningfulLines
                                       let index = l.IndexOf('.')
                                       where index > 0
                                       select l.Substring(0, index)).Distinct();

                foreach (string programmerLine in programmerLines)
                {
                    var programmerSettingsLines = from l in meaningfulLines
                                                  let index = l.IndexOf(".")
                                                  where index > 0 && l.StartsWith(programmerLine)
                                                  select l;

                    bool usesSerial = programmerSettingsLines.Any(s => s.Contains("serial.port"));
                    ProgrammerInfo info = new ProgrammerInfo() { Name = programmerLine, UsesSerial = usesSerial };
                    retVal.Add(new ProgrammerInfo() { Name = programmerLine, UsesSerial = usesSerial });
                }

            }
            catch (Exception e)
            {
                Trace.WriteLine("Crash! " + e.ToString());
            }
            return retVal;
        }
        public static bool ValidateArduinoSetup(string arduinoPath,ProgrammerInfo[] programmers, ProgrammerInfo selectedProgrammer,
            string[] ports, string selectedPort, bool forUpload, out string message)
        {
            if (string.IsNullOrWhiteSpace( arduinoPath) || !File.Exists(arduinoPath) )
            {
                message = "The path to your Arduino IDE is invalid";
                return false;
            }
            var ardPathInfo = new FileInfo(arduinoPath);
            if (ardPathInfo.Extension.ToLowerInvariant() != ".exe")
            {
                message = "The path to your Arduino IDE must point to 'Arduino.exe'";
                return false;
            }

            if (forUpload)
            {
                //not all programmers require ports,
                if (selectedProgrammer == null || string.IsNullOrWhiteSpace(selectedProgrammer.Name) || !programmers.Any(s => s.Name == selectedProgrammer.Name))
                {
                    message = "The Selected";
                    return false;
                }

                if (string.IsNullOrWhiteSpace(selectedPort) || !ports.Contains(selectedPort))
                {

                }
            }
            message = "";
            return true;
        }