#
# Aura Engine (https://gitlab.servus.at/aura/engine)
#
# Copyright (C) 2017-2020 - The Aura Engine Team.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

# READ INI FILE
default_config = "../config/engine-core.yaml"
config_file =
  list.hd(
    default=default_config,
    process.read.lines(
      "ls /etc/aura/engine-core.yaml"
    )
  )
print(
  "\tConfig file: '#{config_file}'"
)

let yaml.parse (config :
  {
    general: {
      engine_id: string,
      socket_dir: string,
      log_dir: string,
      log_level: string,
      liquidsoap_as_root: bool,
      liquidsoap_min_version: string,
      api_url_playlog: string
    },
    telnet: {
      server_enable: bool,
      server_timeout: float,
      server_host: string,
      server_port: float
    },
    fallback: {
      music_folder: string,
      show_name: string,
      show_id: string,
      type: string,
      music_playlist: string,
      music_folder_reload: float,
      max_blank: float,
      min_noise: float,
      threshold: float
    },
    stream: [
      {
        enabled: bool,
        encoding: string,
        bitrate: string,
        channels: string,
        host: string,
        port: float,
        mountpoint: string,
        user: string,
        password: string,
        url: string,
        name: string,
        genre: string,
        description: string
      }
    ],
    audio: {
      soundsystem: string,
      devices: {output: [{name: string}], input: [{name: string}]}
    }
  }
) = file.contents(config_file)

engine_config_folder = string.split(separator="/engine-core.yaml", config_file)
engine_config_folder = list.nth(default="../config/", engine_config_folder, 0)
print(
  "\tengine_config_folder = '#{engine_config_folder}'"
)

# Retrieve a setting either bei 1.) environment variable or 2.) config property.
# If none is available the default value is returned.
# def get_setting(default_value, config_prop, env_var) =
#   dv = list.assoc(default=default_value, config_prop, ini)
#   environment.get(default=dv, env_var)
# end

# FIXME: env vars are always strings, we would have to guess the type here
# since the config_value might not be a string. Maybe we can extract the type
# from config_value?
def get_setting(config_value, env_var) =
  environment.get(default=config_value, env_var)
end

# VERSION CHECK
if
  not liquidsoap.version.at_least(config.general.liquidsoap_min_version)
then
  print(
    "AURA Engine Core requires at least Liquidsoap v#{
      config.general.liquidsoap_min_version
    }"
  )
  exit(1)
end

# ALLOW LIQUIDSOAP RUN AS ROOT
if
  # get_setting(config.general.liquidsoap_as_root, "AURA_ENGINE_RUN_AS_ROOT")
  config.general.liquidsoap_as_root
then
  print(
    "\tAllow Liquidsoap running as root user: true"
  )
  settings.init.allow_root.set(true)
else
  print(
    "\tAllow Liquidsoap running as root user: false"
  )
  settings.init.allow_root.set(false)
end

# SERVER SETTINGS
# server_timeout =
#   float_of_string(
#     get_setting("16380.", "server_timeout", "AURA_ENGINE_SERVER_TIMEOUT")
#   )
settings.server.timeout.set(config.telnet.server_timeout)

# telnet_server_enable =
#   get_setting("true", "telnet_server_enable", "AURA_ENGINE_TELNET_ENABLE")
# telnet_server_host =
#   get_setting("127.0.0.1", "telnet_server_host", "AURA_ENGINE_TELNET_HOST")
# telnet_server_port =
#   int_of_string(
#     get_setting("1234", "telnet_server_port", "AURA_ENGINE_TELNET_PORT")
#   )
if
  not config.telnet.server_enable
then
  print(
    "\tLiquidsoap Telnet Server: disabled"
  )
  settings.server.telnet.set(false)
else
  print(
    "\tLiquidsoap Telnet Server at " ^
      config.telnet.server_host ^
      ":" ^
      string.float(config.telnet.server_port) ^
      " (" ^
      string.float(config.telnet.server_timeout) ^
      "s timeout)"
  )
  settings.server.telnet.bind_addr.set(config.telnet.server_host)
  settings.server.telnet.port.set(int_of_float(config.telnet.server_port))
  settings.server.telnet.set(true)
end

# BASICS
settings.console.colorize.set("always")
#settings.request.grace_time.set(2.)

# LOG SETTINGS
engine_version =
  process.read(
    "cat ../VERSION"
  )
log(
  "######################################################################################"
)
log(
  "AURA Engine:Core v#{engine_version} starting ..."
)
log(
  "######################################################################################"
)

log(
  "System Settings:"
)

# LOGGING SETTINGS
settings.log.stdout.set(true)
settings.log.file.set(true)

log_level =
  if
    config.general.log_level == "DEBUG"
  then
    5
  elsif config.general.log_level == "INFO" then 3
  elsif config.general.log_level == "WARNING" then 2
  elsif config.general.log_level == "ERROR" then 1
  else
    3
  end

# log_level = get_setting("3", "log_level", "AURA_ENGINE_CORE_LOG_LEVEL")
settings.log.level.set(log_level)

# log_dir = get_setting("../logs", "log_dir", "AURA_ENGINE_CORE_LOG_DIR")
log_file = "#{config.general.log_dir}/engine-core.log"
settings.log.file.path.set(log_file)
print(
  "\tLogging to '#{log_file}' with log level #{log_level}"
)
log(
  "\tLog level #{log_level}"
)

# CONFIGURATION FILE
log(
  "\tConfig file: '#{config_file}'"
)

# SOCKET SETTINGS
# socket_dir = get_setting("../socket", "socket_dir", "AURA_ENGINE_SOCKET_DIR")
socket_file = "#{config.general.socket_dir}/engine.sock"
if
  config.general.socket_dir != ""
then
  log(
    "\tSocket location: '#{socket_file}'"
  )
  settings.server.socket.path.set(socket_file)
  settings.server.socket.set(true)
else
  log(
    "\tCRITICAL: No socket directory set!"
  )
end

# SOUND CARD SETTINGS
# settings.audio.converter.samplerate.converters.set(["ffmpeg","libsamplerate","native"])
# settings.decoder.decoders.set(["META","WAV","AIFF","FLAC","AAC","MP4","OGG","MAD"])

# TODO: This needs refactoring
# we save the device name in the corresponding variable name
# I guess the variable name is then later used to create the in- and outputs
# a0_in = get_setting("", "input_device_0", "AURA_ENGINE_INPUT_DEVICE")

# a0_out = get_setting("", "output_device_0", "AURA_ENGINE_OUTPUT_DEVICE")
# ALSA / pulse settings
# soundsystem = get_setting("alsa", "soundsystem", "AURA_ENGINE_SOUNDSYSTEM")
soundsystem = config.audio.soundsystem
use_alsa = soundsystem == "alsa"
use_jack = soundsystem == "jack"

log(
  "\nAudio Settings:"
)
def print_device(device) =
  log(
    "\t\t- #{device.name}"
  )
end
log(
  "\tOutput Devices:"
)
list.iter(print_device, config.audio.devices.output)
log(
  "\tInput Devices:"
)
list.iter(print_device, config.audio.devices.input)

# ReplayGain Settings
# enable_replaygain_resolver =
#   bool_of_string(
#     get_setting(
#       "false",
#       "enable_replaygain_resolver",
#       "AURA_ENGINE_ENABLE_REPLAYGAIN_RESOLVER"
#     )
#   )

# FIXME: read this from the config file?
enable_replaygain_resolver = false
if
  enable_replaygain_resolver == true
then
  enable_replaygain_metadata()
  log(
    "\tReplayGain resolver enabled"
  )
end

# Frame Settings
# TODO: due to the switch to jack these settings are not needed anymore. Maybe if we
# enable ALSA in some point in the future again.
# frame_audio_sample_rate =
#   int_of_string(
#     get_setting("0", "frame_audio_sample_rate", "AURA_ENGINE_FRAME_SAMPLERATE")
#   )
frame_audio_sample_rate = 0

# frame_duration =
#   float_of_string(
#     get_setting("0.0", "frame_duration", "AURA_ENGINE_FRAME_DURATION")
#   )
frame_duration = 0.0

# frame_audio_size =
#   int_of_string(get_setting("0", "frame_audio_size", "AURA_ENGINE_FRAME_SIZE"))
frame_audio_size = 0
if
  frame_audio_sample_rate > 0
then
  log(
    "\tframe.audio.samplerate = #{frame_audio_sample_rate} Hz"
  )
  settings.frame.audio.samplerate.set(frame_audio_sample_rate)
end
if
  frame_duration > 0.0
then
  log(
    "\tframe.duration = #{frame_duration} seconds"
  )
  settings.frame.duration.set(frame_duration)
end
if
  frame_audio_size > 0
then
  log(
    "\tframe.audio.size = #{frame_audio_size} ticks"
  )
  settings.frame.audio.size.set(frame_audio_size)
end

alsa_buffered_input = ref(true)
alsa_buffered_output = ref(true)

# TODO: same question, do we want to keep this code?
# if
#   use_alsa
# then
#   log(
#     "\nALSA Settings:"
#   )
#   alsa_buffered_input :=
#     bool_of_string(
#       get_setting(
#         "true", "alsa_buffered_input", "AURA_ENGINE_ALSA_BUFFERED_INPUT"
#       )
#     )
#   alsa_buffered_output :=
#     bool_of_string(
#       get_setting(
#         "true", "alsa_buffered_output", "AURA_ENGINE_ALSA_BUFFERED_OUTPUT"
#       )
#     )
#   alsa_buffer =
#     int_of_string(get_setting("0", "alsa_buffer", "AURA_ENGINE_ALSA_BUFFER"))
#   alsa_buffer_length =
#     int_of_string(
#       get_setting("0", "alsa_buffer_length", "AURA_ENGINE_ALSA_BUFFER_LENGTH")
#     )
#   alsa_periods =
#     int_of_string(get_setting("0", "alsa_periods", "AURA_ENGINE_ALSA_PERIODS"))

#   log(
#     "\talsa_buffered_input = #{alsa_buffered_input()}"
#   )
#   log(
#     "\talsa_buffered_output = #{alsa_buffered_output()}"
#   )

#   if
#     alsa_buffer > 0
#   then
#     log(
#       "\tInternal buffer size (alsa.alsa_buffer) = #{alsa_buffer}"
#     )
#     settings.alsa.alsa_buffer.set(alsa_buffer)
#   end
#   if
#     alsa_buffer_length > 0
#   then
#     log(
#       "\tBuffer size, in frames (alsa.buffer_length) = #{alsa_buffer_length}"
#     )
#     settings.alsa.buffer_length.set(alsa_buffer_length)
#   end
#   if
#     alsa_periods > 0
#   then
#     log(
#       "\tPeriods (alsa.periods) = #{alsa_periods}"
#     )
#     settings.alsa.periods.set(alsa_periods)
#   end
# end

log(
  "\nStream Settings:"
)

# TODO: read this from the config file
# INPUT STREAM SETTINGS
# input_stream_max_buffer =
#   float_of_string(
#     get_setting(
#       "5.0", "input_stream_max_buffer", "AURA_ENGINE_STREAM_INPUT_MAX_BUFFER"
#     )
#   )
input_stream_max_buffer = 5.0

# input_stream_timeout =
#   float_of_string(
#     get_setting(
#       "10.0", "input_stream_timeout", "AURA_ENGINE_STREAM_INPUT_TIMEOUT"
#     )
#   )
input_stream_timeout = 10.0
log(
  "\tInput stream max buffer: #{input_stream_max_buffer}"
)
log(
  "\tInput stream timeout: #{input_stream_timeout}"
)

# OUTPUT STREAM
# TODO: refactor this to use the list of streams
# s0_enable =
#   get_setting("false", "stream_0", "AURA_ENGINE_STREAM_OUTPUT_ENABLE") == "true"
s0_enable = list.hd(config.stream).enabled

# s1_enable = list.assoc(default="false", "stream_1", ini) == "true"
# s2_enable = list.assoc(default="false", "stream_2", ini) == "true"
# s3_enable = list.assoc(default="false", "stream_3", ini) == "true"
# s4_enable = list.assoc(default="false", "stream_4", ini) == "true"
log(
  "\tOutput stream enabled: #{s0_enable}"
)

log(
  "\nOther Settings:"
)

# AUDIO AND PLAYLIST SOURCES
# TODO: read this from config
# audio_playlist_folder =
#   get_setting(
#     "../audio/playlists", "audio_playlist_folder", "AURA_ENGINE_PLAYLIST_FOLDER"
#   )
audio_playlist_folder = "../audio/playlists"

# FALLBACK SETTINGS
# fallback_show_name =
#   ref(
#     get_setting(
#       "Station Fallback",
#       "fallback_show_name",
#       "AURA_ENGINE_FALLBACK_SHOW_NAME"
#     )
#   )
fallback_show_name = ref(config.fallback.show_name)

# fallback_show_id =
#   ref(get_setting("-1", "fallback_show_id", "AURA_ENGINE_FALLBACK_SHOW_ID"))
fallback_show_id = ref(config.fallback.show_id)

# fallback_type =
#   get_setting("folder", "fallback_type", "AURA_ENGINE_FALLBACK_TYPE")
fallback_type = config.fallback.type

# fallback_station_playlist_name =
#   get_setting(
#     "station-fallback-playlist.m3u",
#     "fallback_music_playlist",
#     "AURA_ENGINE_FALLBACK_PLAYLIST"
#   )
fallback_station_playlist_name = config.fallback.music_playlist
fallback_station_playlist_path =
  "#{audio_playlist_folder}/#{fallback_station_playlist_name}"

# fallback_station_dir =
#   get_setting(
#     "../audio/fallback", "fallback_music_folder", "AURA_ENGINE_FALLBACK_FOLDER"
#   )
fallback_station_dir = config.fallback.music_folder

# fallback_station_dir_reload =
#   int_of_string(
#     get_setting(
#       "300",
#       "fallback_music_folder_reload",
#       "AURA_ENGINE_FALLBACK_FOLDER_RELOAD"
#     )
#   )
fallback_station_dir_reload = int_of_float(config.fallback.music_folder_reload)

# fallback_max_blank =
#   float_of_string(
#     get_setting("15.", "fallback_max_blank", "AURA_ENGINE_FALLBACK_MAX_BLANK")
#   )
fallback_max_blank = config.fallback.max_blank

# fallback_min_noise =
#   float_of_string(
#     get_setting("0.", "fallback_min_noise", "AURA_ENGINE_FALLBACK_MIN_NOISE")
#   )
fallback_min_noise = config.fallback.min_noise

# fallback_threshold =
#   float_of_string(
#     get_setting("-80.", "fallback_threshold", "AURA_ENGINE_FALLBACK_THRESHOLD")
#   )
fallback_threshold = config.fallback.threshold
log(
  "\tfallback_station_playlist_path = '#{fallback_station_playlist_path}'"
)
log(
  "\tfallback_station_dir = '#{fallback_station_dir}'"
)

# FADING SETTINGS
# fade_in_time = list.assoc(default="", "fade_in_time", ini) #int_of_string(list.assoc(default="", "fade_in_time", ini))
# fade_out_time = list.assoc(default="", "fade_out_time", ini) #int_of_string(list.assoc(default="", "fade_out_time", ini))

# Metadata Configuration
# FIXME: whats this? Move into config file
# engine_meta_key_show_id =
#   get_setting("show_id", "meta_key_show_id", "AURA_ENGINE_META_KEY_SHOW_ID")
engine_meta_key_show_id = "show_id"

# engine_default_track_type =
#   get_setting("0", "default_track_type", "AURA_ENGINE_DEFAULT_TRACK_TYPE")
engine_default_track_type = "0"

# engine_api_playlog =
#   get_setting(
#     "http://127.0.0.1:8008/api/v1/playlog",
#     "api_url_playlog",
#     "AURA_ENGINE_API_URL_PLAYLOG"
#   )
engine_api_playlog = config.general.api_url_playlog

# "1" or "2" used for HA setups
# engine_id = get_setting("1", "engine_id", "AURA_ENGINE_ID")
engine_id = config.general.engine_id
log(
  "\n######################################################################################"
)