from __future__ import absolute_import
from contextlib import contextmanager
from multiprocessing import TimeoutError
import signal
import datetime
import subprocess
import time

import pytest

from .adb import ADB
from ..logger import Logger


def get_center(bounds):
    """
    Returns given element center coords::

        from magneto.utils import get_center

        element = self.magneto(text='Foo')
        (x, y) = get_center(element.info['bounds'])

    :param dict bounds: Element position coordinates (top, right, bottom, left)
    :return: x and y coordinates of element center
    """

    x = bounds['right'] - ((bounds['right'] - bounds['left']) / 2)
    y = bounds['bottom'] - ((bounds['bottom'] - bounds['top']) / 2)
    return x, y


def get_config(attr, default=None):
    """
    Allows access to config parameters::

        from magneto.utils import get_config

        package = get_config('--app-package')

    :param str attr: Command line argument
    :return: Requested config value
    """

    # must have this check to avoid sphinx-autodoc exception
    if getattr(pytest, 'config', None) != None:
        return pytest.config.getoption(attr) or default
    else:
        return default


@contextmanager
def timewarp(timedelta_):
    now = datetime.datetime.now()
    future = now + timedelta_
    ADB.set_datetime(future)

    try:
        yield
    finally:
        now = datetime.datetime.now()
        ADB.set_datetime(now)


class Timeout():
    """
    Allows polling a function till success or timeout::

        import time
        from magneto.utils import Timeout

        result = False

        with Timeout(seconds=5):
            while not result:
                result = some_function()
                time.sleep(0.5)


    :param integer seconds: Timeout value in seconds. Defaults to 1.
    :param str error_message: Error message to display when timeout occurs. Defaults to 'Timeout'.
    """

    def __init__(self, seconds=1, error_message='Timeout'):
        self.seconds = seconds or 1
        self.error_message = error_message

    def handle_timeout(self, signum, frame):
        Logger.debug('Timeout reached {} seconds limit'.format(self.seconds))
        raise TimeoutError(self.error_message)

    def __enter__(self):
        Logger.debug('Timeout started for {} seconds'.format(self.seconds))
        signal.signal(signal.SIGALRM, self.handle_timeout)
        signal.alarm(self.seconds)

    def __exit__(self, type, value, traceback):
        Logger.debug('Timeout stopped.')
        signal.alarm(0)


def unlock_device():
    """
    Powers on device and unlocks it.
    """

    # read device screen state
    p = ADB.exec_cmd("shell 'if [ -z $(dumpsys power | grep mScreenOn=true) ]; then echo off; else echo on;fi'",
            stdout=subprocess.PIPE)
    device_screen = p.stdout.readline().strip('\r\n')

    if device_screen == 'off':
        # power on device
        ADB.exec_cmd('shell input keyevent 26').wait()

    # unlock device
    ADB.exec_cmd('shell input keyevent 82').wait()


def wait_for_device():
    """
    Wait for device to boot. 1 minute timeout.
    """

    wait_for_device_cmd = 'wait-for-device shell getprop sys.boot_completed'
    p = ADB.exec_cmd(wait_for_device_cmd, stdout=subprocess.PIPE)
    boot_completed = p.stdout.readline().strip('\r\n')

    try:
        with Timeout(seconds=60):
            while boot_completed != '1':
                time.sleep(1)
                p = ADB.exec_cmd(wait_for_device_cmd, stdout=subprocess.PIPE)
                boot_completed = p.stdout.readline().strip('\r\n')
                Logger.debug('Waiting for device to finish booting (adb shell getprop sys.boot_completed)')
    except TimeoutError:
        Logger.debug('Timed out while waiting for sys.boot_completed, there might not be a default launcher set, trying to run anyway')
        pass