#! /usr/bin/python3
# Copyright (C) 2018-2020 Merspieler, merspieler _at_ airmail.cc
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

from time import sleep
from subprocess import Popen
from signal import SIGINT
import threading
import re
import sys

worker_cmd = ["./scripts/worldbuild-worker.py"]

argc = len(sys.argv)
i = 1
first = 1
while i < argc:
	if sys.argv[i] == "-h" or sys.argv[i] == "--help":
		print("usage: worldbuild-runner.py [OPTIONS] [WORKER-OPTIONS]")
		print("Starts workers")
		print("")
		print("  -h, --help       Shows this help and exit")
		print("")
		print("All other options get passed through to the worker")
		sys.exit(0)
	else:
		worker_cmd.append(sys.argv[i])
	i += 1

class launcher(threading.Thread):
	def __init__(self):
		threading.Thread.__init__(self)
		self.running = []
		self.stopping = []
		self.run_count = 0
		self.stop_count = 0
		self.run_max = 0
		self.stop = False

	def run(self):
		while not self.stop:
			sleep(5)

			i = 0
			while i < self.stop_count:
				if self.stopping[i].poll() != None:
					del self.stopping[i]
					self.stop_count -= 1
				else: # Only increase by one if we don't delete the first element in the array
					i += 1

			i = 0
			while i < self.run_count:
				if self.running[i].poll() != None:
					self.run_count -= 1
					self.run_max -= 1
					print("WARNING: Worker reported nothing to do. Reducing workers to " + str(self.run_max))
					del self.running[i]
				else: # Only increase by one if we don't delete the first element in the array
					i += 1

			while self.run_count + self.stop_count < self.run_max:
				self.start_worker()

	def set_proc_count(self, count):
		if count > self.run_max:
			while count > self.run_count + self.stop_count:
				self.start_worker()

		while count < self.run_count:
			self.running[self.run_count - 1].send_signal(SIGINT)
			self.stopping.append(self.running[self.run_count - 1])
			del self.running[self.run_count - 1]
			self.run_count -= 1
			self.stop_count += 1

		self.run_max = count

	def start_worker(self):
		self.running.append(Popen(worker_cmd, start_new_session=True))
		self.run_count += 1
		

l = launcher()
l.start()

running = True
while running:
	try:
		cmd = input("> ")
		if cmd == "quit":
			running = False
			break
		elif cmd == "help":
			print("Commands:")
			print("help               shows this help")
			print("set workers <n>    sets number of workers to <n>")
			print("status             shows number of running workers")
			print("quit               stops all workers, then exits")
		elif cmd == "status" or cmd == "sts":
			print("Current status:")
			print("Max running:   " + str(l.run_max))
			print("Running:       " + str(l.run_count))
			print("Shutting down: " + str(l.stop_count))
		else:
			match = re.match("(set workers|sw) ([0-9]+)", cmd)
			if match != None:
				if match.group(1) == "set workers" or match.group(1) == "sw":
					l.set_proc_count(int(match.group(2)))
			else:
				print("Invalid command. Type 'help' to see available commands")
	except (KeyboardInterrupt, EOFError):
		print("Please type 'quit' to exit the program")

l.set_proc_count(0)

shutting_down = True
while shutting_down:
	try:
		sleep(5)
		if l.stop_count == 0 and l.run_count == 0:
			shutting_down = False
			l.stop = True
			l.join()
	except (KeyboardInterrupt, EOFError):
		print("Please wait untill all workers are done")