context: using System;
  using System.Collections.Generic;
  using System.ComponentModel;
  using System.Diagnostics;
  using System.Diagnostics.CodeAnalysis;
  using System.Drawing;
  using System.IO;
  using System.Linq;
  using System.Text;
  using System.Text.RegularExpressions;
  using System.Threading.Tasks;
  using System.Windows.Forms;
  using GitCommands;
  using GitCommands.Patches;
  using GitCommands.Settings;
  using GitCommands.Utils;
  using GitExtUtils;
  using GitExtUtils.GitUI;
  using GitExtUtils.GitUI.Theming;
  using GitUI.CommandsDialogs;
  using GitUI.CommandsDialogs.SettingsDialog.Pages;
  using GitUI.Editor.Diff;
  using GitUI.Hotkey;
  using GitUI.Properties;
  using GitUI.UserControls;
  using GitUIPluginInterfaces;
  using Microsoft;
  using ResourceManager;
  
  namespace GitUI.Editor
  {
      [DefaultEvent("SelectedLineChanged")]
      public partial class FileViewer : GitModuleControl
      {
          /// <summary>
          /// Raised when the Escape key is pressed (and only when no selection exists, as the default behaviour of escape is to clear the selection).
          /// </summary>
          public event Action? EscapePressed;
  
          /// <summary>
          /// The type of information currently shown in the file viewer.
          /// </summary>
          private enum ViewMode
          {
              // Plain text
              Text,
  
              // Diff or patch
              Diff,
  
              // Diffs that will not be affected by diff arguments like white space etc (limited options)
              FixedDiff,
  
              // range-diff output
              RangeDiff,
  
              // Image viewer
              Image
          }
  
          private readonly TranslationString _largeFileSizeWarning = new("This file is {0:N1} MB. Showing large files can be slow. Click to show anyway.");
          private readonly TranslationString _cannotViewImage = new("Cannot view image {0}");
  
          public event EventHandler<SelectedLineEventArgs>? SelectedLineChanged;
          public event EventHandler? HScrollPositionChanged;
          public event EventHandler? VScrollPositionChanged;
          public event EventHandler? BottomScrollReached;
          public event EventHandler? TopScrollReached;
          public event EventHandler? RequestDiffView;
          public new event EventHandler? TextChanged;
          public event EventHandler? TextLoaded;
          public event CancelEventHandler? ContextMenuOpening;
          public event EventHandler<EventArgs>? ExtraDiffArgumentsChanged;
          public event EventHandler<EventArgs>? PatchApplied;
  
          private readonly AsyncLoader _async;
          private readonly IFullPathResolver _fullPathResolver;
          private ViewMode _viewMode;
          private Encoding? _encoding;
          private Func<Task>? _deferShowFunc;
          private readonly ContinuousScrollEventManager _continuousScrollEventManager;
          private FileStatusItem? _viewItem;
  
          private static string[] _rangeDiffFullPrefixes = { "      ", "    ++", "    + ", "     +", "    --", "    - ", "     -", "    +-", "    -+", "    " };
          private static string[] _combinedDiffFullPrefixes = { "  ", "++", "+ ", " +", "--", "- ", " -" };
          private static string[] _normalDiffFullPrefixes = { " ", "+", "-" };
  
          private static string[] _rangeDiffPrefixes = { "    " };
          private static string[] _combinedDiffPrefixes = { "+", "-", " +", " -" };
          private static string[] _normalDiffPrefixes = { "+", "-" };
  
          public FileViewer()
          {
              TreatAllFilesAsText = false;
              ShowEntireFile = false;
              NumberOfContextLines = AppSettings.NumberOfContextLines;
              InitializeComponent();
              InitializeComplete();
  
              UICommandsSourceSet += OnUICommandsSourceSet;
  
              internalFileViewer.MouseEnter += (_, e) => OnMouseEnter(e);
              internalFileViewer.MouseLeave += (_, e) => OnMouseLeave(e);
              internalFileViewer.MouseMove += (_, e) => OnMouseMove(e);
              internalFileViewer.KeyUp += (_, e) => OnKeyUp(e);
              internalFileViewer.EscapePressed += () => EscapePressed?.Invoke();
  
              _continuousScrollEventManager = new ContinuousScrollEventManager();
              _continuousScrollEventManager.BottomScrollReached += _continuousScrollEventManager_BottomScrollReached;
              _continuousScrollEventManager.TopScrollReached += _continuousScrollEventManager_TopScrollReached;
  
              PictureBox.MouseWheel += PictureBox_MouseWheel;
              internalFileViewer.SetContinuousScrollManager(_continuousScrollEventManager);
  
              TextLoaded += (sender, args) => AllowLinePatching = SupportLinePatching;
              _async = new AsyncLoader();
              _async.LoadingError +=
                  (_, e) =>
                  {
                      if (!IsDisposed)
                      {
                          ResetView(ViewMode.Text, null);
                          internalFileViewer.SetText("Unsupported file: \n\n" + e.Exception.ToString(), openWithDifftool: null /* not applicable */);
                          TextLoaded?.Invoke(this, null);
                      }
                  };
  
              IgnoreWhitespace = AppSettings.IgnoreWhitespaceKind;
              OnIgnoreWhitespaceChanged();
  
              ignoreWhitespaceAtEol.Image = Images.WhitespaceIgnoreEol.AdaptLightness();
              ignoreWhitespaceAtEolToolStripMenuItem.Image = ignoreWhitespaceAtEol.Image;
  
              ignoreWhiteSpaces.Image = Images.WhitespaceIgnore.AdaptLightness();
              ignoreWhitespaceChangesToolStripMenuItem.Image = ignoreWhiteSpaces.Image;
  
              ignoreAllWhitespaces.Image = Images.WhitespaceIgnoreAll.AdaptLightness();
              ignoreAllWhitespaceChangesToolStripMenuItem.Image = ignoreAllWhitespaces.Image;
  
              ShowEntireFile = AppSettings.ShowEntireFile;
              showEntireFileButton.Checked = ShowEntireFile;
              showEntireFileToolStripMenuItem.Checked = ShowEntireFile;
              SetStateOfContextLinesButtons();
  
              automaticContinuousScrollToolStripMenuItem.Image = Images.UiScrollBar.AdaptLightness();
              automaticContinuousScrollToolStripMenuItem.Checked = AppSettings.AutomaticContinuousScroll;
  
              showNonPrintChars.Image = Images.ShowWhitespace.AdaptLightness();
              showNonprintableCharactersToolStripMenuItem.Image = showNonPrintChars.Image;
              showNonPrintChars.Checked = AppSettings.ShowNonPrintingChars;
              showNonprintableCharactersToolStripMenuItem.Checked = AppSettings.ShowNonPrintingChars;
              ToggleNonPrintingChars(AppSettings.ShowNonPrintingChars);
  
              ShowSyntaxHighlightingInDiff = AppSettings.ShowSyntaxHighlightingInDiff;
              showSyntaxHighlighting.Image = Resources.SyntaxHighlighting.AdaptLightness();
              showSyntaxHighlighting.Checked = ShowSyntaxHighlightingInDiff;
              automaticContinuousScrollToolStripMenuItem.Text = TranslatedStrings.ContScrollToNextFileOnlyWithAlt;
  
              IsReadOnly = true;
  
              internalFileViewer.MouseMove += (_, e) =>
              {
                  if (IsDiffView(_viewMode) && !fileviewerToolbar.Visible)
                  {
                      fileviewerToolbar.Visible = true;
                      fileviewerToolbar.Location = new Point(Width - fileviewerToolbar.Width - 40, 0);
                      fileviewerToolbar.BringToFront();
                  }
              };
              internalFileViewer.MouseLeave += (_, e) =>
              {
                  if (GetChildAtPoint(PointToClient(MousePosition)) != fileviewerToolbar &&
                      fileviewerToolbar is not null)
                  {
                      fileviewerToolbar.Visible = false;
                  }
              };
              internalFileViewer.TextChanged += (sender, e) =>
              {
                  if (IsDiffView(_viewMode))
                  {
                      internalFileViewer.AddPatchHighlighting();
                  }
  
                  TextChanged?.Invoke(sender, e);
              };
              internalFileViewer.HScrollPositionChanged += (sender, e) => HScrollPositionChanged?.Invoke(sender, e);
              internalFileViewer.VScrollPositionChanged += (sender, e) => VScrollPositionChanged?.Invoke(sender, e);
              internalFileViewer.SelectedLineChanged += (sender, e) => SelectedLineChanged?.Invoke(sender, e);
              internalFileViewer.DoubleClick += (_, args) => RequestDiffView?.Invoke(this, EventArgs.Empty);
  
              HotkeysEnabled = true;
  
              if (!IsDesignMode && ContextMenuStrip is null)
              {
                  ContextMenuStrip = contextMenu;
              }
  
              contextMenu.Opening += (sender, e) =>
              {
                  copyToolStripMenuItem.Enabled = internalFileViewer.GetSelectionLength() > 0;
                  ContextMenuOpening?.Invoke(sender, e);
              };
  
              _fullPathResolver = new FullPathResolver(() => Module.WorkingDir);
              SupportLinePatching = false;
          }
  
          // Public properties
  
          [Browsable(false)]
          public byte[]? FilePreamble { get; private set; }
  
          [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
          [Browsable(false)]
          public new Font Font
          {
              get => internalFileViewer.Font;
              set => internalFileViewer.Font = value;
          }
  
          [DefaultValue(true)]
          [Category("Behavior")]
          public bool IsReadOnly
          {
              get => internalFileViewer.IsReadOnly;
              set => internalFileViewer.IsReadOnly = value;
          }
  
          [DefaultValue(true)]
          [Category("Behavior")]
          public bool EnableAutomaticContinuousScroll
          {
              get => automaticContinuousScrollToolStripMenuItem.Visible;
              set => automaticContinuousScrollToolStripMenuItem.Visible = value;
          }
  
          [DefaultValue(null)]
          [Description("If true line numbers are shown in the textarea")]
          [Category("Appearance")]
          public bool? ShowLineNumbers
          {
              get => internalFileViewer.ShowLineNumbers;
              set => internalFileViewer.ShowLineNumbers = value;
          }
  
          [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
          [Browsable(false)]
          [NotNull]
          public Encoding? Encoding
          {
              get => _encoding ??= Module.FilesEncoding;
              set
              {
                  _encoding = value;
  
                  this.InvokeAsync(() =>
                  {
                      if (_encoding is not null)
                      {
                          encodingToolStripComboBox.Text = _encoding.EncodingName;
                      }
                      else
                      {
                          encodingToolStripComboBox.SelectedIndex = -1;
                      }
                  }).FileAndForget();
              }
          }
  
          public void ScrollToTop()
          {
              internalFileViewer.ScrollToTop();
          }
  
          public void ScrollToBottom()
          {
              internalFileViewer.ScrollToBottom();
          }
  
          [DefaultValue(0)]
          [Browsable(false)]
          public int HScrollPosition
          {
              get => internalFileViewer.HScrollPosition;
              set => internalFileViewer.HScrollPosition = value;
          }
  
          [DefaultValue(0)]
          [Browsable(false)]
          public int VScrollPosition
          {
              get => internalFileViewer.VScrollPosition;
              set => internalFileViewer.VScrollPosition = value;
          }
  
          // Private properties
  
          [Description("Sets what kind of whitespace changes shall be ignored in diffs")]
          [DefaultValue(IgnoreWhitespaceKind.None)]
          private IgnoreWhitespaceKind IgnoreWhitespace { get; set; }
  
          [Description("Show diffs with <n> lines of context.")]
          [DefaultValue(3)]
          private int NumberOfContextLines { get; set; }
  
          [Description("Show diffs with entire file.")]
          [DefaultValue(false)]
          private bool ShowEntireFile { get; set; }
  
          [Description("Treat all files as text.")]
          [DefaultValue(false)]
          private bool TreatAllFilesAsText { get; set; }
  
          [Description("Show syntax highlighting in diffs.")]
          [DefaultValue(true)]
          private bool ShowSyntaxHighlightingInDiff { get; set; }
  
          // Public methods
  
          public void SetGitBlameGutter(IEnumerable<GitBlameEntry> gitBlameEntries)
          {
              internalFileViewer.ShowGutterAvatars = AppSettings.BlameShowAuthorAvatar;
  
              if (AppSettings.BlameShowAuthorAvatar)
              {
                  internalFileViewer.SetGitBlameGutter(gitBlameEntries);
              }
          }
  
          public void ReloadHotkeys()
          {
              Hotkeys = HotkeySettingsManager.LoadHotkeys(HotkeySettingsName);
              stageSelectedLinesToolStripMenuItem.ShortcutKeyDisplayString = GetShortcutKeyDisplayString(Command.StageLines);
              unstageSelectedLinesToolStripMenuItem.ShortcutKeyDisplayString = GetShortcutKeyDisplayString(Command.UnstageLines);
              resetSelectedLinesToolStripMenuItem.ShortcutKeyDisplayString = GetShortcutKeyDisplayString(Command.ResetLines);
          }
  
          public ToolStripSeparator AddContextMenuSeparator()
          {
              ToolStripSeparator separator = new();
              contextMenu.Items.Add(separator);
              return separator;
          }
  
          public ToolStripMenuItem AddContextMenuEntry(string text, EventHandler toolStripItem_Click)
          {
              ToolStripMenuItem toolStripItem = new(text);
              contextMenu.Items.Add(toolStripItem);
              toolStripItem.Click += toolStripItem_Click;
              return toolStripItem;
          }
  
          public void EnableScrollBars(bool enable)
          {
              internalFileViewer.EnableScrollBars(enable);
          }
  
          public ArgumentString GetExtraDiffArguments(bool isRangeDiff = false)
          {
              return new ArgumentBuilder
              {
                  { IgnoreWhitespace == IgnoreWhitespaceKind.AllSpace, "--ignore-all-space" },
                  { IgnoreWhitespace == IgnoreWhitespaceKind.Change, "--ignore-space-change" },
                  { IgnoreWhitespace == IgnoreWhitespaceKind.Eol, "--ignore-space-at-eol" },
                  { ShowEntireFile, "--inter-hunk-context=9000 --unified=9000", $"--unified={NumberOfContextLines}" },
  
                  // Handle zero context as showing no file changes, to get the summary only
                  { isRangeDiff && NumberOfContextLines == 0, "--no-patch " },
  
gt:                 { TreatAllFilesAsText, "--text" }