#!/usr/bin/python

"""
Moonlight Menu

Usage:
	mmenu.py [-c COLUMNS] [-r ROWS]

Options:
    -h --help       Show this screen.
    -c COLUMNS      Number of columns to display
    -r ROWS         Number of rows to display
"""

import math
import os
import subprocess
import sys

from colorama import Fore, Style, init
from docopt import docopt
from itertools import islice
from pyparsing import OneOrMore, Word, Literal, nums, restOfLine

# pyparsing expressions
game_expr = (OneOrMore(Word(nums) + Literal('. '))).suppress() + restOfLine().setResultsName('Game_Name')
index_expr = (OneOrMore(Word(nums)))


def chunk(it, size):
	"""return the given iterable object in checks of the specified size.

	:param obj it: An iterable object
	:param int size: The maximum size of each chunk

	:return:  A list of lists
	:rtype: list
	"""
	it = iter(it)
	return iter(lambda: tuple(islice(it, size)), ())


def get_choice(list_of_games, num_games, num_pages=None, current_page=None):
	"""get the choice from the user.

	:param list list_of_games:
		The list of games to choose from
	:param int num_games:
		The number of games to choose from
	:param int num_pages:
		The number of pages (of choices) to display
	:param int current_page:
		The currently displayed page number

	:returns: Either an int representing the index of the chosen game or an alpha representing a program choice
	:rtype: obj
	"""
	if current_page == 0:
		text = Fore.WHITE + 'Options: Display (' + Fore.GREEN + 'N' + Fore.WHITE + ')ext page, (' + Fore.MAGENTA + \
		       'C' + Fore.WHITE + ')urrent page, (' + Fore.RED + 'Q' + Fore.WHITE + ')uit or enter the ' + Fore.CYAN + \
		       'Number' + Fore.WHITE + ' of the game to play'
	else:
		text = Fore.WHITE + 'Options: Display (' + Fore.BLUE + 'P' + Fore.WHITE + ')revious page, (' + Fore.GREEN + \
		       'N' + Fore.WHITE + ')ext page, (' + Fore.MAGENTA + 'C' + Fore.WHITE + ')urrent page, (' + \
		       Fore.RED + 'Q' + Fore.WHITE + ')uit or enter the ' + Fore.CYAN + 'Number' + Fore.WHITE + ' of the game to play'

	print '\n' + text
	index = raw_input(Fore.WHITE + Style.BRIGHT + 'What would you like to do?: ').lower()
	while index != 'p' or index != 'n' or index != 'd' or index.isdigit():
		if index == 'c':
			os.system('clear')
			if num_pages:
				list_columns(list_of_games)
				print '\nDisplaying page {} of {}'.format(current_page, num_pages)
			else:
				list_columns(list_of_games)
			print text
		elif index == 'p':
			break
		elif index == 'n':
			break
		elif index == 'q':
			sys.exit()
		elif index.isdigit():
			if 0 < int(index) < num_games:
				break
			else:
				print Fore.RED + '\nSorry that is not a valid choice!'
			print text
		index = raw_input(Fore.WHITE + Style.BRIGHT + 'What would you like to do?: ')

	return index


def list_columns(obj, cols=2, columnwise=True, gap=4):
	"""
	Print the given list in evenly-spaced columns.

	Parameters
	----------
	:param list obj:
		The list to be printed.
	:param int cols:
		The number of columns in which the list should be printed.
	:param bool columnwise: default=True
		If True, the items in the list will be printed column-wise.
		If False the items in the list will be printed row-wise.
	:param int gap:
		The number of spaces that should separate the longest column
		item/s from the next column. This is the effective spacing
		between columns based on the maximum len() of the list items.
	"""

	sobj = [str(item) for item in obj]
	if cols > len(sobj): cols = len(sobj)
	max_len = max([len(item) for item in sobj])
	if columnwise: cols = int(math.ceil(float(len(sobj)) / float(cols)))
	plist = [sobj[i: i + cols] for i in range(0, len(sobj), cols)]
	if columnwise:
		if not len(plist[-1]) == cols:
			plist[-1].extend([''] * (len(sobj) - len(plist[-1])))
		plist = zip(*plist)
	printer = '\n'.join([
		''.join([c.ljust(max_len + gap) for c in p])
		for p in plist])
	print printer


def main(args):
	num_cols = num_rows = lines = 0

	if args['-c'] is not None:
		num_cols = int(args['-c'])
	else:
		num_cols = 2
	if args['-r'] is not None:
		num_rows = int(args['-r'])
	else:
		num_rows = 25

	init()  # innitialize colorama

	# get rid of the output we don't need
	output = subprocess.check_output(['/usr/bin/moonlight', 'list']).split('\n')
	if output[0] == 'Searching for server...':
		output.pop(0)
	if 'Connect to ' in output[0]:
		output.pop(0)

	display_page = None
	foo = None
	index = 0
	game_list = []
	result_list = []

	# get the list of games (striping the soon to be invalid numbers)
	for line in output:
		if line == '':
			pass
		else:
			result = game_expr.parseString(line)
			result_list.append(result['Game_Name'])

	result_list.sort()
	# renumber the game list
	for index, value in enumerate(result_list, start=1):
		game_list.append(str(index) + '. ' + value)

	lines = len(game_list) / num_cols
	os.system('clear')
	if len(game_list) <= lines:
		list_columns(game_list, num_cols)
		index = get_choice(game_list, len(game_list))
	else:
		index = 'a'
		current_page = 0
		foo = list(chunk(game_list, num_rows * num_cols))
		num_pages = len(foo)
		while not index.isdigit():
			list_columns(list(foo[current_page]), num_cols)
			if current_page == 0:
				display_page = 1
			print '\nDisplaying page {} of {}'.format(display_page, num_pages)
			index = get_choice(list(foo[current_page]), len(game_list), num_pages=num_pages, current_page=current_page)
			if index == 'p':
				current_page -= 1
				if current_page > 0:
					display_page = current_page + 1
				else:
					display_page = 1
			elif index == 'n':
				current_page += 1
				display_page = current_page + 1

	game_to_play = int(index) - 1
	result = game_expr.parseString(game_list[game_to_play])
	print Fore.WHITE + 'Now launching ' + Fore.MAGENTA + result['Game_Name'] + Fore.WHITE + '...'
	command = 'moonlight stream -1080 -app ' + '"' + result['Game_Name'] + '"'
	try:
		os.system(command)
	except subprocess.CalledProcessError:
		print 'Moonlight needs to be installed and in the path'
		sys.exit(-1)


if __name__ == '__main__':
	args = docopt(__doc__)
	main(args)