#! /usr/bin/env ruby

require 'etc'
require 'optionparser'
require 'pathname'
require 'shellwords'

FUSE = false

MOUNT_OVERLAY = FUSE ? %w(fuse-overlayfs) : %w(sudo mount -t overlay)
UMOUNT = FUSE ? %w(fusermount -u) : %w(sudo umount)

def cmd *args
  $stderr.puts "+ #{args.shelljoin}"
  system *args
  $?.success?
end

def umount dir
  cmd('mountpoint', '-q', dir) && (
    cmd(*UMOUNT, dir) || fail('Umount failed'))
end

Dir.chdir __dir__
base = 'ovrlay'

dirs = ['base']
misc = Dir.glob('*', base: File.join(base, 'mod')).to_set
lists = Dir.glob('*', base: File.join('lists')).to_set

save = true
OptionParser.new do |opt|
  opt.on('--no-save') { save = false }
end.parse!

queue = [*ARGV]
lists_done = Set.new
until queue.empty?
  a = queue.shift

  if lists.member?(a) && !lists_done.member?(a)
    lists_done << a
    queue.unshift *File.readlines(File.join('lists', a)).map(&:strip)
  elsif misc.member? a
    path = File.join 'mod', a
    dirs.unshift path unless dirs.member? path
  else
    fail "Unknown mod #{a.inspect}"
  end
end

dirs.unshift 'save' if save
fail if dirs.size < 2

dirs.map! {|d| File.join(base, d).gsub %r{([\\,:])}, '\\\1' }
work = File.join base, 'idiotism'
# note: a single mount option has an undocumented length limit, enough to reach
# by 3-4 mods... lowerdir+ works, but has weird syntax
ld = FUSE ? "lowerdir=#{dirs[1...].join ':'},timeout=1," :
       dirs[1...].map {|x| "lowerdir+=#{x}," }.join
opts = "#{ld}upperdir=#{dirs[0]},workdir=#{work}"
tmp_mnt = File.join(base, 'mount')
real_mnt = 'mount'

umount real_mnt
umount tmp_mnt

cmd(*MOUNT_OVERLAY, '-o', opts, 'nothing', tmp_mnt) ||
  fail('Tmp mount failed')

cmd('mountpoint', '-q', real_mnt) ||
  cmd('ciopfs', tmp_mnt, real_mnt) ||
  fail('Real mout failed')

plugins_fn = "wine_prefix/drive_c/users/#{Etc.getlogin}/AppData/Local/Skyrim Special Edition GOG/Plugins.txt"
orig_es = Dir.glob('*.es[mp]', base: File.join(base, 'base/data')).to_set
new_es = Dir.glob('*.es[mp]', base: File.join(base, 'mount/data')).to_set
diff_es = new_es - orig_es
if diff_es.member? 'skyui_se.esp'
  diff_es = ['skyui_se.esp', *diff_es].to_set
end
File.write plugins_fn, diff_es.map { "*#{it}\n" }.join
