context: using PKHeX.Core;
  using System;
  using System.Collections.Generic;
  using System.Drawing;
  using System.IO;
  using System.Linq;
  using System.Windows.Forms;
  using PKHeX.Drawing.Misc;
  using PKHeX.WinForms.Controls;
  using static PKHeX.Core.MessageStrings;
  
  namespace PKHeX.WinForms
  {
      public partial class SAV_Wondercard : Form
      {
          private readonly SaveFile Origin;
          private readonly SaveFile SAV;
          private readonly SummaryPreviewer Summary = new();
  
          public SAV_Wondercard(SaveFile sav, DataMysteryGift? g = null)
          {
              InitializeComponent();
              WinFormsUtil.TranslateInterface(this, Main.CurrentLanguage);
              SAV = (Origin = sav).Clone();
              mga = SAV.GiftAlbum;
  
              pba = SAV.Generation switch
              {
                  4 => PopulateViewGiftsG4().ToArray(),
                  5 or 6 or 7 => PopulateViewGiftsG567().ToArray(),
                  _ => throw new ArgumentOutOfRangeException(nameof(SAV.Generation), "Game not supported."),
              };
              foreach (var pb in pba)
              {
                  pb.AllowDrop = true;
                  pb.DragDrop += BoxSlot_DragDrop;
                  pb.DragEnter += BoxSlot_DragEnter;
                  pb.MouseDown += BoxSlot_MouseDown;
                  pb.ContextMenuStrip = mnuVSD;
                  pb.MouseHover += (_, _) => Summary.Show(pb, mga.Gifts[pba.IndexOf(pb)]);
              }
  
              SetGiftBoxes();
              GetReceivedFlags();
  
              if (LB_Received.Items.Count > 0)
                  LB_Received.SelectedIndex = 0;
  
              if (mga.Gifts[0] is WR7) // giftused is not a valid prop
                  B_UnusedAll.Visible = B_UsedAll.Visible = L_QR.Visible = false;
  
              DragEnter += Main_DragEnter;
              DragDrop += Main_DragDrop;
  
              if (g == null)
                  ClickView(pba[0], EventArgs.Empty);
              else
                  ViewGiftData(g);
          }
  
          private readonly MysteryGiftAlbum mga;
          private DataMysteryGift? mg;
          private readonly IList<PictureBox> pba;
  
          // Repopulation Functions
          private void SetBackground(int index, Image bg)
          {
              for (int i = 0; i < mga.Gifts.Length; i++)
                  pba[i].BackgroundImage = index == i ? bg : null;
          }
  
          private void SetGiftBoxes()
          {
              for (int i = 0; i < mga.Gifts.Length; i++)
              {
                  MysteryGift m = mga.Gifts[i];
                  pba[i].Image = m.Sprite();
              }
          }
  
          private void ViewGiftData(DataMysteryGift g)
          {
              try
              {
                  // only check if the form is visible (not opening)
                  if (Visible && g.GiftUsed && DialogResult.Yes == WinFormsUtil.Prompt(MessageBoxButtons.YesNo, MsgMsyteryGiftUsedAlert, MsgMysteryGiftUsedFix))
                      g.GiftUsed = false;
  
                  RTB.Lines = g.GetDescription().ToArray();
                  PB_Preview.Image = g.Sprite();
                  mg = g;
              }
              // Some user input mystery gifts can have out-of-bounds values. Just swallow any exception.
              catch (Exception e)
              {
                  WinFormsUtil.Error(MsgMysteryGiftParseTypeUnknown, e);
                  RTB.Clear();
              }
          }
  
          private void GetReceivedFlags()
          {
              LB_Received.Items.Clear();
              for (int i = 1; i < mga.Flags.Length; i++)
              {
                  if (mga.Flags[i])
                      LB_Received.Items.Add(i.ToString("0000"));
              }
  
              if (LB_Received.Items.Count > 0)
                  LB_Received.SelectedIndex = 0;
          }
  
          private void SetCardID(int cardID)
          {
              if (cardID is <= 0 or >= 0x100 * 8)
                  return;
  
              string card = cardID.ToString("0000");
              if (!LB_Received.Items.Contains(card))
                  LB_Received.Items.Add(card);
              LB_Received.SelectedIndex = LB_Received.Items.IndexOf(card);
          }
  
          // Mystery Gift IO (.file<->window)
          private void B_Import_Click(object sender, EventArgs e)
          {
              using var import = new OpenFileDialog {Filter = WinFormsUtil.GetMysterGiftFilter(SAV.Generation, SAV.Version) };
              if (import.ShowDialog() != DialogResult.OK) return;
  
              string path = import.FileName;
              var g = MysteryGift.GetMysteryGift(File.ReadAllBytes(path), Path.GetExtension(path));
              if (g == null)
              {
                  WinFormsUtil.Error(MsgMysteryGiftInvalid, path);
                  return;
              }
              ViewGiftData(g);
          }
  
          private void B_Output_Click(object sender, EventArgs e)
          {
              if (mg == null)
                  return;
              WinFormsUtil.ExportMGDialog(mg, SAV.Version);
          }
  
          private static int GetLastUnfilledByType(MysteryGift Gift, MysteryGiftAlbum Album)
          {
              for (int i = 0; i < Album.Gifts.Length; i++)
              {
                  if (!Album.Gifts[i].Empty)
                      continue;
                  if (Album.Gifts[i].Type != Gift.Type)
                      continue;
                  return i;
              }
              return -1;
          }
          // Mystery Gift RW (window<->sav)
          private void ClickView(object sender, EventArgs e)
          {
              var pb = WinFormsUtil.GetUnderlyingControl<PictureBox>(sender);
              if (pb == null)
                  return;
              int index = pba.IndexOf(pb);
  
              SetBackground(index, Drawing.PokeSprite.Properties.Resources.slotView);
              ViewGiftData(mga.Gifts[index]);
          }
  
          private void ClickSet(object sender, EventArgs e)
          {
              if (mg is not { } gift)
                  return;
  
              if (!gift.IsCardCompatible(SAV, out var msg))
              {
                  WinFormsUtil.Alert(MsgMysteryGiftSlotFail, msg);
                  return;
              }
  
              var pb = WinFormsUtil.GetUnderlyingControl<PictureBox>(sender);
              if (pb == null)
                  return;
              int index = pba.IndexOf(pb);
  
              // Hijack to the latest unfilled slot if index creates interstitial empty slots.
              int lastUnfilled = GetLastUnfilledByType(gift, mga);
              if (lastUnfilled > -1 && lastUnfilled < index)
                  index = lastUnfilled;
  
              if (gift is PCD pcd && mga.Gifts[index] is PGT)
              {
                  gift = pcd.Gift;
              }
              else if (gift.Type != mga.Gifts[index].Type)
              {
                  WinFormsUtil.Alert(MsgMysteryGiftSlotFail, $"{gift.Type} != {mga.Gifts[index].Type}");
                  return;
              }
              SetBackground(index, Drawing.PokeSprite.Properties.Resources.slotSet);
              mga.Gifts[index] = (DataMysteryGift)gift.Clone();
              SetGiftBoxes();
              SetCardID(gift.CardID);
          }
  
          private void ClickDelete(object sender, EventArgs e)
          {
              var pb = WinFormsUtil.GetUnderlyingControl<PictureBox>(sender);
              if (pb == null)
                  return;
              int index = pba.IndexOf(pb);
  
              var arr = mga.Gifts[index].Data;
              Array.Clear(arr, 0, arr.Length);
  
              // Shuffle blank card down
              int i = index;
              while (i < mga.Gifts.Length - 1)
              {
                  if (mga.Gifts[i+1].Empty)
                      break;
                  if (mga.Gifts[i+1].Type != mga.Gifts[i].Type)
                      break;
  
                  i++;
  
                  var mg1 = mga.Gifts[i];
                  var mg2 = mga.Gifts[i-1];
  
                  mga.Gifts[i-1] = mg1;
                  mga.Gifts[i] = mg2;
              }
              SetBackground(i, Drawing.PokeSprite.Properties.Resources.slotDel);
              SetGiftBoxes();
          }
  
          // Close Window
          private void B_Cancel_Click(object sender, EventArgs e)
          {
              Close();
          }
  
          private void B_Save_Click(object sender, EventArgs e)
          {
              // Make sure all of the Received Flags are flipped!
              bool[] flags = new bool[mga.Flags.Length];
              foreach (var o in LB_Received.Items)
              {
  
gt:                 var value = o?.ToString();