context: using Server.Guilds;
  using Server.Network;
  using System;
  using System.Collections.Generic;
  using System.Diagnostics;
  using System.IO;
  using System.Reflection;
  using System.Threading;
  
  namespace Server
  {
      public static class World
      {
          private static readonly bool m_Metrics = Config.Get("General.Metrics", false);
          private static readonly ManualResetEvent m_DiskWriteHandle = new ManualResetEvent(true);
  
          private static Queue<IEntity> _addQueue, _deleteQueue;
  
          public static bool Saving { get; private set; }
          public static bool Loaded { get; private set; }
          public static bool Loading { get; private set; }
  
          public readonly static string MobileIndexPath = Path.Combine("Saves/Mobiles/", "Mobiles.idx");
          public readonly static string MobileTypesPath = Path.Combine("Saves/Mobiles/", "Mobiles.tdb");
          public readonly static string MobileDataPath = Path.Combine("Saves/Mobiles/", "Mobiles.bin");
  
          public readonly static string ItemIndexPath = Path.Combine("Saves/Items/", "Items.idx");
          public readonly static string ItemTypesPath = Path.Combine("Saves/Items/", "Items.tdb");
          public readonly static string ItemDataPath = Path.Combine("Saves/Items/", "Items.bin");
  
          public readonly static string GuildIndexPath = Path.Combine("Saves/Guilds/", "Guilds.idx");
          public readonly static string GuildDataPath = Path.Combine("Saves/Guilds/", "Guilds.bin");
  
          public static void NotifyDiskWriteComplete()
          {
              if (m_DiskWriteHandle.Set())
              {
                  Console.WriteLine("Closing Save Files. ");
              }
          }
  
          public static void WaitForWriteCompletion()
          {
              m_DiskWriteHandle.WaitOne();
          }
  
          public static Dictionary<Serial, Mobile> Mobiles { get; private set; }
  
          public static Dictionary<Serial, Item> Items { get; private set; }
  
          public static bool OnDelete(IEntity entity)
          {
              if (Saving || Loading)
              {
                  if (Saving)
                  {
                      AppendSafetyLog("delete", entity);
                  }
  
                  _deleteQueue.Enqueue(entity);
  
                  return false;
              }
  
              return true;
          }
  
          public static void Broadcast(int hue, bool ascii, string text)
          {
              Broadcast(hue, ascii, AccessLevel.Player, text);
          }
  
          public static void Broadcast(int hue, bool ascii, AccessLevel access, string text)
          {
              WorldBroadcastEventArgs e = new WorldBroadcastEventArgs(hue, ascii, access, text);
  
              EventSink.InvokeWorldBroadcast(e);
  
              hue = e.Hue;
              ascii = e.Ascii;
              text = e.Text;
              access = e.Access;
  
              if (String.IsNullOrWhiteSpace(text))
              {
                  return;
              }
  
              Packet p;
  
              if (ascii)
              {
                  p = new AsciiMessage(Serial.MinusOne, -1, MessageType.Regular, hue, 3, "System", text);
              }
              else
              {
                  p = new UnicodeMessage(Serial.MinusOne, -1, MessageType.Regular, hue, 3, "ENU", "System", text);
              }
  
              List<NetState> list = NetState.Instances;
  
              p.Acquire();
  
              foreach (NetState s in list)
              {
                  if (s.Mobile != null && s.Mobile.AccessLevel >= access)
                  {
                      s.Send(p);
                  }
              }
  
              p.Release();
  
              NetState.FlushAll();
          }
  
          public static void Broadcast(int hue, bool ascii, string format, params object[] args)
          {
              Broadcast(hue, ascii, AccessLevel.Player, format, args);
          }
  
          public static void Broadcast(int hue, bool ascii, AccessLevel access, string format, params object[] args)
          {
              Broadcast(hue, ascii, access, String.Format(format, args));
          }
  
          private interface IEntityEntry
          {
              Serial Serial { get; }
              int TypeID { get; }
              long Position { get; }
              int Length { get; }
          }
  
          private sealed class GuildEntry : IEntityEntry
          {
              public BaseGuild Guild { get; }
  
              public Serial Serial => Guild == null ? 0 : Guild.Id;
  
              public int TypeID => 0;
  
              public long Position { get; }
  
              public int Length { get; }
  
              public GuildEntry(BaseGuild g, long pos, int length)
              {
                  Guild = g;
                  Position = pos;
                  Length = length;
              }
          }
  
          private sealed class ItemEntry : IEntityEntry
          {
              public Item Item { get; }
  
              public Serial Serial => Item == null ? Serial.MinusOne : Item.Serial;
  
              public int TypeID { get; }
  
              public string TypeName { get; }
  
              public long Position { get; }
  
              public int Length { get; }
  
              public ItemEntry(Item item, int typeID, string typeName, long pos, int length)
              {
                  Item = item;
                  TypeID = typeID;
                  TypeName = typeName;
                  Position = pos;
                  Length = length;
              }
          }
  
          private sealed class MobileEntry : IEntityEntry
          {
              public Mobile Mobile { get; }
  
              public Serial Serial => Mobile == null ? Serial.MinusOne : Mobile.Serial;
  
              public int TypeID { get; }
  
              public string TypeName { get; }
  
              public long Position { get; }
  
              public int Length { get; }
  
              public MobileEntry(Mobile mobile, int typeID, string typeName, long pos, int length)
              {
                  Mobile = mobile;
                  TypeID = typeID;
                  TypeName = typeName;
                  Position = pos;
                  Length = length;
              }
          }
  
          public static string LoadingType { get; private set; }
  
          private static readonly Type[] m_SerialTypeArray = new Type[1] { typeof(Serial) };
  
          private static List<Tuple<ConstructorInfo, string>> ReadTypes(BinaryReader tdbReader)
          {
              int count = tdbReader.ReadInt32();
  
              List<Tuple<ConstructorInfo, string>> types = new List<Tuple<ConstructorInfo, string>>(count);
  
              for (int i = 0; i < count; ++i)
              {
                  string typeName = tdbReader.ReadString();
  
                  Type t = ScriptCompiler.FindTypeByFullName(typeName);
  
                  if (t == null)
                  {
                      Console.WriteLine("failed");
  
                      if (!Core.Service)
                      {
                          Console.WriteLine("Error: Type '{0}' was not found. Delete all of those types? (y/n)", typeName);
  
                          if (Console.ReadKey(true).Key == ConsoleKey.Y)
                          {
                              types.Add(null);
                              Utility.PushColor(ConsoleColor.Yellow);
                              Console.Write("World: Loading...");
                              Utility.PopColor();
                              continue;
                          }
  
                          Console.WriteLine("Types will not be deleted. An exception will be thrown.");
                      }
                      else
                      {
                          Console.WriteLine("Error: Type '{0}' was not found.", typeName);
                      }
  
                      throw new Exception(String.Format("Bad type '{0}'", typeName));
                  }
  
                  ConstructorInfo ctor = t.GetConstructor(m_SerialTypeArray);
  
                  if (ctor != null)
                  {
                      types.Add(new Tuple<ConstructorInfo, string>(ctor, typeName));
                  }
                  else
                  {
                      throw new Exception(String.Format("Type '{0}' does not have a serialization constructor", t));
                  }
              }
  
              return types;
          }
  
          public static void Load()
          {
              if (Loaded)
              {
                  return;
              }
  
              Loaded = true;
              LoadingType = null;
  
              Utility.PushColor(ConsoleColor.Yellow);
              Console.WriteLine("World: Loading...");
              Utility.PopColor();
  
              Stopwatch watch = Stopwatch.StartNew();
  
              Loading = true;
  
              _addQueue = new Queue<IEntity>();
              _deleteQueue = new Queue<IEntity>();
              object[] ctorArgs = new object[1];
  
              List<ItemEntry> items = new List<ItemEntry>();
              List<MobileEntry> mobiles = new List<MobileEntry>();
              List<GuildEntry> guilds = new List<GuildEntry>();
  
              if (File.Exists(MobileIndexPath) && File.Exists(MobileTypesPath))
              {
                  using (FileStream idx = new FileStream(MobileIndexPath, FileMode.Open, FileAccess.Read, FileShare.Read))
                  {
                      BinaryReader idxReader = new BinaryReader(idx);
  
                      using (FileStream tdb = new FileStream(MobileTypesPath, FileMode.Open, FileAccess.Read, FileShare.Read))
                      {
                          BinaryReader tdbReader = new BinaryReader(tdb);
  
                          List<Tuple<ConstructorInfo, string>> types = ReadTypes(tdbReader);
  
                          int mobileCount = idxReader.ReadInt32();
                          Mobiles = new Dictionary<Serial, Mobile>(mobileCount);
  
                          for (int i = 0; i < mobileCount; ++i)
                          {
                              int typeID = idxReader.ReadInt32();
                              int serial = idxReader.ReadInt32();
                              long pos = idxReader.ReadInt64();
                              int length = idxReader.ReadInt32();
  
                              Tuple<ConstructorInfo, string> objs = types[typeID];
  
                              if (objs == null)
                              {
                                  continue;
                              }
  
                              Mobile m = null;
                              ConstructorInfo ctor = objs.Item1;
                              string typeName = objs.Item2;
  
                              try
                              {
                                  ctorArgs[0] = (Serial)serial;
                                  m = (Mobile)(ctor.Invoke(ctorArgs));
                              }
                              catch
                              { }
  
                              if (m != null)
                              {
                                  mobiles.Add(new MobileEntry(m, typeID, typeName, pos, length));
                                  AddMobile(m);
                              }
                          }
  
                          tdbReader.Close();
                      }
  
                      idxReader.Close();
                  }
              }
              else
              {
                  Mobiles = new Dictionary<Serial, Mobile>();
              }
  
              if (File.Exists(ItemIndexPath) && File.Exists(ItemTypesPath))
              {
                  using (FileStream idx = new FileStream(ItemIndexPath, FileMode.Open, FileAccess.Read, FileShare.Read))
                  {
                      BinaryReader idxReader = new BinaryReader(idx);
  
                      using (FileStream tdb = new FileStream(ItemTypesPath, FileMode.Open, FileAccess.Read, FileShare.Read))
                      {
                          BinaryReader tdbReader = new BinaryReader(tdb);
  
                          List<Tuple<ConstructorInfo, string>> types = ReadTypes(tdbReader);
  
                          int itemCount = idxReader.ReadInt32();
  
                          Items = new Dictionary<Serial, Item>(itemCount);
  
                          for (int i = 0; i < itemCount; ++i)
                          {
                              int typeID = idxReader.ReadInt32();
                              int serial = idxReader.ReadInt32();
                              long pos = idxReader.ReadInt64();
                              int length = idxReader.ReadInt32();
  
                              Tuple<ConstructorInfo, string> objs = types[typeID];
  
                              if (objs == null)
                              {
                                  continue;
                              }
  
                              Item item = null;
                              ConstructorInfo ctor = objs.Item1;
                              string typeName = objs.Item2;
  
                              try
                              {
                                  ctorArgs[0] = (Serial)serial;
                                  item = (Item)(ctor.Invoke(ctorArgs));
                              }
                              catch
                              { }
  
                              if (item != null)
                              {
                                  items.Add(new ItemEntry(item, typeID, typeName, pos, length));
                                  AddItem(item);
                              }
                          }
  
                          tdbReader.Close();
                      }
  
                      idxReader.Close();
                  }
              }
              else
              {
                  Items = new Dictionary<Serial, Item>();
              }
  
              if (File.Exists(GuildIndexPath))
              {
                  using (FileStream idx = new FileStream(GuildIndexPath, FileMode.Open, FileAccess.Read, FileShare.Read))
                  {
                      BinaryReader idxReader = new BinaryReader(idx);
  
                      int guildCount = idxReader.ReadInt32();
                      CreateGuildEventArgs createEventArgs = new CreateGuildEventArgs(-1);
                      for (int i = 0; i < guildCount; ++i)
                      {
  
gt:                         idxReader.ReadInt32(); //no typeid for guilds