#!/usr/bin/python
# Filename:      hgr
# Purpose:       recursive version of hg
# Authors:       grml-team (grml.org), (c) Michael Gebetsroither <gebi@grml.org>
# Bug-Reports:   see http://grml.org/bugs/
# License:       This file is licensed under the GPL v2.
################################################################################

import os
import sys
import time
import exceptions
import subprocess

from hgutil.ui import Ui
from hgutil.util import execCmd

# colors {{{
NORMAL='[0;39m'
# RED: Failure or error message
RED='[1;31m'
# GREEN: Success message
GREEN='[1;32m'
# YELLOW: Descriptions
YELLOW='[1;33m'
# BLUE: System messages
BLUE='[1;34m'
# MAGENTA: Found devices or drivers
MAGENTA='[1;35m'
# CYAN: Questions
CYAN='[1;36m'
# BOLD WHITE: Hint
WHITE='[1;37m'
# }}}

def ishgrep(dir):
    return os.path.isdir(os.path.join(dir, '.hg'))

dirlist = [x for x in os.listdir('.') if os.path.isdir(x) and ishgrep(x)]


# dict of exited processes
pl_exit = {}
# list of processes in dir order
pl_list = []

ui = Ui()
#ui.debugflag = True

class Scheduler(object):
    def __init__(self):
        self.running = []
    def run(self):
        stopped = []
        idx = 0
        # find dead processes
        for process in self.running[:]:
            if process.poll() != None:
                stopped.append(idx)
                ui.dwrite(repr(process), ": stopped\n")
                del self.running[idx]
            else:
                idx += 1
        return stopped
    def add(self, p):
        self.running.append(p)

class Repos(object):
    def __init__(self, process, dir):
        self.dir = dir
        self.process = process
        self.data_out = []
        self.data_err = []
    def pid(self):
        return self.process.pid
    def poll(self):
        return self.process.poll()
    def output(self):
        for (i, j) in [(self.data_out, self.process.stdout), (self.data_err, self.process.stderr)]:
            i.extend(j.readlines())
        if len(self.data_err) > 0:
            ui.write(RED, self.dir, NORMAL + "\n")
            for line in self.data_err:
                ui.write(line)
        else:
            ui.write(GREEN, self.dir, NORMAL + "\n")
            for line in self.data_out:
                ui.write(line)
    def __repr__(self):
        return "Repos(%s)" % (self.dir)

def process_start(num, list):
    added = []
    ui.dwrite("starting:", num, "\n")
    while num != 0:
        try:    dir = list.pop(0)
        except: return added
        cmd = ['hg']
        cmd.extend(sys.argv[1:])
        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=dir)
        added.append(Repos(proc, dir))
        num -= 1
    return added

def process_stopped(procs, exit_dict, exit_list, in_order=True):
    idx = 0
    for i in exit_list[:]:
        if i.poll() == None:
            ui.dwrite(repr(i), "running\n")
            if in_order:
                return
        else:
            i.output()
            del exit_list[idx]
            del exit_dict[i.dir]
            continue
        idx += 1

###
## MAIN
###

j=7
if len(sys.argv) >= 3:
    if sys.argv[1] == '--j':
        j=abs(int(sys.argv.pop(2)))
        del sys.argv[1]
if len(sys.argv) < 2:
    execCmd(["hg", "help"], "hg", stdout=None)
    sys.exit(1)

sched = Scheduler()
while True:
    num_run = len(sched.running)
    num_torun = len(dirlist)
    # new procs to start
    if num_torun != 0 and num_run < j:
        for i in process_start(j - num_run, dirlist):
            sched.add(i)
            pl_list.append(i)
            pl_exit[i.dir] = i
    process_stopped(sched.run(), pl_exit, pl_list)
    # all procs started
    if num_run == 0 and num_torun == 0:
        ui.dwrite("finished\n")
        break
    time.sleep(0.1)

if len(pl_list) != 0:
    for i in pl_list:
        if i.poll() != None:
            ui.write("%sWarning:%snot ended process in pl_list: %s\n" % (RED, NORMAL, repr(i)))
    ui.write("%sWarning:%selements left in pl_list\n" % (RED, NORMAL))
    ui.dwrite("sched.running: ", sched.running, "\n")
    ui.dwrite("dirlist: ", dirlist, "\n")
    ui.dwrite("pl_list: ", pl_list, "\n")
    ui.dwrite("pl_exit: ", pl_exit, "\n")
    sys.exit(1)

# vim: foldmethod=marker
