import onvif
import os
import asyncio
import urllib.parse
from onvif import ONVIFCamera
from pytapo import Tapo
from .const import ENABLE_MOTION_SENSOR, DOMAIN, LOGGER, CLOUD_PASSWORD
from homeassistant.const import CONF_IP_ADDRESS, CONF_USERNAME, CONF_PASSWORD
from homeassistant.components.onvif.event import EventManager
from homeassistant.components.ffmpeg import DATA_FFMPEG
from haffmpeg.tools import IMAGE_JPEG, ImageFrame


def registerController(host, username, password):
    return Tapo(host, username, password)


async def isRtspStreamWorking(hass, host, username, password):
    _ffmpeg = hass.data[DATA_FFMPEG]
    ffmpeg = ImageFrame(_ffmpeg.binary, loop=hass.loop)
    username = urllib.parse.quote_plus(username)
    password = urllib.parse.quote_plus(password)
    streaming_url = f"rtsp://{username}:{password}@{host}:554/stream1"
    image = await asyncio.shield(
        ffmpeg.get_image(
            streaming_url,
            output_format=IMAGE_JPEG,
        )
    )
    return not image == b""


async def initOnvifEvents(hass, host, username, password):
    device = ONVIFCamera(
        host,
        2020,
        username,
        password,
        f"{os.path.dirname(onvif.__file__)}/wsdl/",
        no_cache=True,
    )
    try:
        await device.update_xaddrs()
        device_mgmt = device.create_devicemgmt_service()
        device_info = await device_mgmt.GetDeviceInformation()
        if "Manufacturer" not in device_info:
            raise Exception("Onvif connection has failed.")

        return device
    except Exception:
        pass

    return False


async def getCamData(hass, controller):
    camData = {}
    presets = await hass.async_add_executor_job(controller.isSupportingPresets)
    camData["user"] = controller.user
    camData["basic_info"] = await hass.async_add_executor_job(controller.getBasicInfo)
    camData["basic_info"] = camData["basic_info"]["device_info"]["basic_info"]
    try:
        motionDetectionData = await hass.async_add_executor_job(
            controller.getMotionDetection
        )
        motion_detection_enabled = motionDetectionData["enabled"]
        if motionDetectionData["digital_sensitivity"] == "20":
            motion_detection_sensitivity = "low"
        elif motionDetectionData["digital_sensitivity"] == "50":
            motion_detection_sensitivity = "normal"
        elif motionDetectionData["digital_sensitivity"] == "80":
            motion_detection_sensitivity = "high"
        else:
            motion_detection_sensitivity = None
    except Exception:
        motion_detection_enabled = None
        motion_detection_sensitivity = None
    camData["motion_detection_enabled"] = motion_detection_enabled
    camData["motion_detection_sensitivity"] = motion_detection_sensitivity

    try:
        privacy_mode = await hass.async_add_executor_job(controller.getPrivacyMode)
        privacy_mode = privacy_mode["enabled"]
    except Exception:
        privacy_mode = None
    camData["privacy_mode"] = privacy_mode

    try:
        alarmData = await hass.async_add_executor_job(controller.getAlarm)
        alarm = alarmData["enabled"]
        alarm_mode = alarmData["alarm_mode"]
    except Exception:
        alarm = None
        alarm_mode = None
    camData["alarm"] = alarm
    camData["alarm_mode"] = alarm_mode

    try:
        commonImageData = await hass.async_add_executor_job(controller.getCommonImage)
        day_night_mode = commonImageData["image"]["common"]["inf_type"]
    except Exception:
        day_night_mode = None
    camData["day_night_mode"] = day_night_mode

    try:
        led = await hass.async_add_executor_job(controller.getLED)
        led = led["enabled"]
    except Exception:
        led = None
    camData["led"] = led

    try:
        auto_track = await hass.async_add_executor_job(controller.getAutoTrackTarget)
        auto_track = auto_track["enabled"]
    except Exception:
        auto_track = None
    camData["auto_track"] = auto_track

    if presets:
        camData["presets"] = presets
    else:
        camData["presets"] = {}

    return camData


async def update_listener(hass, entry):
    """Handle options update."""
    host = entry.data.get(CONF_IP_ADDRESS)
    username = entry.data.get(CONF_USERNAME)
    password = entry.data.get(CONF_PASSWORD)
    motionSensor = entry.data.get(ENABLE_MOTION_SENSOR)
    cloud_password = entry.data.get(CLOUD_PASSWORD)
    try:
        if cloud_password != "":
            tapoController = await hass.async_add_executor_job(
                registerController, host, "admin", cloud_password
            )
        else:
            tapoController = await hass.async_add_executor_job(
                registerController, host, username, password
            )
        hass.data[DOMAIN][entry.entry_id]["controller"] = tapoController
    except Exception:
        LOGGER.error(
            "Authentication to Tapo camera failed."
            + " Please restart the camera and try again."
        )

    for entity in hass.data[DOMAIN][entry.entry_id]["entities"]:
        entity._host = host
        entity._username = username
        entity._password = password
    if hass.data[DOMAIN][entry.entry_id]["events"]:
        await hass.data[DOMAIN][entry.entry_id]["events"].async_stop()
    if hass.data[DOMAIN][entry.entry_id]["motionSensorCreated"]:
        await hass.config_entries.async_forward_entry_unload(entry, "binary_sensor")
        hass.data[DOMAIN][entry.entry_id]["motionSensorCreated"] = False
    if motionSensor:
        await setupOnvif(hass, entry, host, username, password)


async def setupOnvif(hass, entry, host, username, password):
    hass.data[DOMAIN][entry.entry_id]["eventsDevice"] = await initOnvifEvents(
        hass, host, username, password
    )

    if hass.data[DOMAIN][entry.entry_id]["eventsDevice"]:
        hass.data[DOMAIN][entry.entry_id]["events"] = EventManager(
            hass,
            hass.data[DOMAIN][entry.entry_id]["eventsDevice"],
            f"{entry.entry_id}_tapo_events",
        )

        hass.data[DOMAIN][entry.entry_id]["eventsSetup"] = await setupEvents(
            hass, entry
        )


async def setupEvents(hass, entry):
    if not hass.data[DOMAIN][entry.entry_id]["events"].started:
        events = hass.data[DOMAIN][entry.entry_id]["events"]
        if await events.async_start():
            if not hass.data[DOMAIN][entry.entry_id]["motionSensorCreated"]:
                hass.data[DOMAIN][entry.entry_id]["motionSensorCreated"] = True
                hass.async_create_task(
                    hass.config_entries.async_forward_entry_setup(
                        entry, "binary_sensor"
                    )
                )
            return True
        else:
            return False

[(15, 'pytapo.Tapo', 'Tapo', False, 'from pytapo import Tapo\n'), (20, 'haffmpeg.tools.ImageFrame', 'ImageFrame', False, 'from haffmpeg.tools import IMAGE_JPEG, ImageFrame\n'), (169, 'homeassistant.components.onvif.event.EventManager', 'EventManager', False, 'from homeassistant.components.onvif.event import EventManager\n'), (39, 'os.path.dirname', 'os.path.dirname', False, 'import os\n')]