require_relative "./lex.rb"
require_relative "./error.rb"
require_relative "./parse.rb"
require_relative "./ir.rb"
require_relative "./ir2.rb"
require_relative "./infer.rb"
require_relative "./version.rb"
require_relative "./auto_parse.rb"
require_relative "./to_engine.rb"

Site = "https://golfscript.com/iogii/"

TestUnicodeChar = [1000].pack('U')
begin
  TestUnicodeChar.encode(Encoding.default_external)
  def int_to_char(i)
    begin
      [i].pack("U")
    rescue
      raise IogiiError.new("char out of range %d" % i)
    end
  end
rescue Encoding::UndefinedConversionError # (e.g. is ASCII)
  # to work with values 128..255 which are invalid in regular ascii
  Encoding.default_internal = Encoding.default_external = Encoding::ISO8859_1
  def int_to_char(i)
    (i%256).chr
  end
end

def gen_graph(source, input, pp_sep: nil, ast_cb: nil, ir_cb: nil)
  tokens, input_mode = *lex(source)

  if input_mode == :auto_parse_mode
    inputs = promise { read_inputs(input) }
    input_present = promise { !!inputs.value }
  else
    input_present = promise { true }
  end

  ast, ast_inds, registers = *parse_main(tokens, promise{ $input_checked = true; input_present.value })

  ast_cb[ast, ast_inds] if ast_cb

  input_op = promise { # don't eval this unless needed since awaits input
    if input_mode == :raw_mode
      if Array === input
        read_input = promise { to_lazy_list(input.map{|a| str_to_lazy_list(a) }) }
        new_op("input", "[[char]]"){ read_input.value }
      else
        read_input = promise { read_stdin(input) }
        new_op("input", "[char]"){ read_input.value }
      end
    elsif input_mode == :raw_line_mode
      if Array === input
        new_op("input", "[[[char]]]"){ to_lazy_list(input.map{|a| lines(str_to_lazy_list(a).const) } ) }
      else
        read_input = promise { lines( promise{ read_stdin(input) }) }
        new_op("input", "[[char]]"){ read_input.value }
      end
    else
      value, type, rank = parse_input(inputs.value)
      new_op(type == EmptyType ? "inputEmpty" : "input", type_and_rank_to_str(type, rank)){ value }
    end
  }

  ir, ir_inds = *to_ir(ast, registers, ast_inds, input_op)
  types,nodes_ways = *solve_types(ir)
  ranks,nodes_way = *solve_ranks(ir, types, nodes_ways)
  ir2,ir2_inds = to_ir2(ir, ranks, types, ir_inds, nodes_way)
  ir_cb[ir2, ir2_inds] if ir_cb
  ir3,ir3_ind = to_ir3(ir2, ir2_inds, pp_sep)
  return [ir3, ir3_ind]
end

def run_and_do_io(source,input,pp,engine,ast_cb: nil,ir_cb: nil)
  pp_sep = pp ? "\n" : nil
  if engine
    to_engine(source, pp_sep)
  else
    ir3, ir3_ind = gen_graph(source, input, pp_sep: pp_sep, ast_cb: ast_cb, ir_cb: ir_cb)
    promises = make_promises(ir3)
    begin
      prints(promises[ir3_ind].value)
    rescue SystemStackError => e
      raise IogiiError.new "Stack Overflow\n(You can increase the limit with the shell command: export RUBY_THREAD_VM_STACK_SIZE=<size in bytes>\nor use the engine to remove the limit)."
    end
  end
end
