// ask the user to confirm value propagation from the last value
 private bool? ConfirmPropagateFromLastValue(String text, int imagesAffected)
 {
     text = text.Trim();
     MessageBox messageBox = new MessageBox("Please confirm 'Propagate to Here' for this field.", Application.Current.MainWindow, MessageBoxButton.YesNo);
     messageBox.Message.StatusImage = MessageBoxImage.Question;
     messageBox.Message.What = "Propagate to here is not undoabl, and can overwrite existing values.";
     messageBox.Message.Reason = "\u2022 The last non-empty value '" + text + "' was seen " + imagesAffected.ToString() + " files back." + Environment.NewLine;
     messageBox.Message.Reason += "\u2022 That field's value will be copied across all files between that file and this one in the selection";
     messageBox.Message.Result = "If you select yes: " + Environment.NewLine;
     messageBox.Message.Result = "\u2022 " + imagesAffected.ToString() + " files will be affected.";
     return messageBox.ShowDialog();
 }
        /// <summary>Propagate the current value of this control forward from this point across the current selection.</summary>
        public void CopyForward(DataEntryControl control, bool checkForZeroValue)
        {
            int filesAffected = this.FileDatabase.CurrentlySelectedFileCount - this.ImageCache.CurrentRow - 1;
            if (filesAffected == 0)
            {
                // should be unreachable as the menu shouldn't be be enabled on the last file
                MessageBox messageBox = new MessageBox("Nothing to copy forward.", Application.Current.MainWindow);
                messageBox.Message.Reason = "As you are on the last file, there are no files after this.";
                messageBox.ShowDialog();
                return;
            }

            string valueToCopy = this.ImageCache.Current.GetValueDisplayString(control);
            if (this.ConfirmCopyForward(valueToCopy, filesAffected, checkForZeroValue) != true)
            {
                return;
            }

            // update starts on the next row since copying from the current row
            this.FileDatabase.UpdateFiles(this.ImageCache.Current, control, this.ImageCache.CurrentRow + 1, this.FileDatabase.CurrentlySelectedFileCount - 1);
        }
        // ask the user to confirm value propagation from the last value
        private bool? ConfirmCopyForward(string text, int imagesAffected, bool checkForZero)
        {
            text = text.Trim();

            MessageBox messageBox = new MessageBox("Please confirm copy forward for this field...", Application.Current.MainWindow, MessageBoxButton.YesNo);
            messageBox.Message.StatusImage = MessageBoxImage.Question;
            messageBox.Message.What = "Copy forward is not undoable and can overwrite existing values.";
            messageBox.Message.Result = "If you select yes, this operation will:" + Environment.NewLine;
            if (!checkForZero && text.Equals(String.Empty))
            {
                messageBox.Message.Result += "\u2022 copy the (empty) value '" + text + "' in this field from here to the last of the selected files.";
            }
            else
            {
                messageBox.Message.Result += "\u2022 copy the value '" + text + "' in this field from here to the last of the selected files.";
            }
            messageBox.Message.Result += Environment.NewLine + "\u2022 over-write any existing data values in those fields";
            messageBox.Message.Result += Environment.NewLine + "\u2022 will affect " + imagesAffected.ToString() + " files.";
            return messageBox.ShowDialog();
        }
        // ask the user to confirm propagation to all selected files
        private bool? ConfirmCopyCurrentValueToAll(String text, int filesAffected, bool checkForZero)
        {
            text = text.Trim();

            MessageBox messageBox = new MessageBox("Please confirm copy to all for this field...", Application.Current.MainWindow, MessageBoxButton.YesNo);
            messageBox.Message.StatusImage = MessageBoxImage.Question;
            messageBox.Message.What = "Copy to all is not undoable and can overwrite existing values.";
            messageBox.Message.Result = "If you select yes, this operation will:" + Environment.NewLine;
            if (!checkForZero && text.Equals(String.Empty))
            {
                messageBox.Message.Result += "\u2022 clear this field across all " + filesAffected.ToString() + " selected files.";
            }
            else
            {
                messageBox.Message.Result += "\u2022 set this field to '" + text + "' across all " + filesAffected.ToString() + " selected files.";
            }
            messageBox.Message.Result += Environment.NewLine + "\u2022 over-write any existing data values in those fields";
            return messageBox.ShowDialog();
        }
        /// <summary>
        /// Copy the last non-empty value in this control preceding this file up to the current file
        /// </summary>
        public string CopyFromLastNonEmptyValue(DataEntryControl control)
        {
            bool isCounter = control is DataEntryCounter;
            bool isFlag = control is DataEntryFlag;

            ImageRow fileWithLastNonEmptyValue = null;
            int indexToCopyFrom = Constant.Database.InvalidRow;
            string valueToCopy = isCounter ? "0" : String.Empty;
            for (int previousIndex = this.ImageCache.CurrentRow - 1; previousIndex >= 0; previousIndex--)
            {
                // Search for the row with some value in it, starting from the previous row
                ImageRow file = this.FileDatabase.Files[previousIndex];
                valueToCopy = file.GetValueDatabaseString(control.DataLabel);
                if (valueToCopy == null)
                {
                    continue;
                }

                valueToCopy = valueToCopy.Trim();
                if (valueToCopy.Length > 0)
                {
                    if ((isCounter && !valueToCopy.Equals("0")) ||                                               // skip zero values for counters
                        (isFlag && !valueToCopy.Equals(Boolean.FalseString, StringComparison.OrdinalIgnoreCase)) || // false values for flags are considered empty
                        (!isCounter && !isFlag))
                    {
                        indexToCopyFrom = previousIndex;
                        fileWithLastNonEmptyValue = file;
                        break;
                    }
                }
            }

            if (indexToCopyFrom == Constant.Database.InvalidRow)
            {
                // Nothing to propagate.  If the menu item is deactivated as expected this shouldn't be reachable.
                MessageBox messageBox = new MessageBox("Nothing to propagate to here.", Application.Current.MainWindow);
                messageBox.Message.Reason = "None of the earlier files have anything in this field, so there are no values to propagate.";
                messageBox.ShowDialog();
                return this.FileDatabase.Files[this.ImageCache.CurrentRow].GetValueDisplayString(control); // No change, so return the current value
            }

            int imagesAffected = this.ImageCache.CurrentRow - indexToCopyFrom;
            if (this.ConfirmPropagateFromLastValue(valueToCopy, imagesAffected) != true)
            {
                return this.FileDatabase.Files[this.ImageCache.CurrentRow].GetValueDisplayString(control); // No change, so return the current value
            }

            this.FileDatabase.UpdateFiles(fileWithLastNonEmptyValue, control, indexToCopyFrom + 1, this.ImageCache.CurrentRow);
            return valueToCopy;
        }
        // Checks for updates by comparing the current version number with a version stored on the Carnassial website in an xml file.
        public bool TryGetAndParseRelease(bool showNoUpdatesMessage)
        {
            Version publicallyAvailableVersion = null;
            string description = null;

            using (WebClient webClient = new WebClient())
            {
                webClient.Headers.Add(HttpRequestHeader.UserAgent, "Carnassial-GithubReleaseClient");
                try
                {
                    string releaseJson = webClient.DownloadString(this.latestReleaseAddress);
                    if (String.IsNullOrWhiteSpace(releaseJson) || releaseJson.Contains("\"message\": \"Not Found\""))
                    {
                        // no releases, so nothing to do
                        return false;
                    }
                    JsonSerializer serializer = JsonSerializer.CreateDefault();
                    using (StringReader reader = new StringReader(releaseJson))
                    {
                        using (JsonReader jsonReader = new JsonTextReader(reader))
                        {
                            GithubRelease latestRelease = serializer.Deserialize<GithubRelease>(jsonReader);
                            if (latestRelease == null || String.IsNullOrWhiteSpace(latestRelease.TagName))
                            {
                                return false;
                            }
                            // Version's parse implementation requires the leading v of Github convention be removed
                            if (Version.TryParse(latestRelease.TagName.Substring(1), out publicallyAvailableVersion) == false)
                            {
                                return false;
                            }
                            description = latestRelease.Name + Environment.NewLine + latestRelease.Body;
                        }
                    }
                }
                catch (WebException exception)
                {
                    // 404 if no production releases (Github's latest endpoint doesn't return releases with prerelease = true)
                    if ((exception.Response is HttpWebResponse == false) || (((HttpWebResponse)exception.Response).StatusCode != HttpStatusCode.NotFound))
                    {
                        Debug.Fail(exception.ToString());
                    }
                    return false;
                }
                catch (Exception exception)
                {
                    Debug.Fail(exception.ToString());
                    return false;
                }
            }

            // get the running version
            Version currentVersion = Assembly.GetExecutingAssembly().GetName().Version;

            // compare the versions
            if (currentVersion < publicallyAvailableVersion)
            {
                // ask the user if they would like to download the new version
                string title = String.Format("Get the new version of {0}?", this.applicationName);
                MessageBox messageBox = new MessageBox(title, Application.Current.MainWindow, MessageBoxButton.YesNo);
                messageBox.Message.StatusImage = MessageBoxImage.Question;
                messageBox.Message.What = String.Format("You're running an old release, {0} {1}.", this.applicationName, currentVersion);
                messageBox.Message.Reason = String.Format("A new version is available, {0} {1}", this.applicationName, publicallyAvailableVersion);
                messageBox.Message.Solution = "Select 'Yes' to go to the website and download it.";
                messageBox.Message.Result = description;
                messageBox.Message.Hint = "\u2022 We recommend downloading the latest release." + Environment.NewLine;
                messageBox.Message.Hint += String.Format(@"\u2022 To see all releases, go to {0}.", CarnassialConfigurationSettings.GetReleasesBrowserAddress());
                if (messageBox.ShowDialog() == true)
                {
                    Uri releasesAddress = CarnassialConfigurationSettings.GetReleasesBrowserAddress();
                    Process.Start(releasesAddress.AbsoluteUri);
                }
            }
            else if (showNoUpdatesMessage)
            {
                MessageBox messageBox = new MessageBox(String.Format("No updates to {0} are available.", this.applicationName), Application.Current.MainWindow);
                messageBox.Message.Reason = String.Format("You're running the latest release, {0} {1}.", this.applicationName, currentVersion);
                messageBox.Message.StatusImage = MessageBoxImage.Information;
                bool? messageBoxResult = messageBox.ShowDialog();
            }

            return true;
        }
        public static void ShowExceptionReportingDialog(string title, UnhandledExceptionEventArgs e, Window owner)
        {
            MessageBox exitNotification = new MessageBox("Uh-oh.  We're so sorry.", owner);
            exitNotification.Message.StatusImage = MessageBoxImage.Error;
            exitNotification.Message.Title = title;
            exitNotification.Message.Problem = String.Format("This is a bug.  The What section below has been copied.  Please paste it into a new issue at {0} or an email to {1}.  Please also include a clear and specific description of what you were doing at the time.",
                                                             CarnassialConfigurationSettings.GetIssuesBrowserAddress(),
                                                             CarnassialConfigurationSettings.GetDevTeamEmailLink().ToEmailAddress());
            exitNotification.Message.What = String.Format("{0}, {1}, .NET {2}{3}", typeof(CarnassialWindow).Assembly.GetName(), Environment.OSVersion, Utilities.GetDotNetVersion(), Environment.NewLine);
            if (e.ExceptionObject != null)
            {
                exitNotification.Message.What += e.ExceptionObject.ToString();
            }
            exitNotification.Message.Reason = "It's not you, it's us.  If you let us know we'll get it fixed.  If you don't tell us probably we don't know there's a problem and things won't get any better.";
            exitNotification.Message.Result = String.Format("The data file is likely OK.  If it's not you can restore from the {0} folder.", Constant.File.BackupFolder);
            exitNotification.Message.Hint = "\u2022 If you do the same thing this'll probably happen again.  If so, that's helpful to know as well." + Environment.NewLine;
            exitNotification.Message.Hint += "\u2022 If the automatic copy of the What content didn't take click on the What details, hit ctrl+a to select all of it, and ctrl+c to copy.";

            Clipboard.SetText(exitNotification.Message.What);
            exitNotification.ShowDialog();
        }