e = '3.2'
a = RUBY_VERSION.split('.')[0, 2].join('.')
unless a == e
  m = "KorpCraft package requires Ruby #{e}, current Ruby is #{RUBY_VERSION}"
  UI.messagebox(m) if defined?(UI) && UI.respond_to?(:messagebox)
  raise LoadError, m
end

i = RubyVM::InstructionSequence if defined?(RubyVM::InstructionSequence)
unless i && i.respond_to?(:load_from_binary)
  raise LoadError, "KorpCraft requires RubyVM bytecode support for Ruby #{e}"
end

unless File.respond_to?(:absolute_path?)
  File.define_singleton_method(:absolute_path?) { |v| v.to_s.match?(%r{\A(?:[A-Za-z]:[\\/]|[\\/]{2}|/)}) }
end

p = ENV['KORPCRAFT_BOOT_PATHS_BASE64'].to_s
p = p.empty? ? [] : p.unpack1('m0').force_encoding(Encoding::UTF_8).split("\0", 2)
r = File.expand_path(p[0] || File.expand_path('..', __dir__))
s = ENV['KORPCRAFT_SLOT_ROOT'].to_s
s = File.expand_path(s) unless s.empty?
u = p[1] || ENV['KORPCRAFT_USER_DATA_ROOT'].to_s
u = u.empty? ? nil : File.expand_path(u)
t = defined?(Sketchup) ? "sketchup_#{Sketchup.version.to_s.split('.').first}" : 'sketchup_dev'
w = u || File.join(ENV['APPDATA'].to_s.tr('\\', '/'), 'KorpCraft', t)
w = w.encode(Encoding::UTF_8, w.encoding == Encoding::ASCII_8BIT ? Encoding.find('filesystem') : w.encoding)

f = lambda do |failure|
  user_message, diagnostic = failure
  begin
    require 'fileutils'
    FileUtils.mkdir_p(w)
    File.open(File.join(w, 'bootstrap_errors.log'), 'a:UTF-8') do |h|
      h.puts("#{Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ')} #{diagnostic}")
    end
  rescue StandardError
    nil
  end
  m = "KorpCraft: #{user_message}"
  UI.messagebox(m) if defined?(UI) && UI.respond_to?(:messagebox)
  raise LoadError, m
end

k = File.join(r, 'slots', 'pointer.json')
q = []
if s.empty? && File.file?(k)
  require 'json'
  y = begin
    JSON.parse(File.binread(k))
  rescue StandardError
    nil
  end
  y = nil unless y.is_a?(Hash) && %w[a b].include?(y['active'])
  if y
    g = y['pending']
    if %w[a b].include?(g) && g != y['active'] && y['attempts'].to_i < 1
      begin
        y['attempts'] = y['attempts'].to_i + 1
        File.binwrite("#{k}.tmp", JSON.generate(y))
        File.rename("#{k}.tmp", k)
        q << File.join(r, 'slots', g)
      rescue StandardError
        nil
      end
    end
    q << File.join(r, 'slots', y['active'])
  end
end
q << (s.empty? ? r : s) if q.empty?

z = lambda do |v|
  p = File.join(v, 'core', 'bytecode_loader.rbc')
  incomplete = 'Пакет установлен не полностью. Переустановите плагин полным пакетом .rbz.'
  unavailable = 'Не удалось проверить целостность пакета. Переустановите плагин полным пакетом .rbz.'
  rejected = 'Проверка целостности пакета не пройдена. Переустановите плагин полным пакетом .rbz.'
  next [[incomplete, 'bytecode loader is missing'], nil, nil] unless File.file?(p)

  b = File.binread(p)
  c = File.join(v, 'manifest.kcm1')
  n = File.file?(c)
  next [[incomplete, 'slot manifest is missing'], nil, nil] if true && !n

  d = nil
  if n
    require 'json'
    require 'digest'
    m = File.binread(c).strip
    o = false
    dll = File.join(r, 'kc_license.dll').tr('/', '\\')
    dll = dll.encode(Encoding::UTF_8, dll.encoding == Encoding::ASCII_8BIT ? Encoding.find('filesystem') : dll.encoding)
    next [[unavailable, "license DLL is missing: #{dll}"], nil, nil] unless File.file?(dll)

    free_library = nil
    library = nil
    begin
      require 'fiddle'
      kernel = Fiddle::Handle.new('kernel32.dll')
      load_library = Fiddle::Function.new(
        kernel['LoadLibraryW'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_VOIDP
      )
      get_proc_address = Fiddle::Function.new(
        kernel['GetProcAddress'],
        [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP], Fiddle::TYPE_VOIDP
      )
      free_library = Fiddle::Function.new(
        kernel['FreeLibrary'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT
      )
      get_last_error = Fiddle::Function.new(
        kernel['GetLastError'], [], Fiddle::TYPE_LONG
      )
      wide_dll = dll.encode(Encoding::UTF_16LE).b + "\0\0"
      library = load_library.call(wide_dll)
      if library.nil? || library.to_i.zero?
        code = get_last_error.call.to_i
        next [[unavailable, "license DLL failed to load (win32=#{code}): #{dll}"], nil, nil]
      end

      address = get_proc_address.call(library, "kc_update_verify_manifest\0".b)
      if address.nil? || address.to_i.zero?
        code = get_last_error.call.to_i
        next [[unavailable, "manifest verifier export is missing (win32=#{code}): #{dll}"], nil, nil]
      end

      o = Fiddle::Function.new(
        address, [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT
      ).call(File.join(w, 'update'), m) == 1
    rescue LoadError, StandardError => e
      next [[unavailable, "manifest verifier unavailable: #{e.class}: #{e.message}"], nil, nil]
    ensure
      free_library.call(library) if free_library && library && !library.to_i.zero?
    end
    next [[rejected, 'slot manifest was rejected'], nil, nil] unless o

    j = m.split('.')[2].to_s.tr('-_', '+/')
    j += '=' * (-j.size % 4)
    d = begin
      JSON.parse(j.unpack1('m0'))['files']
    rescue StandardError
      nil
    end
    next [[rejected, 'slot manifest is unreadable'], nil, nil] unless d.is_a?(Hash) && !d.empty?

    x = d['core/bytecode_loader.rbc']
    unless x.is_a?(Hash) && x['size'] == b.bytesize && x['sha256'] == Digest::SHA256.hexdigest(b)
      next [[rejected, 'bytecode loader failed integrity check'], nil, nil]
    end
  end
  [nil, b, d]
end

b = nil
d = nil
l = nil
q.each do |v|
  h, bb, dd = z.call(v)
  if h
    l ||= h
    next
  end
  s = v
  b = bb
  d = dd
  break
end
f.call(l || ['Пакет установлен не полностью. Переустановите плагин полным пакетом .rbz.',
             'bytecode loader is missing']) unless b

i.load_from_binary(b).eval
KorpCraft::BytecodeLoader.install!(r, slot_root: s, user_data_root: u, manifest: d)
