#!/usr/bin/python
# -*- coding: utf-8 -*-

import os
import sys
import re
import time
import cPickle
from glob import glob
from ConfigParser import SafeConfigParser
try:
	from paramiko import SSHClient
	ssh = True
except ImportError:
	ssh = False
	sys.stderr.write('please install the package python-paramiko\n')
	sys.exit(1)
	pass

server = 'packman-pmbs.dnsalias.org'
username = None
port = 22022
cachefile = os.path.expanduser('~/.pmbsqueuecache')
configfile = os.path.expanduser('~/.pmbsrc')
oldconfigfile = os.path.expanduser('~/.pmbsqueuerc')
default_show_queue_headers = False
queue_basedir = '/srv/pmbs'
queues = ['jobs-high', 'jobs', 'jobs-low', 'jobs-nightly']
queue_names = {
		'jobs-high': ('high', 'hi'),
		'jobs-low': ('low', 'lo'),
		'jobs': ('normal', '  '),
		'jobs-nightly': ('nightly', 'n8'),
		}
queue_colors = {
		'normal': ('42;33;1', '32'),
		'low': ('43;33;1', '33'),
		'high': ('41;33;1', '31'),
		'nightly': ('40;30;1', '30;1'),
		}

if os.path.exists(cachefile):
	dump = open(cachefile, 'rb')
	uidcache = cPickle.load(dump)
	dump.close()
else:
	uidcache = {}
	pass

config = SafeConfigParser()
config.read([oldconfigfile, configfile])

if config.has_option('server', 'host'):
	server = config.get('server', 'host')
	pass
if config.has_option('server', 'username'):
	username = config.get('server', 'username')
elif config.has_option('server', 'user'):
	username = config.get('server', 'user')
elif config.has_option('user', 'name'):
	username = config.get('user', 'name')
else:
	username = os.environ['USER']
	print >>sys.stderr, """\
WARNING: no configuration option \"name\" found in the section \"[user]\" in %s
         Will guess your username as your current $USER (%s)
         Please create a configuration file named %s
         with the following content:
[User]
name = your_username_on_%s
""" % (configfile, username, configfile, server)
	pass
if config.has_option('server', 'port'):
	port = config.getint('server', 'port')
	pass

if config.has_option('queue', 'headers'):
	show_queue_headers = config.getboolean('queue', 'headers')
elif config.has_option('output', 'headers'):
	show_queue_headers = config.getboolean('output', 'headers')
else:
	show_queue_headers = default_show_queue_headers
	pass

users = [username]
users += map(lambda x: x[1:], filter(lambda x: x.startswith('@'), sys.argv[1:]))

if len(sys.argv) > 1:
	projects = filter(lambda x: not x.startswith('@'), sys.argv[1:])
else:
	projects = map(lambda x: os.path.basename(x)[:-len('.spec')], glob('*.spec'))
	pass

items = {}
len_username = 0
len_filename = 0
qcount = {}

if ssh:
	client = SSHClient()
	client.load_system_host_keys()
	client.connect(hostname=server, username=username, port=port)
	sftp = client.open_sftp()
	missing_uids = {} # use a dict for uniqueness
	for queue in queues:
		items[queue] = []
		qcount[queue] = 0
		# don't use os.path.join, it's the remote system's separator that must be used
		remote_dir = '%s/%s' % (queue_basedir, queue)
		for attr in sftp.listdir_attr(remote_dir):
			qcount[queue] += 1
			items[queue].append({
				"filename": attr.filename,
				"uid": attr.st_uid,
				"mtime": attr.st_mtime
				})
			if not uidcache.has_key(attr.st_uid):
				missing_uids[attr.st_uid] = 1
			else:
				l = len(uidcache[attr.st_uid])
				if l > len_username:
					len_username = l
					pass
				pass
			if len(attr.filename) > len_filename:
				len_filename = len(attr.filename)
				pass
			pass
		pass
	sftp.close()

	# retrieve missing usernames
	if len(missing_uids) > 0:
		sys.stdout.write("retrieving missing UID to username mappings:")
		sys.stdout.flush()
		pass
	for uid in missing_uids.iterkeys():
		sys.stdout.write(" %d=" % uid)
		sys.stdout.flush()
		stdin, stdout, stderr = client.exec_command('getent passwd %d' % uid)
		line = stdout.readline()
		if line:
			line = line.strip(' \r\n')
			(username, password, userid, groupid, finger, homedir, shell) = re.split(':', line)
			sys.stdout.write(username)
			sys.stdout.flush()
			uidcache[uid] = username
			if len(username) > len_username:
				len_username = len(username)
				pass
		else:
			for errline in stderr.readlines():
				print "ERROR: %s" % errline.rstrip('\r\n')
				pass
			sys.stdout.write('notfound')
			sys.stdout.flush()
			pass
		sys.stdout.write('\n')
		pass
	client.close()
else:
	# TODO: fallback strategy by invoking ssh
	pass

if len_filename <= len('PROJECT'):
	len_filename = len('PROJECT')
	pass
len_filename += 1

sys.stdout.write('[%s@%s:%s]\n' % (username, server, port))
header = '\033[4m'
if not show_queue_headers:
	header += 'Q '
	pass
header += ('POS │ %%-%d.%ds │ DATE       │ TIME     │ %%-%d.%ds\033[0m\033[K\n' % \
		(len_username, len_username, len_filename, len_filename)) % ('USER', 'PROJECT')
header_was_shown = False

if show_queue_headers:
	fmt = ""
else:
	fmt = "\033[%sm%s\033[0m"
	pass
fmt += ("%%s%%3d │ %%-%d.%ds │ %%s │ %%-%d.%ds\033[0m\033[K\n" % (len_username, len_username, len_filename, len_filename))

pos = 0
for queue in queues:
	if queue_names.has_key(queue):
		queuename, queueshortname = queue_names[queue]
	else:
		queuename = queue
		queueshortname = queue[:2]
		pass
	if queue_colors.has_key(queuename):
		color = queue_colors[queuename][0]
	else:
		color = ''
		pass

	jobs = items[queue]
	jobs.sort(cmp=lambda x,y: cmp(x['mtime'], y['mtime']))

	if show_queue_headers and qcount[queue] > 0:
		width = 6 + len_username + 27 + len_filename
		sys.stdout.write(('\033[%%sm%%-%d.%ds\033[0m\033[K\n' % (width, width)) % (color, queuename.upper()))
		pass

	for job in jobs:
		pos += 1
		if not header_was_shown:
			sys.stdout.write(header)
			header_was_shown = True
			pass

		if uidcache.has_key(job['uid']):
			username = uidcache[job['uid']]
		else:
			username = job['uid']
			pass
		if job['filename'] in projects:
			hilite = '\033[45;37;1m'
		elif username in users:
			hilite = '\033[46m'
		else:
			hilite = ''
			pass

		if show_queue_headers:
			sys.stdout.write(fmt % (hilite,
				pos, username,
				time.strftime('%Y-%m-%d │ %H:%M:%S', time.localtime(job['mtime'])),
				job['filename']))
		else:
			sys.stdout.write(fmt % (color, queueshortname, hilite,
				pos, username,
				time.strftime('%Y-%m-%d │ %H:%M:%S', time.localtime(job['mtime'])),
				job['filename']))
			pass
		pass
	pass

if pos > 0:
	sys.stdout.write("%d jobs in all queues --" % pos)
	for queue in queues:
		if queue_names.has_key(queue):
			queuename = queue_names[queue][0]
		else:
			queuename = queue
			pass
		if queue_colors.has_key(queuename):
			color = queue_colors[queuename][1]
		else:
			color = ''
			pass
		sys.stdout.write(' \033[%sm%s\033[0m:%d' % (color, queuename, qcount[queue]))
		pass
	pass
else:
	sys.stdout.write('All queues are empty')
	pass

sys.stdout.write("\n")

dump = open(cachefile, 'wb')
cPickle.dump(uidcache, dump)
dump.close()

