#!/usr/bin/env ruby

require "thread"

ROOT = File.expand_path("../..", __FILE__).freeze
Dir.chdir(ROOT)

SUBMODULES = `git config --list --file .gitmodules`.lines.grep(/\.path=/).map { |line| line.chomp.split("=", 2).last }.freeze
SLOW_SUBMODULES = %w[
  vendor/grammars/factor
  vendor/grammars/fsharpbinding
  vendor/grammars/ioke-outdated
]

class TaskResult < Struct.new(:submodule, :output, :status); end

def run_process(*args)
  read, write = IO.pipe
  pid = Process.spawn(*args, in: :close, out: write, err: [:child, :out])
  write.close
  output = read.read
  read.close
  Process.wait(pid)
  [output, $?]
end

def update_submodule(submodule)
  output, status = run_process("git", "submodule", "update", "--", submodule)
  TaskResult.new(submodule, output, status)
end

def run_thread(submodules, results)
  loop do
    begin
      submodule = submodules.pop(true)
    rescue ThreadError
      # The queue is empty.
      break
    end

    results.push(update_submodule(submodule))
  end
end

submodules = Queue.new
results = Queue.new

# Update the slow submodules first so they can update in the background while
# the fast ones run.
SUBMODULES.partition { |submodule| SLOW_SUBMODULES.include?(submodule) }.flatten.each do |submodule|
  submodules.push(submodule)
end

8.times do
  Thread.new { run_thread(submodules, results) }
end

success = true
SUBMODULES.each do
  result = results.pop
  unless result.status.success?
    success = false
    puts "Error updating #{result.submodule}"
  end
  puts result.output if result.output =~ /\S/
end
exit success ? 0 : 1
