gt:                     RENDER_FOLDER = (String) data[3];
context: package gaia.cu9.ari.gaiaorbit.util;
  
  import com.badlogic.gdx.controllers.Controller;
  import com.badlogic.gdx.controllers.ControllerListener;
  import com.badlogic.gdx.controllers.Controllers;
  import com.badlogic.gdx.files.FileHandle;
  import com.badlogic.gdx.math.MathUtils;
  import com.badlogic.gdx.utils.Array;
  import com.bitfire.postprocessing.effects.CubemapProjections.CubemapProjection;
  import gaia.cu9.ari.gaiaorbit.GaiaSky;
  import gaia.cu9.ari.gaiaorbit.desktop.util.camera.CameraKeyframeManager;
  import gaia.cu9.ari.gaiaorbit.event.EventManager;
  import gaia.cu9.ari.gaiaorbit.event.Events;
  import gaia.cu9.ari.gaiaorbit.event.IObserver;
  import gaia.cu9.ari.gaiaorbit.render.ComponentType;
  import gaia.cu9.ari.gaiaorbit.render.system.AbstractRenderSystem;
  import gaia.cu9.ari.gaiaorbit.util.Logger.Log;
  import gaia.cu9.ari.gaiaorbit.util.format.DateFormatFactory;
  import gaia.cu9.ari.gaiaorbit.util.format.DateFormatFactory.DateType;
  import gaia.cu9.ari.gaiaorbit.util.format.IDateFormat;
  import gaia.cu9.ari.gaiaorbit.util.math.MathUtilsd;
  
  import java.io.File;
  import java.nio.file.Path;
  import java.nio.file.Paths;
  import java.time.Instant;
  import java.util.ArrayList;
  import java.util.List;
  
  /**
   * Holds the global configuration options
   *
   * @author Toni Sagrista
   */
  public class GlobalConf {
      private static final Log logger = Logger.getLogger(GlobalConf.class);
  
      public static final String APPLICATION_NAME = "Gaia Sky";
      public static final String APPLICATION_SHORT_NAME = "gaiasky";
      public static final String WEBPAGE = "https://www.zah.uni-heidelberg.de/gaia/outreach/gaiasky";
      public static final String WEBPAGE_DOWNLOADS = "https://www.zah.uni-heidelberg.de/gaia/outreach/gaiasky/downloads";
      public static final String DOCUMENTATION = "http://gaia-sky.rtfd.io";
      public static final String ICON_URL = "https://github.com/langurmonkey/gaiasky/blob/master/assets/icon/gs_064.png?raw=true";
      public static final String AUTHOR_NAME = "Toni Sagrista Selles";
      public static final String AUTHOR_EMAIL = "tsagrista@ari.uni-heidelberg.de";
      public static final String AUTHOR_AFFILIATION = "Heidelberg University, Zentrum fuer Astronomie, Astronomisches Rechen-Institut";
  
      // Assets location for this instance of Gaia Sky
      public static final String ASSETS_LOC = System.getProperty("assets.location") != null ? System.getProperty("assets.location") : "";
  
      // Scale factor
      public static float SCALE_FACTOR = -1.0f;
  
      public static void updateScaleFactor(float sf) {
          SCALE_FACTOR = sf;
          logger.debug("GUI scale factor set to " + GlobalConf.SCALE_FACTOR);
      }
  
      public static interface IConf {
      }
  
      public enum ScreenshotMode {
          simple, redraw
      }
  
      public static enum ImageFormat {
          PNG, JPG
      }
  
      public static class ScreenshotConf implements IConf {
  
          public static final int MIN_SCREENSHOT_SIZE = 50;
          public static final int MAX_SCREENSHOT_SIZE = 25000;
  
          public int SCREENSHOT_WIDTH;
          public int SCREENSHOT_HEIGHT;
          public String SCREENSHOT_FOLDER;
          public ScreenshotMode SCREENSHOT_MODE;
          public ImageFormat SCREENSHOT_FORMAT;
          public float SCREENSHOT_QUALITY;
  
          public void initialize(int sCREENSHOT_WIDTH, int sCREENSHOT_HEIGHT, String sCREENSHOT_FOLDER, ScreenshotMode sCREENSHOT_MODE, ImageFormat sCREENSHOT_FORMAT, float sCREENSHOT_QUALITY) {
              SCREENSHOT_WIDTH = sCREENSHOT_WIDTH;
              SCREENSHOT_HEIGHT = sCREENSHOT_HEIGHT;
              SCREENSHOT_FOLDER = sCREENSHOT_FOLDER;
              SCREENSHOT_MODE = sCREENSHOT_MODE;
              SCREENSHOT_FORMAT = sCREENSHOT_FORMAT;
              SCREENSHOT_QUALITY = sCREENSHOT_QUALITY;
          }
  
          public boolean isSimpleMode() {
              return SCREENSHOT_MODE.equals(ScreenshotMode.simple);
          }
  
          public boolean isRedrawMode() {
              return SCREENSHOT_MODE.equals(ScreenshotMode.redraw);
          }
  
      }
  
      public static class PerformanceConf implements IConf {
  
          public boolean MULTITHREADING;
          public int NUMBER_THREADS;
  
          public void initialize(boolean MULTITHREADING, int NUMBER_THREADS) {
              this.MULTITHREADING = MULTITHREADING;
              this.NUMBER_THREADS = NUMBER_THREADS;
          }
  
          /**
           * Returns the actual number of threads. It accounts for the number of
           * threads being 0 or less, "let the program decide" option, in which
           * case the number of processors is returned
           *
           * @return The number of threads
           */
          public int NUMBER_THREADS() {
              if (NUMBER_THREADS <= 0)
                  return Runtime.getRuntime().availableProcessors();
              else
                  return NUMBER_THREADS;
          }
  
      }
  
      public static class PostprocessConf implements IConf, IObserver {
  
          public enum Antialias {
              NONE(0), FXAA(-1), NFAA(-2), SSAA(1);
  
              int aacode;
  
              private Antialias(int aacode) {
                  this.aacode = aacode;
              }
  
              public int getAACode() {
                  return this.aacode;
              }
  
              public boolean isPostProcessAntialias() {
                  return this.aacode < 0;
              }
          }
  
          public Antialias getAntialias(int code) {
              switch (code) {
                  case 0:
                      return Antialias.NONE;
                  case -1:
                      return Antialias.FXAA;
                  case -2:
                      return Antialias.NFAA;
                  case 1:
                      return Antialias.SSAA;
                  default:
                      return Antialias.NONE;
              }
          }
  
          public Antialias POSTPROCESS_ANTIALIAS;
          public float POSTPROCESS_BLOOM_INTENSITY;
          public float POSTPROCESS_MOTION_BLUR;
          public boolean POSTPROCESS_LENS_FLARE;
          public boolean POSTPROCESS_LIGHT_SCATTERING;
          public boolean POSTPROCESS_FISHEYE;
          /**
           * Brightness level in [-1..1]. Default is 0.
           **/
          public float POSTPROCESS_BRIGHTNESS;
          /**
           * Contrast level in [0..2]. Default is 1.
           **/
          public float POSTPROCESS_CONTRAST;
          /**
           * Hue level in [0..2]. Default is 1.
           **/
          public float POSTPROCESS_HUE;
          /**
           * Saturation level in [0..2]. Default is 1.
           **/
          public float POSTPROCESS_SATURATION;
          /**
           * Gamma correction value in [0.1..3]
           **/
          public float POSTPROCESS_GAMMA;
  
          public PostprocessConf() {
              EventManager.instance.subscribe(this, Events.BLOOM_CMD, Events.LENS_FLARE_CMD, Events.MOTION_BLUR_CMD, Events.LIGHT_SCATTERING_CMD, Events.FISHEYE_CMD, Events.BRIGHTNESS_CMD, Events.CONTRAST_CMD, Events.HUE_CMD, Events.SATURATION_CMD, Events.GAMMA_CMD);
          }
  
          public void initialize(Antialias POSTPROCESS_ANTIALIAS, float POSTPROCESS_BLOOM_INTENSITY, float POSTPROCESS_MOTION_BLUR, boolean POSTPROCESS_LENS_FLARE, boolean POSTPROCESS_LIGHT_SCATTERING, boolean POSTPROCESS_FISHEYE, float POSTPROCESS_BRIGHTNESS, float POSTPROCESS_CONTRAST, float POSTPROCESS_HUE, float POSTPROCESS_SATURATION, float POSTPROCESS_GAMMA) {
              this.POSTPROCESS_ANTIALIAS = POSTPROCESS_ANTIALIAS;
              this.POSTPROCESS_BLOOM_INTENSITY = POSTPROCESS_BLOOM_INTENSITY;
              this.POSTPROCESS_MOTION_BLUR = POSTPROCESS_MOTION_BLUR;
              this.POSTPROCESS_LENS_FLARE = POSTPROCESS_LENS_FLARE;
              this.POSTPROCESS_LIGHT_SCATTERING = POSTPROCESS_LIGHT_SCATTERING;
              this.POSTPROCESS_FISHEYE = POSTPROCESS_FISHEYE;
              this.POSTPROCESS_BRIGHTNESS = POSTPROCESS_BRIGHTNESS;
              this.POSTPROCESS_CONTRAST = POSTPROCESS_CONTRAST;
              this.POSTPROCESS_HUE = POSTPROCESS_HUE;
              this.POSTPROCESS_SATURATION = POSTPROCESS_SATURATION;
              this.POSTPROCESS_GAMMA = POSTPROCESS_GAMMA;
          }
  
          @Override
          public void notify(Events event, Object... data) {
              switch (event) {
                  case BLOOM_CMD:
                      POSTPROCESS_BLOOM_INTENSITY = (float) data[0];
                      break;
                  case LENS_FLARE_CMD:
                      POSTPROCESS_LENS_FLARE = (Boolean) data[0];
                      break;
                  case LIGHT_SCATTERING_CMD:
                      POSTPROCESS_LIGHT_SCATTERING = (Boolean) data[0];
                      break;
                  case MOTION_BLUR_CMD:
                      POSTPROCESS_MOTION_BLUR = (float) data[0];
                      break;
                  case FISHEYE_CMD:
                      POSTPROCESS_FISHEYE = (Boolean) data[0];
                      break;
                  case BRIGHTNESS_CMD:
                      POSTPROCESS_BRIGHTNESS = MathUtils.clamp((float) data[0], Constants.MIN_BRIGHTNESS, Constants.MAX_BRIGHTNESS);
                      break;
                  case CONTRAST_CMD:
                      POSTPROCESS_CONTRAST = MathUtils.clamp((float) data[0], Constants.MIN_CONTRAST, Constants.MAX_CONTRAST);
                      break;
                  case HUE_CMD:
                      POSTPROCESS_HUE = MathUtils.clamp((float) data[0], Constants.MIN_HUE, Constants.MAX_HUE);
                      break;
                  case SATURATION_CMD:
                      POSTPROCESS_SATURATION = MathUtils.clamp((float) data[0], Constants.MIN_SATURATION, Constants.MAX_SATURATION);
                      break;
                  case GAMMA_CMD:
                      POSTPROCESS_GAMMA = MathUtils.clamp((float) data[0], Constants.MIN_GAMMA, Constants.MAX_GAMMA);
                      break;
                  default:
                      break;
              }
          }
  
      }
  
      public static class ControlsConf implements IConf {
          public String CONTROLLER_MAPPINGS_FILE;
          public boolean INVERT_LOOK_Y_AXIS;
          public boolean DEBUG_MODE;
          /**
           * Controller name blacklist. Check out names in the preferences window.
           **/
          public String[] CONTROLLER_BLACKLIST;
  
          public ControlsConf() {
  
          }
  
          public void initialize(String cONTROLLER_MAPPINGS_FILE, boolean iNVERT_LOOK_Y_AXIS, boolean dEBUG_MODE, String[] cONTROLLER_BLACKLIST) {
              CONTROLLER_MAPPINGS_FILE = cONTROLLER_MAPPINGS_FILE;
              INVERT_LOOK_Y_AXIS = iNVERT_LOOK_Y_AXIS;
              DEBUG_MODE = dEBUG_MODE;
              CONTROLLER_BLACKLIST = cONTROLLER_BLACKLIST;
  
          }
  
          public boolean isControllerBlacklisted(String controllerName) {
              if (CONTROLLER_BLACKLIST == null || CONTROLLER_BLACKLIST.length == 0) {
                  return false;
              } else {
                  for (String cn : CONTROLLER_BLACKLIST) {
                      if (controllerName.equalsIgnoreCase(cn))
                          return true;
                  }
              }
              return false;
          }
  
          public void addControllerListener(ControllerListener listener) {
              Array<Controller> controllers = Controllers.getControllers();
              for (Controller controller : controllers) {
                  if (!isControllerBlacklisted(controller.getName())) {
                      controller.addListener(listener);
                  }
              }
          }
  
          public void removeControllerListener(ControllerListener listener) {
              Array<Controller> controllers = Controllers.getControllers();
              for (Controller controller : controllers) {
                  if (!isControllerBlacklisted(controller.getName())) {
                      controller.removeListener(listener);
                  }
              }
          }
      }
  
      /**
       * Runtime configuration values, which are never persisted.
       *
       * @author Toni Sagrista
       */
      public static class RuntimeConf implements IConf, IObserver {
  
          public boolean DISPLAY_GUI;
          public boolean UPDATE_PAUSE;
          public boolean TIME_ON;
          /**
           * Whether we use the RealTimeClock or the GlobalClock
           **/
          public boolean REAL_TIME;
          public boolean INPUT_ENABLED;
          public boolean RECORD_CAMERA;
          public boolean RECORD_KEYFRAME_CAMERA;
          public float LIMIT_MAG_RUNTIME;
          public boolean STRIPPED_FOV_MODE = false;
          /**
           * Whether octree drawing is active or not
           **/
          public boolean DRAW_OCTREE;
          public boolean RELATIVISTIC_ABERRATION = false;
          public boolean GRAVITATIONAL_WAVES = false;
  
          public RuntimeConf() {
              EventManager.instance.subscribe(this, Events.LIMIT_MAG_CMD, Events.INPUT_ENABLED_CMD, Events.DISPLAY_GUI_CMD, Events.TOGGLE_UPDATEPAUSE, Events.TOGGLE_TIME_CMD, Events.RECORD_CAMERA_CMD, Events.GRAV_WAVE_START, Events.GRAV_WAVE_STOP);
          }
  
          public void initialize(boolean dISPLAY_GUI, boolean uPDATE_PAUSE, boolean sTRIPPED_FOV_MODE, boolean tIME_ON, boolean iNPUT_ENABLED, boolean rECORD_CAMERA, float lIMIT_MAG_RUNTIME, boolean rEAL_TIME, boolean dRAW_OCTREE) {
              DISPLAY_GUI = dISPLAY_GUI;
              UPDATE_PAUSE = uPDATE_PAUSE;
              TIME_ON = tIME_ON;
              INPUT_ENABLED = iNPUT_ENABLED;
              RECORD_CAMERA = rECORD_CAMERA;
              LIMIT_MAG_RUNTIME = lIMIT_MAG_RUNTIME;
              STRIPPED_FOV_MODE = sTRIPPED_FOV_MODE;
              REAL_TIME = rEAL_TIME;
              DRAW_OCTREE = dRAW_OCTREE;
          }
  
          @Override
          public void notify(Events event, Object... data) {
              switch (event) {
                  case LIMIT_MAG_CMD:
                      LIMIT_MAG_RUNTIME = (float) data[0];
                      AbstractRenderSystem.POINT_UPDATE_FLAG = true;
                      break;
  
                  case INPUT_ENABLED_CMD:
                      INPUT_ENABLED = (boolean) data[0];
                      break;
  
                  case DISPLAY_GUI_CMD:
                      if (data.length > 1) {
                          // Value
                          Boolean val = (Boolean) data[1];
                          DISPLAY_GUI = val;
                      } else {
                          // Toggle
                          DISPLAY_GUI = !DISPLAY_GUI;
                      }
                      break;
                  case TOGGLE_UPDATEPAUSE:
                      UPDATE_PAUSE = !UPDATE_PAUSE;
                      EventManager.instance.post(Events.UPDATEPAUSE_CHANGED, UPDATE_PAUSE);
                      break;
                  case TOGGLE_TIME_CMD:
                      toggleTimeOn((Boolean) data[0]);
                      break;
                  case RECORD_CAMERA_CMD:
                      toggleRecord((Boolean) data[0]);
                      break;
                  case GRAV_WAVE_START:
                      GRAVITATIONAL_WAVES = true;
                      break;
                  case GRAV_WAVE_STOP:
                      GRAVITATIONAL_WAVES = false;
                      break;
                  default:
                      break;
  
              }
  
          }
  
          /**
           * Toggles the time
           */
          public void toggleTimeOn(Boolean timeOn) {
              if (timeOn != null) {
                  TIME_ON = timeOn;
              } else {
                  TIME_ON = !TIME_ON;
              }
          }
  
          /**
           * Toggles the record camera
           */
          public void toggleRecord(Boolean rec) {
              if (rec != null) {
                  RECORD_CAMERA = rec;
              } else {
                  RECORD_CAMERA = !RECORD_CAMERA;
              }
          }
  
      }
  
      /**
       * Holds the configuration for the output frame subsystem and the camera
       * recording.
       *
       * @author Toni Sagrista
       */
      public static class FrameConf implements IConf, IObserver {
          public static final int MIN_FRAME_SIZE = 50;
          public static final int MAX_FRAME_SIZE = 25000;
  
          /**
           * The width of the image frames
           **/
          public int RENDER_WIDTH;
          /**
           * The height of the image frames
           **/
          public int RENDER_HEIGHT;
          /**
           * The number of images per second to produce
           **/
          public int RENDER_TARGET_FPS;
          /**
           * The target FPS when recording the camera
           **/
          public int CAMERA_REC_TARGET_FPS;
          /**
           * Automatically activate frame output system when playing camera file
           **/
          public boolean AUTO_FRAME_OUTPUT_CAMERA_PLAY;
          /**
           * The output folder
           **/
          public String RENDER_FOLDER;
          /**
           * The prefix for the image files
           **/
          public String RENDER_FILE_NAME;
          /**
           * Should we write the simulation time to the images?
           **/
          public boolean RENDER_SCREENSHOT_TIME;
          /**
           * Whether the frame system is activated or not
           **/
          public boolean RENDER_OUTPUT = false;
          /**
           * The frame output screenshot mode
           **/
          public ScreenshotMode FRAME_MODE;
          /**
           * Format
           **/
          public ImageFormat FRAME_FORMAT;
          /**
           * Quality, in case format is JPG
           **/
          public float FRAME_QUALITY;
  
          /**
           * Path type of camera position
           **/
          public CameraKeyframeManager.PathType KF_PATH_TYPE_POSITION;
          /**
           * Path type of camera orientation
           **/
          public CameraKeyframeManager.PathType KF_PATH_TYPE_ORIENTATION;
  
          public FrameConf() {
              EventManager.instance.subscribe(this, Events.CONFIG_FRAME_OUTPUT, Events.FRAME_OUTPUT_CMD);
          }
  
          public boolean isSimpleMode() {
              return FRAME_MODE.equals(ScreenshotMode.simple);
          }
  
          public boolean isRedrawMode() {
              return FRAME_MODE.equals(ScreenshotMode.redraw);
          }
  
          public void initialize(int rENDER_WIDTH, int rENDER_HEIGHT, int rENDER_TARGET_FPS, int cAMERA_REC_TARGET_FPS, boolean aUTO_FRAME_OUTPUT_CAMERA_PLAY, String rENDER_FOLDER, String rENDER_FILE_NAME, boolean rENDER_SCREENSHOT_TIME, boolean rENDER_OUTPUT, ScreenshotMode fRAME_MODE, ImageFormat fRAME_FORMAT, float fRAME_QUALITY, CameraKeyframeManager.PathType kF_PATH_TYPE_POSITION, CameraKeyframeManager.PathType kF_PATH_TYPE_ORIENTATION) {
              RENDER_WIDTH = rENDER_WIDTH;
              RENDER_HEIGHT = rENDER_HEIGHT;
              RENDER_TARGET_FPS = rENDER_TARGET_FPS;
              CAMERA_REC_TARGET_FPS = cAMERA_REC_TARGET_FPS;
              AUTO_FRAME_OUTPUT_CAMERA_PLAY = aUTO_FRAME_OUTPUT_CAMERA_PLAY;
              RENDER_FOLDER = rENDER_FOLDER;
              RENDER_FILE_NAME = rENDER_FILE_NAME;
              RENDER_SCREENSHOT_TIME = rENDER_SCREENSHOT_TIME;
              RENDER_OUTPUT = rENDER_OUTPUT;
              FRAME_MODE = fRAME_MODE;
              FRAME_FORMAT = fRAME_FORMAT;
              FRAME_QUALITY = fRAME_QUALITY;
              KF_PATH_TYPE_ORIENTATION = kF_PATH_TYPE_ORIENTATION;
              KF_PATH_TYPE_POSITION = kF_PATH_TYPE_POSITION;
          }
  
          @Override
          public void notify(Events event, Object... data) {
              switch (event) {
                  case CONFIG_FRAME_OUTPUT:
                      boolean updateFrameSize = RENDER_WIDTH != (int) data[0] || RENDER_HEIGHT != (int) data[1];
                      RENDER_WIDTH = (int) data[0];
                      RENDER_HEIGHT = (int) data[1];
                      RENDER_TARGET_FPS = (int) data[2];
  