package ocelot.desktop.util

import ocelot.desktop.Settings.Int2D
import ocelot.desktop.util.SettingsData.Fields

import scala.reflect.runtime.universe

class SettingsData {
  // implementation notes:
  // this class relies on reflection to implement updateWith
  // it's assumed all fields that should be copied are declared as **public var**
  def this(data: SettingsData) {
    this()
    updateWith(data)
    keymap = data.keymap.clone()
  }

  var brainCustomConfigPath: Option[String] = None

  var volumeMaster: Float = 1f
  var volumeBeep: Float = 1f
  var volumeRecords: Float = 1f
  var volumeEnvironment: Float = 1f
  var volumeInterface: Float = 0.5f
  var audioDisable: Boolean = false
  var soundPositional: Boolean = false
  var logAudioErrorStacktrace: Boolean = false

  var scaleFactor: Float = 1f
  var windowSize: Int2D = new Int2D()
  var windowPosition: Int2D = new Int2D()
  var windowValidatePosition: Boolean = true
  var windowFullscreen: Boolean = false
  var disableVsync: Boolean = false
  var debugLwjgl: Boolean = false

  var keymap: Keymap = new Keymap

  var recentWorkspace: Option[String] = None

  var pinNewWindows: Boolean = true
  var unfocusedWindowTransparency: Float = 0.5f
  var unfocusedWindowHide: Boolean = false
  var saveOnExit: Boolean = true
  var autosave: Boolean = true
  var autosavePeriod: Int = 300
  var openLastWorkspace: Boolean = true
  var renderScreenDataOnNodes: Boolean = true
  var tooltipDelayItem: Float = 0.001f
  var tooltipDelayUI: Float = 0.3f
  var enableFestiveDecorations: Boolean = true

  var screenWindowMipmap: Boolean = true

  private val mirror = universe.runtimeMirror(getClass.getClassLoader).reflect(this)

  def updateWith(data: SettingsData): Unit = {
    for (fieldSym <- Fields) {
      val value = data.mirror.reflectMethod(fieldSym.getter.asMethod)()
      mirror.reflectMethod(fieldSym.setter.asMethod)(value)
    }
  }
}

object SettingsData {
  private val Fields = {
    universe.typeOf[SettingsData]
      .decls
      .filter(_.isTerm)
      .map(_.asTerm)
      .filter(_.isVar)
      // the var itself is private: check setter and getter
      .filter(v => v.setter.isPublic && v.getter.isPublic)
      .toList
  }
}
