#!/usr/bin/python3

import re
import os
import sys
import time
import subprocess
from threading import Thread
from math import cos, sin


class GlxThread(Thread):
    """
    Start a thread running glxgears
    """

    def run(self):
        subprocess.call(
            'glxgears -geometry 400x400',
            stdout=open(os.devnull, 'w'),
            stderr=subprocess.STDOUT,
            shell=True)

    def terminate(self):
        subprocess.call('wmctrl -i -c %s' % self.id, shell=True)


class RotateGlxThread(Thread):
    """
    Start a thread performing glxgears windows rotations
    """

    def __init__(self, id, offset):
        Thread.__init__(self)
        self.id = id
        self.offset = offset
        self.cancel = False

    def run(self):
        while(1):
            for j in range(60):
                x = int(200 * self.offset + 100 * sin(j * 0.2))
                y = int(200 * self.offset + 100 * cos(j * 0.2))
                coords = "%s,%s" % (x, y)
                subprocess.call(
                    'wmctrl -i -r %s -e 0,%s,-1,-1' % (self.id, coords),
                    shell=True)
                time.sleep(0.002 * self.offset)
                if self.cancel:
                    return


class ChangeWorkspace(Thread):
    """
    Start a thread performing fast workspace switches
    """

    def __init__(self, size):
        Thread.__init__(self)
        self.size = size
        self.cancel = False

    def run(self):
        while(1):
            for i in range(self.size):
                subprocess.call('wmctrl -s %s' % i, shell=True)
                time.sleep(0.5)
                if self.cancel:
                    # Switch back to workspace #1
                    subprocess.call('wmctrl -s 0', shell=True)
                    return


class ChangeViewport(Thread):
    """
    Start a thread performing fast viewport switches
    """

    def __init__(self, hsize, vsize, xsize, ysize):
        Thread.__init__(self)
        self.hsize = hsize
        self.vsize = vsize
        self.xsize = xsize
        self.ysize = ysize
        self.cancel = False

    def run(self):
        while(1):
            for i in range(self.hsize):
                for j in range(self.vsize):
                    subprocess.call(
                        'wmctrl -o %s,%s' % (self.xsize * j, self.ysize * i),
                        shell=True)
                    time.sleep(0.5)
                    if self.cancel:
                        # Switch back to viewport #1
                        subprocess.call('wmctrl -o 0,0', shell=True)
                        return


class Html5VideoThread(Thread):
    """
    Start a thread performing playback of an HTML5 video in the default browser
    """

    def run(self):
        html5_path = os.path.join(os.getenv('CHECKBOX_SHARE', 'data/websites/html5_video.html'))
        subprocess.call(
            'sudo -H -u %s xdg-open %s' % (os.getenv('SUDO_USER'), html5_path),
            stdout=open(os.devnull, 'w'),
            stderr=subprocess.STDOUT,
            shell=True)

    def terminate(self):
        subprocess.call('wmctrl -c firefox', shell=True)


def pidof(name):
    for pid in os.listdir("/proc"):
        if not pid.isdigit():
            continue
        try:
            exe = os.readlink(os.path.join("/proc", pid, "exe"))
        except OSError:
            continue
        if os.path.basename(exe) == name:
            return int(pid)
    return 0


def is_unity_2d_running():
    return pidof("unity-2d-panel") > 0


def check_gpu():
    f = open('/var/log/kern.log', 'r')
    with f:
        if re.findall(r'gpu\s+hung', f.read(), flags=re.I):
            print("GPU hung Detected")
            sys.exit(1)


def main():
    if not (os.geteuid() == 0):
        print("Must be run as root.")
        return 1

    check_gpu()
    GlxWindows = []
    GlxRotate = []
    subprocess.call("pkill 'glxgears|firefox'", shell=True)

    Html5Video = Html5VideoThread()
    Html5Video.start()

    for i in range(2):
        GlxWindows.append(GlxThread())
        GlxWindows[i].start()
        time.sleep(5)
        windows = subprocess.check_output(
            'wmctrl -l | grep glxgears',
            shell=True)
        for app in sorted(windows.splitlines(), reverse=True):
            if not b'glxgears' in app:
                continue
            GlxWindows[i].id = str(
                re.match(b'^(0x\w+)', app).group(0), 'utf-8')
            break
        GlxRotate.append(RotateGlxThread(GlxWindows[i].id, i + 1))
        GlxRotate[i].start()

    if is_unity_2d_running():
        size = int(subprocess.check_output(
            'gconftool --get /apps/metacity/general/num_workspaces',
            shell=True))
        DesktopSwitch = ChangeWorkspace(size)
    else:
        (x_res, y_res) = re.search(b'DG:\s+(\d+)x(\d+)',
            subprocess.check_output('wmctrl -d', shell=True)).groups()
        hsize = int(subprocess.check_output(
            'gconftool --get /apps/compiz-1/general/screen0/options/hsize',
            shell=True))
        vsize = int(subprocess.check_output(
            'gconftool --get /apps/compiz-1/general/screen0/options/vsize',
            shell=True))
        DesktopSwitch = ChangeViewport(
            hsize, vsize, int(x_res) // hsize, int(y_res) // vsize)
    DesktopSwitch.start()

    time.sleep(20)
    # Suspend/resume the SUT
    subprocess.call('fwts -q s3 --s3-sleep-delay=30', shell=True)
    time.sleep(20)

    for i in range(2):
        GlxRotate[i].cancel = True
        GlxWindows[i].terminate()
    DesktopSwitch.cancel = True
    time.sleep(10)
    Html5Video.terminate()
    check_gpu()

if __name__ == '__main__':
    sys.exit(main())
