#!/usr/bin/env ruby

#
# Main entry point: Load and orchestrate functions, handle global vars
#

require 'set'
require 'yaml'
require 'json'
require 'fileutils'
require 'open3'
require 'tmpdir'
require 'erb'
require 'timeout'
require 'tempfile'
require 'singleton'
require_relative 'libexec/helper'
require_relative 'libexec/arguments'
require_relative 'libexec/config'
require_relative 'libexec/interaction'
require_relative 'libexec/players'
require_relative 'libexec/sound_driver'
require_relative 'libexec/analyze'
require_relative 'libexec/licks'
require_relative 'libexec/handle_holes'
require_relative 'libexec/listen'
require_relative 'libexec/quiz_or_licks'
require_relative 'libexec/play'
require_relative 'libexec/print'
require_relative 'libexec/calibrate'
require_relative 'libexec/tools'
require_relative 'libexec/develop'

$program_start = $mode_start = Time.now.to_f
$initialized = false
$on_error_raise = false
# grep .rb-files for 'debug' to see choices, e.g. :check_screen. Still
# need to give '--debug' to switch it on
$debug_what = Set.new([])
# store (ad-hoc) state for debugging
$debug_state = {}

$term_height, $term_width = prepare_screen 

# check this early, beause it is used during initialization
$testing = !!ENV["HARPWISE_TESTING"]
$testing_what = nil
if $testing && ! %w(1 true t yes y).include?(ENV["HARPWISE_TESTING"].downcase)
  $testing_what = ENV["HARPWISE_TESTING"].downcase.to_sym
  tw_allowed = [:lag, :jitter, :player]
  err "Environment variable HARPWISE_TESTING is '#{ENV["HARPWISE_TESTING"]}', none of the allowed values #{tw_allowed} (case insensitive)" unless tw_allowed.include?($testing_what)
end

# while testing, additionally make sure, that we do not use relative
# paths; do this early, before we even have parsed options
Dir.chdir(Dir.home) if $testing

set_global_vars_early

check_installation

$conf = read_technical_config

# neede for usage
initialize_extra_vars

# get debug info early to help e.g. for error messages
$opts = {debug: true} if ARGV.include?('--debug')
$mode, $type, $key, $scale, $opts = parse_arguments_early

IO.popen('ps -ef').each_line do |line|
  fields = line.chomp.split(' ',8)
  err "An instance of this program is already running: pid: #{fields[1]}, commandline: '#{fields[-1]}'" if ![:develop, :tools, :print].include?($mode) && Process.pid != fields[1].to_i && fields[-1][File.basename($0)] && fields[-1]['ruby']
end

# we need to have mode for this, so calculate it late
$lines = calculate_screen_layout
# Count things and print them on --debug; supplements info from ruby -rprofile
$perfctr = Hash.new {|h,k| h[k] = 0}
  
set_global_vars_late

set_global_musical_vars

to_handle = parse_arguments_late

check_screen(hint_on_large_term: true) if [:listen, :quiz, :licks].include?($mode)

write_dump 'start' if $testing

# We need this to be true, because otherwise exceptions in started
# threads go unnoticed and may stall the hole program
Thread.report_on_exception = true

Signal.trap('SIGINT') do
  print "\e[#{$lines[:message2]}H" if $move_down_on_exit
  fail "\n\n\n\e[0mexit on ctrl-c" if $opts[:debug]
  puts "\n\n\n\e[0mexit on ctrl-c"
  exit 1
end


if [:listen, :quiz, :licks].include?($mode)
  Signal.trap('WINCH') do
    $ctl_sig_winch = true
  end
  Signal.trap('CONT') do
    system('clear')
    prepare_term
    $ctl_redraw = true
  end
end


at_exit do
  sane_term if STDIN.isatty
  print "\e[0m"
  write_dump 'end' if $testing
  Thread.list.each do |thread|
    thread.exit unless thread == Thread.current
  end

  # a user recording could still be going on
  $ulrec&.stop_rec
  
  # notice for lagging or jitter if appropriate
  print_afterthought

  # give processes started by threads some time to terminate
  sleep 0.1
  # take care to not remove too much, if something happens and $dirs
  # would not be initialized completetly
  FileUtils.remove_dir($dirs[:tmp]) if $dirs && $dirs[:tmp] && File.directory?($dirs[:tmp])

  if $opts && $opts[:debug] 
    print_debug_info
    puts "Note: A debug log has been written: #{$debug_log}" if File.exist?($debug_log)
  end
    
  if $pers_file && $pers_data.keys.length > 0 && $pers_fingerprint != $pers_data.hash
    File.write($pers_file, JSON.pretty_generate($pers_data))
  end
end
$initialized = true

$other_mode_saved = Hash.new
begin

  $ulrec&.stop_rec
  switch_modes if $ctl_mic[:switch_modes]

  case $mode
  when :quiz
    do_quiz_or_licks
  when :licks
    do_quiz_or_licks
  when :listen
    do_listen
  when :play
    do_play to_handle
  when :print
    do_print to_handle
  when :calibrate
    if $opts[:auto]
      do_calibrate_auto
    else
      do_calibrate_assistant
    end
  when :tools
    do_tools to_handle
  when :develop
    do_develop to_handle
  end
end while $ctl_mic[:switch_modes]

