context: using Server.Accounting;
  using Server.Commands;
  using Server.Commands.Generic;
  using Server.Gumps;
  using Server.Items;
  using Server.Multis;
  using Server.Network;
  using Server.Regions;
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Linq;
  using System.Threading;
  
  namespace Server.Mobiles
  {
      public class XmlFindGump : Gump
      {
          public class XmlFindThread
          {
              readonly SearchCriteria m_SearchCriteria;
              readonly Mobile m_From;
              readonly string m_commandstring;
  
              public XmlFindThread(Mobile from, SearchCriteria criteria, string commandstring)
              {
                  m_SearchCriteria = criteria;
                  m_From = from;
                  m_commandstring = commandstring;
              }
  
  
              public void XmlFindThreadMain()
              {
  
                  if (m_From == null) return;
  
                  string status_str;
  
                  ArrayList results = Search(m_SearchCriteria, out status_str);
  
                  XmlFindGump gump = new XmlFindGump(m_From, m_From.Location, m_From.Map, true, true, false,
  
                      m_SearchCriteria,
  
                      results, -1, 0, null, m_commandstring,
                      false, false, false, false, false, false, 0, 0);
  
                  // display the updated gump synched with the main server thread
                  Timer.DelayCall(TimeSpan.Zero, new TimerStateCallback(GumpDisplayCallback), new object[] { m_From, gump, status_str });
  
              }
  
              public void GumpDisplayCallback(object state)
              {
                  object[] args = (object[])state;
  
                  Mobile from = (Mobile)args[0];
                  XmlFindGump gump = (XmlFindGump)args[1];
                  string status_str = (string)args[2];
  
                  if (from != null && !from.Deleted)
                  {
                      from.SendGump(gump);
                      if (status_str != null)
                      {
                          from.SendMessage(33, "XmlFind: {0}", status_str);
                      }
                  }
              }
  
  
          }
  
          private const int MaxEntries = 18;
          private const int MaxEntriesPerPage = 18;
  
          public class SearchEntry
          {
              public bool Selected;
              public object Object;
  
              public SearchEntry(object o)
              {
                  Object = o;
              }
          }
  
          public class SearchCriteria
          {
              public bool Dosearchtype;
              public bool Dosearchname;
              public bool Dosearchrange;
              public bool Dosearchregion;
              public bool Dosearchspawnentry;
              public bool Dosearchspawntype;
              public bool Dosearchcondition;
              public bool Dosearchfel;
              public bool Dosearchtram;
              public bool Dosearchmal;
              public bool Dosearchilsh;
              public bool Dosearchtok;
              public bool Dosearchter;
              public bool Dosearchint;
              public bool Dosearchnull;
              public bool Dosearcherr;
              public bool Dosearchage;
              public bool Dohidevalidint = false;
              public bool Searchagedirection;
              public double Searchage;
              public int Searchrange;
              public string Searchregion;
              public string Searchcondition;
              public string Searchtype;
              public string Searchname;
              public string Searchspawnentry;
  
              public Map Currentmap;
              public Point3D Currentloc;
  
              public SearchCriteria(bool dotype, bool doname, bool dorange, bool doregion, bool doentry, bool doentrytype, bool docondition, bool dofel, bool dotram,
                  bool domal, bool doilsh, bool dotok, bool doter, bool doint, bool donull, bool doerr, bool doage, bool dohidevalid,
                  bool agedirection, double age, int range, string region, string condition, string type, string name, string entry
                  )
              {
                  Dosearchtype = dotype;
                  Dosearchname = doname;
                  Dosearchrange = dorange;
                  Dosearchregion = doregion;
                  Dosearchspawnentry = doentry;
                  Dosearchspawntype = doentrytype;
                  Dosearchcondition = docondition;
                  Dosearchfel = dofel;
                  Dosearchtram = dotram;
                  Dosearchmal = domal;
                  Dosearchilsh = doilsh;
                  Dosearchtok = dotok;
                  Dosearchter = doter;
                  Dosearchint = doint;
                  Dosearchnull = donull;
                  Dosearcherr = doerr;
                  Dosearchage = doage;
                  Dohidevalidint = dohidevalid;
                  Searchagedirection = agedirection;
                  Searchage = age;
                  Searchrange = range;
                  Searchregion = region;
                  Searchcondition = condition;
                  Searchtype = type;
                  Searchname = name;
                  Searchspawnentry = entry;
              }
  
              public SearchCriteria()
              {
              }
          }
  
          private readonly SearchCriteria m_SearchCriteria;
          private bool Sorttype;
          private bool Sortrange;
          private bool Sortname;
          private bool Sortmap;
          private bool Sortselect;
          private readonly Mobile m_From;
          private readonly Point3D StartingLoc;
          private readonly Map StartingMap;
          private bool m_ShowExtension;
          private bool Descendingsort;
          private int Selected;
          private int DisplayFrom;
          private string SaveFilename;
          private string CommandString;
  
          private bool SelectAll = false;
  
          private ArrayList m_SearchList;
  
          public static void Initialize()
          {
              CommandSystem.Register("XmlFind", AccessLevel.GameMaster, XmlFind_OnCommand);
          }
  
          private static bool TestRange(object o, int range, Map currentmap, Point3D currentloc)
          {
              if (range < 0) return true;
              if (o is Item item)
              {
                  if (item.Map != currentmap) return false;
  
                  // is the item in a container?
                  // if so, then check the range of the parent rather than the item
                  Point3D loc = item.Location;
                  if (item.Parent != null && item.RootParent != null)
                  {
                      if (item.RootParent is Mobile mobile)
                      {
                          loc = mobile.Location;
                      }
                      else
                          if (item.RootParent is Container container)
                      {
                          loc = container.Location;
                      }
  
                  }
                  return (Utility.InRange(currentloc, loc, range));
  
              }
              if (o is Mobile mob)
              {
                  if (mob.Map != currentmap) return false;
                  return (Utility.InRange(currentloc, mob.Location, range));
  
              }
              return false;
          }
  
          private static bool TestRegion(object o, string regionname)
          {
              if (regionname == null)
                  return false;
  
              if (o is Item item)
              {
                  // is the item in a container?
                  // if so, then check the region of the parent rather than the item
                  Point3D loc = item.Location;
                  if (item.Parent != null && item.RootParent != null)
                  {
                      if (item.RootParent is Mobile mobile)
                      {
                          loc = mobile.Location;
                      }
                      else
                          if (item.RootParent is Container container)
                      {
                          loc = container.Location;
                      }
                  }
  
                  Region r = Region.Regions.FirstOrDefault(reg => reg.Map == item.Map && !string.IsNullOrEmpty(reg.Name) && string.Equals(reg.Name, regionname, StringComparison.CurrentCultureIgnoreCase));
  
                  if (r == null)
                      return false;
  
                  return (r.Contains(loc));
              }
  
              if (o is Mobile mob)
              {
                  Region r = Region.Regions.FirstOrDefault(reg => reg.Map == mob.Map && !string.IsNullOrEmpty(reg.Name) && string.Equals(reg.Name, regionname, StringComparison.CurrentCultureIgnoreCase));
  
                  if (r == null) return false;
                  return (r.Contains(mob.Location));
  
              }
  
              return false;
          }
  
          private static bool TestAge(object o, double age, bool direction)
          {
              if (age <= 0) return true;
  
              if (o is Mobile mob)
              {
                  if (direction)
                  {
                      // true means allow only mobs greater than the age
                      if ((DateTime.UtcNow - mob.CreationTime) > TimeSpan.FromHours(age)) return true;
                  }
                  else
                  {
                      // false means allow only mobs less than the age
                      if ((DateTime.UtcNow - mob.CreationTime) < TimeSpan.FromHours(age)) return true;
                  }
              }
  
              return false;
          }
  
          private static void IgnoreManagedInternal(object i, ref ArrayList ignoreList)
          {
              // ignore valid internalized commodity deed items
              if (i is CommodityDeed deed && deed.Commodity != null && deed.Commodity.Map == Map.Internal)
              {
                  ignoreList.Add(deed.Commodity);
              }
  
              // ignore valid internalized keyring keys
              if (i is KeyRing keyring && keyring.Keys != null)
              {
                  foreach (Key k in keyring.Keys)
                  {
                      ignoreList.Add(k);
                  }
              }
  
              // ignore valid internalized relocatable house items
              if (i is BaseHouse house)
              {
                  foreach (RelocatedEntity relEntity in house.RelocatedEntities)
                  {
                      if (relEntity.Entity is Item)
                          ignoreList.Add(relEntity.Entity);
                  }
  
                  foreach (VendorInventory inventory in house.VendorInventories)
                  {
                      foreach (Item subItem in inventory.Items)
                          ignoreList.Add(subItem);
                  }
              }
          }
  
          // test for valid items/mobs on the internal map
          private static bool TestValidInternal(object o)
          {
              if (o is Mobile m)
              {
                  if (m.Map != Map.Internal || m.Account != null ||
                      ((m as IMount)?.Rider != null) ||
                      (m is GalleonPilot) || m is PetParrot ||
                      (GenericBuyInfo.IsDisplayCache(m)) ||
                      (m is EffectMobile) ||
                      (m is BaseCreature creature && creature.IsStabled) ||
                      (m is PlayerVendor && BaseHouse.AllHouses.Any(x => x.InternalizedVendors.Contains(m))))
                      return true;
              }
              else if (o is Item i)
              {
                  // note, in order to test for a vendors display container that contains valid internal map items 
                  if (i.Map != Map.Internal || i.Parent != null || i is Fists || i is MountItem || i is EffectItem || i.HeldBy != null ||
                      i is MovingCrate || i is SpawnPersistence || GenericBuyInfo.IsDisplayCache(i) || i.GetType().DeclaringType == typeof(GenericBuyInfo))
                      return true;
  
                  // boat stuffs
                  if (i is Static && i.Name != null && (i.Name.ToLower() == "weapon pad" || i.Name.ToLower() == "deck"))
                      return true;
                  if (i is GalleonHold || i is MooringLine || i is HoldItem || i is BaseDockedBoat || i is Rudder || i is RudderHandle || i is ShipWheel || i is BaseBoat || i is Plank || i is TillerMan || i is Hold || i is IShipCannon || i is DeckItem || i is WeaponPad)
                      return true;
  
                  // Ignores shadowguard addons that are internalized while not in use
                  if (i is AddonComponent component)
                  {
                      BaseAddon addon = component.Addon;
  
                      if (addon != null && (addon is ArmoryAddon || addon is BarAddon || addon is BelfryAddon || addon is ShadowguardFountainAddon || addon is OrchardAddon
                                            || addon is CastleAddon))
                          return true;
                  }
  
                  if (i is BaseAddon && (i is ArmoryAddon || i is BarAddon || i is BelfryAddon || i is ShadowguardFountainAddon || i is OrchardAddon
                                         || i is CastleAddon))
                      return true;
  
                  if (i is BoatMountItem || i is Misc.TreasuresOfTokunoPersistence || i is StealableArtifactsSpawner)
                      return true;
  
                  if (i is ArisenController)
                      return true;
              }
  
              return false;
          }
  
          public static ArrayList Search(SearchCriteria criteria, out string status_str)
          {
              status_str = null;
              ArrayList newarray = new ArrayList();
              ArrayList ignoreList = new ArrayList();
  
              if (criteria == null)
              {
                  status_str = "Empty search criteria";
                  return newarray;
              }
  
              Type targetType = null;
  
              Map tokunomap = null;
              try
              {
                  tokunomap = Map.Parse("Tokuno");
              }
              catch (Exception e) { Diagnostics.ExceptionLogging.LogException(e); }
  
              // if the type is specified then get the search type
              if (criteria.Dosearchtype && criteria.Searchtype != null)
              {
                  targetType = SpawnerType.GetType(criteria.Searchtype);
                  if (targetType == null)
                  {
                      status_str = "Invalid type: " + criteria.Searchtype;
                      return newarray;
                  }
              }
  
              // do the search through items
  
              // make a copy so that we dont get enumeration errors if World.Items.Values changes while searching
              ArrayList itemarray = null;
  
              ICollection itemvalues = World.Items.Values;
  
              lock (itemvalues.SyncRoot)
              {
                  try
                  {
                      itemarray = new ArrayList(itemvalues);
                  }
                  catch (SystemException e) { status_str = "Unable to search World.Items: " + e.Message; }
              }
  
              if (itemarray != null)
              {
                  foreach (Item i in itemarray)
                  {
                      bool hastype = false;
                      bool hasname = false;
                      bool hasentry = false;
                      bool hascondition = false;
                      bool hasrange = false;
                      bool hasregion = false;
                      bool hasmap = false;
                      bool hasspawnerr = false;
                      bool hasvalidhidden = false;
  
  
                      if (i == null || i.Deleted) continue;
  
                      // this will deal with items that are not on the internal map but hold valid internal items
                      if (criteria.Dohidevalidint && i.Map != Map.Internal && i.Map != null)
                      {
                          IgnoreManagedInternal(i, ref ignoreList);
                      }
  
                      // check for map
                      if ((i.Map == Map.Felucca && criteria.Dosearchfel) || (i.Map == Map.Trammel && criteria.Dosearchtram) ||
                          (i.Map == Map.Malas && criteria.Dosearchmal) || (i.Map == Map.Ilshenar && criteria.Dosearchilsh) ||
                          (i.Map == Map.TerMur && criteria.Dosearchter) || (i.Map == Map.Internal && criteria.Dosearchint) ||
                          (i.Map == null && criteria.Dosearchnull))
                      {
                          hasmap = true;
                      }
  
                      if (tokunomap != null && i.Map == tokunomap && criteria.Dosearchtok)
                      {
                          hasmap = true;
                      }
  
                      if (!hasmap)
                          continue;
  
                      // check for type
                      if (criteria.Dosearchtype && (i.GetType().IsSubclassOf(targetType) || i.GetType() == targetType))
                      {
                          hastype = true;
                      }
                      if (criteria.Dosearchtype && !hastype) continue;
  
                      // check for name
                      if (criteria.Dosearchname && (i.Name != null) && (criteria.Searchname != null) && (i.Name.ToLower().IndexOf(criteria.Searchname.ToLower()) >= 0))
                      {
                          hasname = true;
                      }
                      if (criteria.Dosearchname && !hasname) continue;
  
                      // check for valid internal map items
                      if (criteria.Dohidevalidint && TestValidInternal(i))
                      {
                          hasvalidhidden = true;
  
                          // this will deal with items that are on the internal map and hold valid internal items
                          IgnoreManagedInternal(i, ref ignoreList);
                      }
                      if (criteria.Dohidevalidint && hasvalidhidden) continue;
  
                      // check for range
                      if (criteria.Dosearchrange && TestRange(i, criteria.Searchrange, criteria.Currentmap, criteria.Currentloc))
                      {
                          hasrange = true;
                      }
                      if (criteria.Dosearchrange && !hasrange) continue;
  
                      // check for region
                      if (criteria.Dosearchregion && TestRegion(i, criteria.Searchregion))
                      {
                          hasregion = true;
                      }
                      if (criteria.Dosearchregion && !hasregion) continue;
  
                      // check for condition
                      if (criteria.Dosearchcondition && (criteria.Searchcondition != null))
                      {
                          // check the property test
                          hascondition = BaseXmlSpawner.CheckPropertyString(null, i, criteria.Searchcondition, out status_str);
                      }
                      if (criteria.Dosearchcondition && !hascondition) continue;
  
                      // check for entry
                      if (criteria.Dosearchspawnentry)
                      {
                          Type targetentrytype = null;
  
                          if (criteria.Dosearchspawntype)
                          {
                              targetentrytype = SpawnerType.GetType(criteria.Searchspawnentry.ToLower());
                          }
  
                          if (criteria.Searchspawnentry == null || (targetentrytype == null && criteria.Dosearchspawntype))
                          {
                              hasentry = false;
                          }
                          else
                          {
                              // see what kind of spawner it is
                              if (i is XmlSpawner spawner)
                              {
  
                                  // search the entries of the spawner
                                  foreach (XmlSpawner.SpawnObject so in spawner.m_SpawnObjects)
                                  {
                                      if (criteria.Dosearchspawntype)
                                      {
                                          // search by entry type
                                          Type type = null;
  
                                          if (so.TypeName != null)
                                          {
                                              string[] args = so.TypeName.Split('/');
                                              string typestr = null;
                                              if (args != null && args.Length > 0)
                                              {
                                                  typestr = args[0];
                                              }
  
                                              type = SpawnerType.GetType(typestr);
                                          }
  
                                          if (type != null && (type == targetentrytype || type.IsSubclassOf(targetentrytype)))
                                          {
                                              hasentry = true;
                                              break;
                                          }
                                      }
                                      else
                                      {
                                          // search by entry string
                                          if (so.TypeName != null && so.TypeName.ToLower().IndexOf(criteria.Searchspawnentry.ToLower()) >= 0)
                                          {
                                              hasentry = true;
                                              break;
                                          }
                                      }
                                  }
                              }
                              else if (i is Spawner spawner1)
                              {
                                  // search the entries of the spawner
                                  foreach (SpawnObject obj in spawner1.SpawnObjects)
                                  {
                                      string so = obj.SpawnName;
  
                                      if (criteria.Dosearchspawntype)
                                      {
                                          // search by entry type
                                          Type type = null;
  
                                          if (so != null)
                                          {
                                              type = SpawnerType.GetType(so);
                                          }
  
                                          if (type != null && (type == targetentrytype || type.IsSubclassOf(targetentrytype)))
                                          {
                                              hasentry = true;
                                              break;
                                          }
                                      }
                                      else
                                      {
                                          if (so != null && so.ToLower().IndexOf(criteria.Searchspawnentry.ToLower()) >= 0)
                                          {
                                              hasentry = true;
                                              break;
                                          }
                                      }
                                  }
                              }
                              else
                              {
                                  hasentry = false;
                              }
                          }
                      }
  
                      if (criteria.Dosearchspawnentry && !hasentry)
                          continue;
  
                      if (criteria.Dosearcherr && i is XmlSpawner hasSpawn && hasSpawn.status_str != null)
                      {
                          hasspawnerr = true;
                      }
  
                      if (criteria.Dosearcherr && !hasspawnerr)
                          continue;
  
                      // satisfied all conditions so add it
                      newarray.Add(new SearchEntry(i));
                  }
              }
  
              // do the search through mobiles
              if (!criteria.Dosearcherr)
              {
                  // make a copy so that we dont get enumeration errors if World.Mobiles.Values changes while searching
                  ArrayList mobilearray = null;
                  ICollection mobilevalues = World.Mobiles.Values;
                  lock (mobilevalues.SyncRoot)
                  {
                      try
                      {
                          mobilearray = new ArrayList(mobilevalues);
                      }
                      catch (SystemException e) { status_str = "Unable to search World.Mobiles: " + e.Message; }
                  }
  
                  if (mobilearray != null)
                  {
                      foreach (Mobile i in mobilearray)
                      {
                          bool hastype = false;
                          bool hasname = false;
                          bool hascondition = false;
                          bool hasrange = false;
                          bool hasregion = false;
                          bool hasmap = false;
                          bool hasage = false;
                          bool hasvalidhidden = false;
  
                          if (i == null || i.Deleted) continue;
  
                          // check for map
                          if ((i.Map == Map.Felucca && criteria.Dosearchfel) || (i.Map == Map.Trammel && criteria.Dosearchtram) ||
                              (i.Map == Map.Malas && criteria.Dosearchmal) || (i.Map == Map.Ilshenar && criteria.Dosearchilsh) ||
                              (i.Map == Map.TerMur && criteria.Dosearchter) || (i.Map == Map.Internal && criteria.Dosearchint) ||
                              (i.Map == null && criteria.Dosearchnull))
                          {
                              hasmap = true;
                          }
  
                          if (tokunomap != null && i.Map == tokunomap && criteria.Dosearchtok)
                          {
                              hasmap = true;
                          }
  
                          if (!hasmap) continue;
  
                          // check for range
                          if (criteria.Dosearchrange && TestRange(i, criteria.Searchrange, criteria.Currentmap, criteria.Currentloc))
                          {
                              hasrange = true;
                          }
                          if (criteria.Dosearchrange && !hasrange) continue;
  
                          // check for region
                          if (criteria.Dosearchregion && TestRegion(i, criteria.Searchregion))
                          {
                              hasregion = true;
                          }
                          if (criteria.Dosearchregion && !hasregion) continue;
  
                          // check for valid internal map mobiles
                          if (criteria.Dohidevalidint && TestValidInternal(i))
                          {
                              hasvalidhidden = true;
                          }
                          if (criteria.Dohidevalidint && hasvalidhidden) continue;
  
                          // check for age
                          if (criteria.Dosearchage && TestAge(i, criteria.Searchage, criteria.Searchagedirection))
                          {
                              hasage = true;
                          }
                          if (criteria.Dosearchage && !hasage) continue;
  
                          // check for type
                          if (criteria.Dosearchtype && (i.GetType().IsSubclassOf(targetType) || i.GetType() == targetType))
                          {
                              hastype = true;
                          }
                          if (criteria.Dosearchtype && !hastype) continue;
  
                          // check for name
                          if (criteria.Dosearchname && (i.Name != null) && (criteria.Searchname != null) && (i.Name.ToLower().IndexOf(criteria.Searchname.ToLower()) >= 0))
                          {
                              hasname = true;
                          }
                          if (criteria.Dosearchname && !hasname) continue;
  
                          // check for condition
                          if (criteria.Dosearchcondition && (criteria.Searchcondition != null))
                          {
                              // check the property test
                              hascondition = BaseXmlSpawner.CheckPropertyString(null, i, criteria.Searchcondition, out status_str);
                          }
                          if (criteria.Dosearchcondition && !hascondition) continue;
  
                          // passed all conditions so add it to the list
  
                          newarray.Add(new SearchEntry(i));
                      }
                  }
              }
  
              ArrayList removelist = new ArrayList();
              for (int i = 0; i < ignoreList.Count; ++i)
              {
                  foreach (SearchEntry se in newarray)
                  {
                      if (se.Object == ignoreList[i])
                      {
                          removelist.Add(se);
                          break;
                      }
                  }
              }
  
              foreach (SearchEntry se in removelist)
              {
                  newarray.Remove(se);
              }
  
              return newarray;
          }
  
          [Usage("XmlFind [objecttype] [range]")]
          [Description("Finds objects in the world")]
          public static void XmlFind_OnCommand(CommandEventArgs e)
          {
              if (e?.Mobile == null)
                  return;
  
              Account acct = e.Mobile.Account as Account;
              int x = 0;
              int y = 0;
              XmlSpawnerDefaults.DefaultEntry defs = null;
              if (acct != null)
                  defs = XmlSpawnerDefaults.GetDefaults(acct.ToString(), e.Mobile.Name);
  
              if (defs != null)
              {
                  x = defs.FindGumpX;
                  y = defs.FindGumpY;
              }
  
              string typename = "Xmlspawner";
              int range = -1;
              bool dorange = false;
  
              if (e.Arguments.Length > 0)
              {
                  typename = e.Arguments[0];
              }
  
              if (e.Arguments.Length > 1)
              {
                  dorange = true;
                  try
                  {
                      range = int.Parse(e.Arguments[1]);
                  }
                  catch
                  {
                      dorange = false;
                      e.Mobile.SendMessage("Invalid range argument {0}", e.Arguments[1]);
                  }
              }
  
              e.Mobile.SendGump(new XmlFindGump(e.Mobile, e.Mobile.Location, e.Mobile.Map, typename, range, dorange, x, y));
          }
  
          public XmlFindGump(Mobile from, Point3D startloc, Map startmap, int x, int y)
              : this(from, startloc, startmap, null, x, y)
          {
          }
  
          public XmlFindGump(Mobile from, Point3D startloc, Map startmap, string type, int x, int y)
              : this(from, startloc, startmap, type, -1, false, x, y)
          {
          }
  
          public XmlFindGump(Mobile from, Point3D startloc, Map startmap, string type, int range, bool dorange, int x, int y)
              : this(from, startloc, startmap, true, false, false,
  
              new SearchCriteria(
              true, // dotype
              false, // doname
              dorange, // dorange
              false, // doregion
              false, // doentry
              false, // doentrytype
              false, // docondition
              true, // dofel
              true, // dotram
              true, // domal
              true, // doilsh
              true, // dotok
              true, // doter
              false, // doint
              false, // donull
              false, // doerr
              false, // doage
              false, // dohidevalid
              true, // agedirection
              0, // age
              range, // range
              null, // region
              null, // condition
              type, // type
              null, // name
              null // entry 
              ),
  
              null, -1, 0, null, null,
              false, false, false, false, false, false, x, y)
          {
          }
  
  
          public XmlFindGump(Mobile from, Point3D startloc, Map startmap, bool firststart, bool extension, bool descend, SearchCriteria criteria, ArrayList searchlist, int selected, int displayfrom, string savefilename,
              string commandstring, bool sorttype, bool sortname, bool sortrange, bool sortmap, bool sortselect, bool selectall, int X, int Y)
              : base(X, Y)
          {
  
              StartingMap = startmap;
              StartingLoc = startloc;
              if (from != null && !from.Deleted)
              {
                  m_From = from;
                  if (firststart)
                  {
                      StartingMap = from.Map;
                      StartingLoc = from.Location;
                  }
              }
  
              SaveFilename = savefilename;
              CommandString = commandstring;
              SelectAll = selectall;
              Sorttype = sorttype;
              Sortname = sortname;
              Sortrange = sortrange;
              Sortmap = sortmap;
              Sortselect = sortselect;
              DisplayFrom = displayfrom;
              Selected = selected;
              m_ShowExtension = extension;
              Descendingsort = descend;
  
              m_SearchCriteria = criteria ?? new SearchCriteria();
  
              m_SearchList = searchlist;
  
              // prepare the page
              const int height = 500;
  
              AddPage(0);
              if (m_ShowExtension)
              {
                  AddBackground(0, 0, 755, height, 5054);
                  AddAlphaRegion(0, 0, 755, height);
              }
              else
              {
                  AddBackground(0, 0, 170, height, 5054);
                  AddAlphaRegion(0, 0, 170, height);
              }
  
  
              // ----------------
              // SORT section
              // ----------------
              int y = 5;
              // add the Sort button
              AddButton(5, y, 0xFAB, 0xFAD, 700, GumpButtonType.Reply, 0);
              AddLabel(38, y, 0x384, "Sort");
  
              // add the sort direction button
              if (Descendingsort)
              {
                  AddButton(75, y + 3, 0x15E2, 0x15E6, 701, GumpButtonType.Reply, 0);
                  AddLabel(95, y, 0x384, "descend");
              }
              else
              {
                  AddButton(75, y + 3, 0x15E0, 0x15E4, 701, GumpButtonType.Reply, 0);
                  AddLabel(95, y, 0x384, "ascend");
              }
              y += 22;
              // add the Sort on type toggle
              AddRadio(5, y, 0xD2, 0xD3, Sorttype, 0);
              AddLabel(28, y, 0x384, "type");
  
              // add the Sort on name toggle
              AddRadio(75, y, 0xD2, 0xD3, Sortname, 1);
              AddLabel(98, y, 0x384, "name");
  
              y += 20;
              // add the Sort on range toggle
              AddRadio(5, y, 0xD2, 0xD3, Sortrange, 2);
              AddLabel(28, y, 0x384, "range");
  
              // add the Sort on map toggle
              AddRadio(75, y, 0xD2, 0xD3, Sortmap, 4);
              AddLabel(98, y, 0x384, "map");
  
              y += 20;
              // add the Sort on selected toggle
              AddRadio(5, y, 0xD2, 0xD3, Sortselect, 5);
              AddLabel(28, y, 0x384, "select");
  
              // ----------------
              // SEARCH section
              // ----------------
              y = 85;
              // add the Search button
              AddButton(5, y, 0xFA8, 0xFAA, 3, GumpButtonType.Reply, 0);
              AddLabel(38, y, 0x384, "Search");
  
              y += 20;
              // add the map buttons
              AddCheck(5, y, 0xD2, 0xD3, m_SearchCriteria.Dosearchint, 312);
              AddLabel(28, y, 0x384, "Int");
              AddCheck(75, y, 0xD2, 0xD3, m_SearchCriteria.Dosearchnull, 314);
              AddLabel(98, y, 0x384, "Null");
  
              y += 20;
              AddCheck(5, y, 0xD2, 0xD3, m_SearchCriteria.Dosearchfel, 308);
              AddLabel(28, y, 0x384, "Fel");
              AddCheck(75, y, 0xD2, 0xD3, m_SearchCriteria.Dosearchtram, 309);
              AddLabel(98, y, 0x384, "Tram");
  
              y += 20;
              AddCheck(5, y, 0xD2, 0xD3, m_SearchCriteria.Dosearchmal, 310);
              AddLabel(28, y, 0x384, "Mal");
              AddCheck(75, y, 0xD2, 0xD3, m_SearchCriteria.Dosearchilsh, 311);
              AddLabel(98, y, 0x384, "Ilsh");
  
              y += 20;
              AddCheck(5, y, 0xD2, 0xD3, m_SearchCriteria.Dosearchtok, 318);
              AddLabel(28, y, 0x384, "Tok");
              AddCheck(75, y, 0xD2, 0xD3, m_SearchCriteria.Dosearchter, 320);
              AddLabel(98, y, 0x384, "Ter");
  
              y += 20;
              // add the hide valid internal map button
              AddCheck(5, y, 0xD2, 0xD3, m_SearchCriteria.Dohidevalidint, 316);
              AddLabel(28, y, 0x384, "Hide valid internal");
  
              // ----------------
              // FILTER section
              // ----------------
              y = height - 295;
  
              // add the search region entry
              AddLabel(28, y, 0x384, "region");
              AddImageTiled(70, y, 68, 19, 0xBBC);
              AddTextEntry(70, y, 250, 19, 0, 106, m_SearchCriteria.Searchregion);
              // add the toggle to enable search region
              AddCheck(5, y, 0xD2, 0xD3, m_SearchCriteria.Dosearchregion, 319);
  
              y += 20;
              // add the search age entry
              AddLabel(28, y, 0x384, "age");
              //AddImageTiled( 80, 220, 50, 23, 0x52 );
              AddImageTiled(70, y, 45, 19, 0xBBC);
              AddTextEntry(70, y, 45, 19, 0, 105, m_SearchCriteria.Searchage.ToString());
              AddLabel(117, y, 0x384, "Hrs");
              // add the toggle to enable search age
              AddCheck(5, y, 0xD2, 0xD3, m_SearchCriteria.Dosearchage, 303);
              // add the toggle to set the search age test direction
              AddCheck(50, y + 2, 0x1467, 0x1468, m_SearchCriteria.Searchagedirection, 302);
  
              y += 20;
              // add the search range entry
              AddLabel(28, y, 0x384, "range");
              AddImageTiled(70, y, 45, 19, 0xBBC);
              AddTextEntry(70, y, 45, 19, 0, 100, m_SearchCriteria.Searchrange.ToString());
              // add the toggle to enable search range
              AddCheck(5, y, 0xD2, 0xD3, m_SearchCriteria.Dosearchrange, 304);
  
              y += 20;
              // add the search type entry
              AddLabel(28, y, 0x384, "type");
              // add the toggle to enable search by type
              AddCheck(5, y, 0xD2, 0xD3, m_SearchCriteria.Dosearchtype, 305);
              //AddImageTiled( 5, 285, 135, 23, 0x52 );
              AddImageTiled(6, y + 20, 132, 19, 0xBBC);
              AddTextEntry(6, y + 20, 250, 19, 0, 101, m_SearchCriteria.Searchtype);
  
              y += 41;
              // add the search condition entry
              AddLabel(28, y, 0x384, "property test");
              // add the toggle to enable search by condition
              AddCheck(5, y, 0xD2, 0xD3, m_SearchCriteria.Dosearchcondition, 315);
              //AddImageTiled( 5, 285, 135, 23, 0x52 );
              AddImageTiled(6, y + 20, 132, 19, 0xBBC);
              AddTextEntry(6, y + 20, 500, 19, 0, 104, m_SearchCriteria.Searchcondition);
  
              y += 41;
              // add the search name entry
              AddLabel(28, y, 0x384, "name");
              // add the toggle to enable search by name
              AddCheck(5, y, 0xD2, 0xD3, m_SearchCriteria.Dosearchname, 306);
              //AddImageTiled( 5, 350, 135, 23, 0x52 );
              AddImageTiled(6, y + 20, 132, 19, 0xBBC);
              AddTextEntry(6, y + 20, 250, 19, 0, 102, m_SearchCriteria.Searchname);
  
              y += 41;
              // add the search spawner entries
              AddLabel(28, y, 0x384, "entry");
              // add the toggle to enable search spawner entries
              AddCheck(5, y, 0xD2, 0xD3, m_SearchCriteria.Dosearchspawnentry, 307);
  
              // add the search spawner entries by type
              AddLabel(88, y, 0x384, "type");
              // add the toggle to enable search spawner entry types
              AddCheck(65, y, 0xD2, 0xD3, m_SearchCriteria.Dosearchspawntype, 326);
  
              //AddImageTiled( 5, 415, 135, 23, 0x52 );
              AddImageTiled(6, y + 20, 132, 19, 0xBBC);
              AddTextEntry(6, y + 20, 250, 19, 0, 103, m_SearchCriteria.Searchspawnentry);
  
              // add the search spawner errors
              AddLabel(140, y, 0x384, "err");
              // add the toggle to enable search spawner entries
              AddCheck(117, y, 0xD2, 0xD3, m_SearchCriteria.Dosearcherr, 313);
  
              // add the Show Map button
              //AddButton( 5, 450, 0xFAB, 0xFAD, 150, GumpButtonType.Reply, 0 );
              //AddLabel( 38, 450, 0x384, "Map" );
  
              // ----------------
              // CONTROL section
              // ----------------
  
              y = height - 25;
              // add the Return button
              AddButton(72, y, 0xFAE, 0xFAF, 155, GumpButtonType.Reply, 0);
              AddLabel(105, y, 0x384, "Return");
  
              y = height - 25;
              // add the Bring button
              AddButton(5, y, 0xFAE, 0xFAF, 154, GumpButtonType.Reply, 0);
              AddLabel(38, y, 0x384, "Bring");
  
  
              // add gump extension button
              if (m_ShowExtension)
              {
                  AddButton(720, y + 5, 0x15E3, 0x15E7, 200, GumpButtonType.Reply, 0);
              }
              else
              {
                  AddButton(150, y + 5, 0x15E1, 0x15E5, 200, GumpButtonType.Reply, 0);
              }
  
              if (m_ShowExtension)
              {
                  AddLabel(143, 5, 0x384, "Gump");
                  AddLabel(178, 5, 0x384, "Prop");
                  AddLabel(210, 5, 0x384, "Goto");
  
gt:                 AddLabel(250, 5, 0x384, "Name");