"""Support for Amcrest IP cameras."""
import logging
from datetime import timedelta

import aiohttp
import voluptuous as vol
from requests.exceptions import HTTPError, ConnectTimeout
from requests.exceptions import ConnectionError as ConnectError

from homeassistant.const import (
    CONF_NAME, CONF_HOST, CONF_PORT, CONF_USERNAME, CONF_PASSWORD,
    CONF_SENSORS, CONF_SWITCHES, CONF_SCAN_INTERVAL, HTTP_BASIC_AUTHENTICATION)
from homeassistant.helpers import discovery
import homeassistant.helpers.config_validation as cv

REQUIREMENTS = ['amcrest==1.2.3']
DEPENDENCIES = ['ffmpeg']

_LOGGER = logging.getLogger(__name__)

CONF_AUTHENTICATION = 'authentication'
CONF_RESOLUTION = 'resolution'
CONF_STREAM_SOURCE = 'stream_source'
CONF_FFMPEG_ARGUMENTS = 'ffmpeg_arguments'

DEFAULT_NAME = 'Amcrest Camera'
DEFAULT_PORT = 80
DEFAULT_RESOLUTION = 'high'
DEFAULT_STREAM_SOURCE = 'snapshot'
TIMEOUT = 10

DATA_AMCREST = 'amcrest'
DOMAIN = 'amcrest'

NOTIFICATION_ID = 'amcrest_notification'
NOTIFICATION_TITLE = 'Amcrest Camera Setup'

RESOLUTION_LIST = {
    'high': 0,
    'low': 1,
}

SCAN_INTERVAL = timedelta(seconds=10)

AUTHENTICATION_LIST = {
    'basic': 'basic'
}

STREAM_SOURCE_LIST = {
    'mjpeg': 0,
    'snapshot': 1,
    'rtsp': 2,
}

# Sensor types are defined like: Name, units, icon
SENSORS = {
    'motion_detector': ['Motion Detected', None, 'mdi:run'],
    'sdcard': ['SD Used', '%', 'mdi:sd'],
    'ptz_preset': ['PTZ Preset', None, 'mdi:camera-iris'],
}

# Switch types are defined like: Name, icon
SWITCHES = {
    'motion_detection': ['Motion Detection', 'mdi:run-fast'],
    'motion_recording': ['Motion Recording', 'mdi:record-rec']
}

CONFIG_SCHEMA = vol.Schema({
    DOMAIN: vol.All(cv.ensure_list, [vol.Schema({
        vol.Required(CONF_HOST): cv.string,
        vol.Required(CONF_USERNAME): cv.string,
        vol.Required(CONF_PASSWORD): cv.string,
        vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
        vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
        vol.Optional(CONF_AUTHENTICATION, default=HTTP_BASIC_AUTHENTICATION):
            vol.All(vol.In(AUTHENTICATION_LIST)),
        vol.Optional(CONF_RESOLUTION, default=DEFAULT_RESOLUTION):
            vol.All(vol.In(RESOLUTION_LIST)),
        vol.Optional(CONF_STREAM_SOURCE, default=DEFAULT_STREAM_SOURCE):
            vol.All(vol.In(STREAM_SOURCE_LIST)),
        vol.Optional(CONF_FFMPEG_ARGUMENTS): cv.string,
        vol.Optional(CONF_SCAN_INTERVAL, default=SCAN_INTERVAL):
            cv.time_period,
        vol.Optional(CONF_SENSORS):
            vol.All(cv.ensure_list, [vol.In(SENSORS)]),
        vol.Optional(CONF_SWITCHES):
            vol.All(cv.ensure_list, [vol.In(SWITCHES)]),
    })])
}, extra=vol.ALLOW_EXTRA)


def setup(hass, config):
    """Set up the Amcrest IP Camera component."""
    from amcrest import AmcrestCamera

    hass.data[DATA_AMCREST] = {}
    amcrest_cams = config[DOMAIN]

    for device in amcrest_cams:
        try:
            camera = AmcrestCamera(device.get(CONF_HOST),
                                   device.get(CONF_PORT),
                                   device.get(CONF_USERNAME),
                                   device.get(CONF_PASSWORD)).camera
            # pylint: disable=pointless-statement
            camera.current_time

        except (ConnectError, ConnectTimeout, HTTPError) as ex:
            _LOGGER.error("Unable to connect to Amcrest camera: %s", str(ex))
            hass.components.persistent_notification.create(
                'Error: {}<br />'
                'You will need to restart hass after fixing.'
                ''.format(ex),
                title=NOTIFICATION_TITLE,
                notification_id=NOTIFICATION_ID)
            continue

        ffmpeg_arguments = device.get(CONF_FFMPEG_ARGUMENTS)
        name = device.get(CONF_NAME)
        resolution = RESOLUTION_LIST[device.get(CONF_RESOLUTION)]
        sensors = device.get(CONF_SENSORS)
        switches = device.get(CONF_SWITCHES)
        stream_source = STREAM_SOURCE_LIST[device.get(CONF_STREAM_SOURCE)]

        username = device.get(CONF_USERNAME)
        password = device.get(CONF_PASSWORD)

        # currently aiohttp only works with basic authentication
        # only valid for mjpeg streaming
        if username is not None and password is not None:
            if device.get(CONF_AUTHENTICATION) == HTTP_BASIC_AUTHENTICATION:
                authentication = aiohttp.BasicAuth(username, password)
            else:
                authentication = None

        hass.data[DATA_AMCREST][name] = AmcrestDevice(
            camera, name, authentication, ffmpeg_arguments, stream_source,
            resolution)

        discovery.load_platform(
            hass, 'camera', DOMAIN, {
                CONF_NAME: name,
            }, config)

        if sensors:
            discovery.load_platform(
                hass, 'sensor', DOMAIN, {
                    CONF_NAME: name,
                    CONF_SENSORS: sensors,
                }, config)

        if switches:
            discovery.load_platform(
                hass, 'switch', DOMAIN, {
                    CONF_NAME: name,
                    CONF_SWITCHES: switches
                }, config)

    return True


class AmcrestDevice:
    """Representation of a base Amcrest discovery device."""

    def __init__(self, camera, name, authentication, ffmpeg_arguments,
                 stream_source, resolution):
        """Initialize the entity."""
        self.device = camera
        self.name = name
        self.authentication = authentication
        self.ffmpeg_arguments = ffmpeg_arguments
        self.stream_source = stream_source
        self.resolution = resolution
