context: using kOS.Safe.Compilation.KS;
  using kOS.Safe.Encapsulation;
  using kOS.Safe.Encapsulation.Suffixes;
  using kOS.Safe.Exceptions;
  using kOS.Safe.Execution;
  using kOS.Safe.Utilities;
  using System;
  using System.IO;
  using System.Collections.Generic;
  using PreFlightTests;
  using System.Linq;
  
  namespace kOS.Suffixed
  {
      [kOS.Safe.Utilities.KOSNomenclature("Kuniverse")]
      public class KUniverseValue : Structure
      {
          private readonly SharedObjects shared;
  
          public KUniverseValue(SharedObjects shared)
          {
              this.shared = shared;
              InitializeSuffixes();
          }
  
          public void InitializeSuffixes()
          {
              AddSuffix("CANREVERT", new Suffix<BooleanValue>(CanRevert));
              AddSuffix("CANREVERTTOLAUNCH", new Suffix<BooleanValue>(CanRevertToLaunch));
              AddSuffix("CANREVERTTOEDITOR", new Suffix<BooleanValue>(CanRevvertToEditor));
              AddSuffix("REVERTTOLAUNCH", new NoArgsVoidSuffix(RevertToLaunch));
              AddSuffix("REVERTTOEDITOR", new NoArgsVoidSuffix(RevertToEditor));
              AddSuffix("REVERTTO", new OneArgsSuffix<StringValue>(RevertTo));
              AddSuffix("CANQUICKSAVE", new Suffix<BooleanValue>(CanQuicksave));
              AddSuffix("PAUSE", new NoArgsVoidSuffix(PauseGame));
              AddSuffix("QUICKSAVE", new NoArgsVoidSuffix(QuickSave));
              AddSuffix("QUICKLOAD", new NoArgsVoidSuffix(QuickLoad));
              AddSuffix("QUICKSAVETO", new OneArgsSuffix<StringValue>(QuickSaveTo));
              AddSuffix("QUICKLOADFROM", new OneArgsSuffix<StringValue>(QuickLoadFrom));
              AddSuffix("QUICKSAVELIST", new Suffix<ListValue>(GetQuicksaveList));
              AddSuffix("ORIGINEDITOR", new Suffix<StringValue>(OriginatingEditor));
              AddSuffix("DEFAULTLOADDISTANCE", new Suffix<LoadDistanceValue>(() => new LoadDistanceValue(PhysicsGlobals.Instance.VesselRangesDefault)));
              AddSuffix("ACTIVEVESSEL", new SetSuffix<VesselTarget>(() => VesselTarget.CreateOrGetExisting(FlightGlobals.ActiveVessel, shared), SetActiveVessel));
              AddSuffix(new string[] { "FORCESETACTIVEVESSEL", "FORCEACTIVE" }, new OneArgsSuffix<VesselTarget>(ForceSetActiveVessel));
              AddSuffix("HOURSPERDAY", new Suffix<ScalarValue>(GetHoursPerDay));
              AddSuffix("DEBUGLOG", new OneArgsSuffix<StringValue>(DebugLog));
              AddSuffix("GETCRAFT", new TwoArgsSuffix<CraftTemplate, StringValue, StringValue>(GetCraft));
              AddSuffix("LAUNCHCRAFT", new OneArgsSuffix<CraftTemplate>(LaunchShip));
              AddSuffix("LAUNCHCRAFTFROM", new TwoArgsSuffix<CraftTemplate, StringValue>(LaunchShip));
              AddSuffix("LAUNCHCRAFTWITHCREWFROM", new ThreeArgsSuffix<CraftTemplate, ListValue, StringValue>(LaunchShip));
              AddSuffix("CRAFTLIST", new Suffix<ListValue>(CraftTemplate.GetAllTemplates));
              AddSuffix("SWITCHVESSELWATCHERS", new NoArgsSuffix<UniqueSetValue<UserDelegate>>(() => shared.DispatchManager.CurrentDispatcher.GetSwitchVesselNotifyees()));
              AddSuffix("TIMEWARP", new Suffix<TimeWarpValue>(() => TimeWarpValue.Instance));
              AddSuffix(new string[] { "REALWORLDTIME", "REALTIME" }, new Suffix<ScalarValue>(GetRealWorldTime));
          }
          
  
          public void RevertToLaunch()
          {
              if (CanRevertToLaunch())
              {
                  shared.Cpu.GetCurrentOpcode().AbortProgram = true;
                  FlightDriver.RevertToLaunch();
              }
              else throw new KOSCommandInvalidHereException(LineCol.Unknown(), "REVERTTOLAUNCH", "When revert is disabled", "When revert is enabled");
          }
  
          public void RevertToEditor()
          {
              if (CanRevvertToEditor())
              {
                  EditorFacility fac = ShipConstruction.ShipType;
                  shared.Cpu.GetCurrentOpcode().AbortProgram = true;
                  FlightDriver.RevertToPrelaunch(fac);
              }
              else throw new KOSCommandInvalidHereException(LineCol.Unknown(), "REVERTTOEDITOR", "When revert is disabled", "When revert is enabled");
          }
  
          public void RevertTo(StringValue editor)
          {
              if (CanRevvertToEditor())
              {
                  EditorFacility fac;
                  switch (editor.ToUpper())
                  {
                      case "VAB":
                          fac = EditorFacility.VAB;
                          break;
  
                      case "SPH":
                          fac = EditorFacility.SPH;
                          break;
  
                      default:
                          fac = EditorFacility.None;
                          break;
                  }
                  shared.Cpu.GetCurrentOpcode().AbortProgram = true;
                  FlightDriver.RevertToPrelaunch(fac);
              }
              else throw new KOSCommandInvalidHereException(LineCol.Unknown(), "REVERTTO", "When revert is disabled", "When revert is enabled");
          }
  
          public BooleanValue CanRevert()
          {
              return FlightDriver.CanRevert;
          }
  
          public BooleanValue CanRevertToLaunch()
          {
              return FlightDriver.CanRevertToPostInit && HighLogic.CurrentGame.Parameters.Flight.CanRestart;
          }
  
          public BooleanValue CanRevvertToEditor()
          {
              return FlightDriver.CanRevertToPrelaunch &&
                  HighLogic.CurrentGame.Parameters.Flight.CanLeaveToEditor &&
                  ShipConstruction.ShipConfig != null;
          }
  
          public StringValue OriginatingEditor()
          {
              if (ShipConstruction.ShipConfig != null)
              {
                  EditorFacility fac = ShipConstruction.ShipType;
                  return fac.ToString().ToUpper();
              }
              return "";
          }
  
          public void SetActiveVessel(VesselTarget vesselTarget)
          {
              Vessel vessel = vesselTarget.Vessel;
              if (!vessel.isActiveVessel)
              {
                  FlightGlobals.SetActiveVessel(vessel);
              }
          }
  
          public void ForceSetActiveVessel(VesselTarget vesselTarget)
          {
              Vessel vessel = vesselTarget.Vessel;
              if (!vessel.isActiveVessel)
              {
                  FlightGlobals.ForceSetActiveVessel(vessel);
              }
          }
  
          public ScalarValue GetHoursPerDay()
          {
              return KSPUtil.dateTimeFormatter.Day / KSPUtil.dateTimeFormatter.Hour;
          }
  
          public void DebugLog(StringValue message)
          {
              SafeHouse.Logger.Log("(KUNIVERSE:DEBUGLOG) " + message);
          }
  
          public BooleanValue CanQuicksave()
          {
              if (HighLogic.CurrentGame.Parameters.Flight.CanQuickSave && FlightGlobals.ClearToSave() == ClearToSaveStatus.CLEAR)
              {
                  return true;
              }
              return false;
          }
  
          public void PauseGame()
          {
              string textToHud = "kOS script has triggered a Pause Game.  Manual intervention needed to resume.";
              ScreenMessages.PostScreenMessage("<color=#ffff80><size=20>" + textToHud + "</size></color>", 10, ScreenMessageStyle.UPPER_CENTER);
  
              PauseMenu.Display();
  
              // It would be weird to execute the rest of the IPU's instructions when the script said to pause the game.
              // Presumably most users would expect calling Pause to make the script stop right there, not
              // like 3 or 4 lines later.  Therefore stop this FixedUpdate tick here:
              shared.Cpu.YieldProgram(new YieldFinishedNextTick());
          }
  
          public void QuickSave()
          {
              if (HighLogic.CurrentGame.Parameters.Flight.CanQuickSave)
              {
                  QuickSaveLoad.QuickSave();
              }
              else throw new KOSException("KSP prevents using quicksave currently.");
          }
  
          public void QuickSaveTo(StringValue name)
          {
              QuickSaveTo(name.ToString());
          }
  
          public void QuickSaveTo(string name)
          {
              if (name.EndsWith(".sfs"))
              {
                  name = name.Substring(0, name.Length - 4);
              }
              var reserved = new List<string>() { "persistent", "quicksave", "kos-backup-quicksave" };
              if (reserved.Contains(name))
              {
                  throw new KOSException("Cannot save to " + name + " because it is a reserved name.");
              }
              SaveGame(name);
          }
  
          private void SaveGame(string name)
          {
              if (HighLogic.CurrentGame.Parameters.Flight.CanQuickSave && FlightGlobals.ClearToSave() == ClearToSaveStatus.CLEAR)
              {
                  var game = HighLogic.CurrentGame.Updated();
                  game.startScene = GameScenes.FLIGHT;
                  GamePersistence.SaveGame(game, name, HighLogic.SaveFolder, SaveMode.OVERWRITE);
                  game.startScene = GameScenes.SPACECENTER;
              }
              else throw new KOSException("KSP prevents using quicksave currently.");
          }
  
          public void QuickLoad()
          {
              LoadGame("quicksave");
          }
  
          public void QuickLoadFrom(StringValue name)
          {
              LoadGame(name.ToString());
          }
  
          private void LoadGame(string name)
          {
              if (name.EndsWith(".sfs"))
              {
                  name = name.Substring(0, name.Length - 4);
              }
              if (HighLogic.CurrentGame.Parameters.Flight.CanQuickLoad)
              {
                  string filename = name + ".sfs";
                  string path = KSPUtil.GetOrCreatePath("saves/" + HighLogic.SaveFolder);
  
gt:                 if (!File.Exists(Path.Combine(path, filename)))