#!/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 'pp'
require 'tmpdir'
require 'erb'
require_relative 'lib/helper'
require_relative 'lib/arguments'
require_relative 'lib/config'
require_relative 'lib/interaction'
require_relative 'lib/sound_driver'
require_relative 'lib/analyze'
require_relative 'lib/licks'
require_relative 'lib/handle_holes'
require_relative 'lib/listen'
require_relative 'lib/quiz_or_licks'
require_relative 'lib/play'
require_relative 'lib/report'
require_relative 'lib/calibrate'
require_relative 'lib/tools'
require_relative 'lib/develop'

$program_start = Time.now.to_f
$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"]

# 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

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

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 $mode != :report && $mode != :develop && 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

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

write_dump 'start' if $testing

Thread.report_on_exception = false


Signal.trap('SIGINT') do
  print "\e[#{$lines[:message2]}H" if $move_down_on_exit
  if $opts[:debug]
    print_debug_info
    fail "\n\n\n\e[0mexit on ctrl-c"
  end
  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
  write_dump 'end' if $testing
  Thread.list.each do |thread|
    thread.exit unless thread == Thread.current
  end
  print_lagging_info if $lagging_freqs_lost > 0 && $total_freqs > 0
  # 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])
end


# some regression tests
note2semi('a4') == 0 or fail 'Internal error'
semi2note(0) == 'a4' or fail 'Internal error'

$other_mode_saved = Hash.new
begin
  if $ctl_mic[:switch_modes]
    $ctl_mic[:switch_modes] = false
    $mode = ($modes_for_switch - [$mode])[0]
    if !$other_mode_saved[:conf]
      # We are switching first time from licks or quiz to listen
      $other_mode_saved[:conf] = $conf.clone
      $other_mode_saved[:opts] = $opts.clone
      $other_mode_saved[:opts][:no_progress] = false
      $other_mode_saved[:opts][:comment] = :note
    end
    $conf, $other_mode_saved[:conf] = $other_mode_saved[:conf], $conf
    $opts, $other_mode_saved[:opts] = $other_mode_saved[:opts], $opts
    $lines = calculate_screen_layout

    clear_area_comment
    print "\e[#{$lines[:comment_tall] + 1}H\e[0m\e[#{$mode == :listen ? 34 : 32}m"
    do_figlet_unwrapped "> > >   #{$mode}", 'smblock'
    sleep 1
  end

  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 :report
    do_report 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]

