#!/usr/bin/env python3.2
"""
Script for listing all games in contrib.

games list
games info <game>

"""
from argparse import ArgumentParser
from os import listdir, path
import json
import logging
import re

CONTRIB_DIR = '/contrib/projects/'
GAMES_DIR = path.dirname(path.realpath(__file__)) + '/'
MANUAL_ENTRY_DIR = GAMES_DIR + 'overrides/'

MANUAL_ENTRY_POSTFIX = '.json'
GAME_JSON_FILE = '.games.json'

LOG = logging.getLogger("games")

def main():
	args = set_and_parse_args()
	setup_logging(args.verbose)
	if args.info: show_game_info()
	else: print_games()

def set_and_parse_args():
	parser = ArgumentParser(description='Student contrib games information '
			'tool. Running without arguments will list all the known games in '
			'contrib.') # TODO: Add info about adding your game.
	parser.add_argument('-v', '--verbose', action='count', help='show '
			'debugging information; -vv shows even more debugging information')
	parser.add_argument('-i', '--info', nargs=1, metavar='NAME',
			help='display detailed information about the project specified by '
			'NAME')
	return parser.parse_args()

def setup_logging(verbosity):
	"""Inits logging on the number of -v args received, given as verbosity."""
	if verbosity is None: level = logging.WARNING
	elif verbosity == 1: level = logging.INFO
	else: level = logging.DEBUG
	logging.basicConfig(level=level)

def get_logger(proj_name):
	return LOG.getChild(proj_name);

def print_games():
	"""Finds all games in contrib and displays them to the user."""
	for game in get_games_list():
		if game.url is not None:
			print('*', game.name, '(' + game.url + ')', '-', game.desc)
		else:
			print('*', game.name, '-', game.desc)

def get_games_list():
	"""Returns a list of Game for all the games in contrib."""
	projects = set()

	if path.isdir(CONTRIB_DIR):
		projects |= set(listdir(CONTRIB_DIR))
	else:
		LOG.warning('Contrib directory "%s" does not exist or isn\'t a directory' % CONTRIB_DIR)

	if path.isdir(MANUAL_ENTRY_DIR):
		projects |= {re.sub('\.json$', '', f) for f in listdir(MANUAL_ENTRY_DIR)}
	else:
		LOG.warning('Overrides directory "%s" does not exist or isn\'t a directory' % MANUAL_ENTRY_DIR)

	games = []
	for proj_name in projects:
		game = load_game(proj_name)
		if isinstance(game, Game):
			get_logger(proj_name).info('Successfully loaded')
			games.append(game)
	return games

def load_game(proj_name):
	"""Returns a Game if the given project is found, else returns None."""
	proj_path = CONTRIB_DIR + proj_name + '/' + GAME_JSON_FILE
	manual_entry_path = MANUAL_ENTRY_DIR + proj_name + MANUAL_ENTRY_POSTFIX

	log = get_logger(proj_name)

	json_paths = [proj_path, manual_entry_path]
	for path in json_paths:
		try:
			with open(path) as f:
				game_info = json.load(f)
		except IOError as e:
			# TODO: This is not true (ex: permission denied). Print e instead.
			log.debug(path + ' cannot be accessed')
		except Exception as e: # TODO: Is there a more specific exception?
			log.info(path + ' is not valid JSON: ' + str(e))
		else:
			break
	else:
		log.info('No valid .json file found')
		return None

	try:
		game = Game(proj_name, game_info)
	except Exception as e:
		log.info('Unable to construct Game from json')
		log.debug('Problems with construction: ' + str(e))
		return None
	return game

class Game:
	"""Represents a single contrib/ game."""
	def __init__(self, proj_name, game_info):
		v = self.InitVerifier()

		self.proj_name = proj_name
		self.name = v.getAndVerify(game_info, 'name')
		self.desc = v.getAndVerify(game_info, 'desc')
		self.url = v.getAndVerify(game_info, 'url', False)
		self.loaded_json = game_info

		if not v.valid: raise Exception(v.getLog())

	def __str__(self):
		return self.proj_name + ': ' + json.dumps(self.loaded_json, indent=4)

	class InitVerifier:
		"""Handles verification of Game.__init__ through the given json."""
		log = []
		valid = True

		def getAndVerify(self, d, key, required=True):
			"""Gets d[key] if it exists, otherwise None.

			If a non-existent key is required, an error is logged.
			"""
			if key in d: return d[key]
			elif required:
				self.valid = False
				self.log.append('Missing required "%s" attribute' % key)
			return None

		def getLog(self):
			return '; '.join(self.log)

def show_game_info():
	print('show_game_info(): NOT YET IMPLEMENTED') #TODO

if __name__ == '__main__':
	main()
