#!/usr/bin/env python3
"""Checks whether bambam works.

This is intended to be run by autopkgtest, with the environment set up to
connect to an Xvfb server in order to take screenshots from its XWD output
file.

See the main() function for a high-level overview of what the script does.
"""

import logging
import os
import random
import re
import subprocess
import time


_COLOR_PATTERN = re.compile(r'(\d+),(\d+),(\d+)')
_EXIT_SECONDS = 5


def main():
    bambam = subprocess.Popen(['/usr/games/bambam'])
    try:
        await_startup()
        test_functionality()
        shut_bambam_down()
        logging.info('Waiting for game to exit cleanly.')
        exit_code = bambam.wait(timeout=_EXIT_SECONDS)
        if exit_code != 0:
            raise Exception('Bambam exited with unexpected code %d.' % exit_code)
        logging.info('Test successful.')
    except:
        take_screenshot('exception')
        raise


def await_startup():
    logging.info('Polling to observe a mostly-white screen (which means that bambam has started).')
    attempt_count = 40
    sleep_delay = 0.25
    for attempt in range(attempt_count):
        current_average_color = get_average_color()
        logging.info('On attempt %d the average screen color was %s.', attempt, current_average_color)
        if all(color_component >= 248 for color_component in current_average_color):
            logging.info('Found mostly white screen, looks like bambam started up OK.')
            take_screenshot('startup')
            return
        time.sleep(sleep_delay)
    raise Exception('Failed to see bambam start and display mostly-white background, after polling %d times every %f seconds.' % (attempt_count, sleep_delay))


def test_functionality():
    logging.info('Simulating space and letter keypresses and measuring average screen color, to test functionality.')
    attempt_count = 1000
    for attempt in range(attempt_count):
        send_keycodes('space', 'm')  # any letter will do, but em is nice and big
        send_mouse_move()
        time.sleep(0.005)  # let the event propagate and bambam process it
        if is_screen_colorful_enough(attempt):
            take_screenshot('success')
            return
    raise Exception('Failed to see a colorful enough screen after %d key presses.' % (attempt_count * 2))


def is_screen_colorful_enough(attempt):
    r, g, b = get_average_color()
    if any(color > 225 for color in (r, g, b)):
        logging.info('On attempt %d the average screen color was too close to white: %d,%d,%d.', attempt, r, g, b)
        return False
    else:
        logging.info('Found colorful enough screen, colors %d, %d, %d.', r, g, b)
        return True


def shut_bambam_down():
    logging.info('Simulating shutdown keypresses.')
    send_keycodes('q', 'u', 'i', 't')


def take_screenshot(title):
    file_name = os.path.join(os.environ['AUTOPKGTEST_ARTIFACTS'], '%s.png' % title)
    subprocess.call([
        'convert',
        'xwd:' + os.path.join(os.environ['AUTOPKGTEST_TMP'], 'Xvfb_screen0'),
        file_name])
    logging.info('Took a screenshot to: %s', file_name)


def get_average_color():
    color_str = subprocess.check_output([
        'convert',
        'xwd:' + os.path.join(os.environ['AUTOPKGTEST_TMP'], 'Xvfb_screen0'),
        '-resize', '1x1!',
        '-format', '%[fx:int(r*255)],%[fx:int(g*255)],%[fx:int(b*255)]',
        'info:-']).decode()
    m = _COLOR_PATTERN.match(color_str)
    if not m:
        raise Exception('Failed to parse color ' + color_str)
    return [int(i) for i in m.group(1, 2, 3)]


def xdotool(*args):
    subprocess.check_call(['xdotool', 'search', '--class', 'bambam'] + list(args))


def send_keycodes(*keycodes):
    xdotool('key', *keycodes)


def random_move():
    return random.choice(['+', '-']) + str(random.choice(range(1, 50)))


def send_mouse_move():
    x, y = random_move(), random_move()
    xdotool('mousedown', '1', 'mousemove_relative', '--', x, y, 'mouseup', '1')


if __name__ == '__main__':
    logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s: %(message)s")
    main()
