"""
Provides functionality to interact with image processing services.

For more details about this component, please refer to the documentation at
https://home-assistant.io/components/image_processing/
"""
import asyncio
from datetime import timedelta
import logging
import os

import voluptuous as vol

import homeassistant.helpers.config_validation as cv
from homeassistant.config import load_yaml_config_file
from homeassistant.const import (
    ATTR_ENTITY_ID, CONF_NAME, CONF_ENTITY_ID)
from homeassistant.exceptions import HomeAssistantError
from homeassistant.loader import bind_hass
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.loader import get_component

_LOGGER = logging.getLogger(__name__)

DOMAIN = 'image_processing'
DEPENDENCIES = ['camera']

SCAN_INTERVAL = timedelta(seconds=10)

DEVICE_CLASSES = [
    'alpr',        # Automatic license plate recognition
    'face',        # Face
    'ocr',         # OCR
]

SERVICE_SCAN = 'scan'

ATTR_CONFIDENCE = 'confidence'

CONF_SOURCE = 'source'
CONF_CONFIDENCE = 'confidence'

DEFAULT_TIMEOUT = 10
DEFAULT_CONFIDENCE = 80

SOURCE_SCHEMA = vol.Schema({
    vol.Required(CONF_ENTITY_ID): cv.entity_id,
    vol.Optional(CONF_NAME): cv.string,
})

PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
    vol.Optional(CONF_SOURCE): vol.All(cv.ensure_list, [SOURCE_SCHEMA]),
    vol.Optional(CONF_CONFIDENCE, default=DEFAULT_CONFIDENCE):
        vol.All(vol.Coerce(float), vol.Range(min=0, max=100))
})

SERVICE_SCAN_SCHEMA = vol.Schema({
    vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
})


@bind_hass
def scan(hass, entity_id=None):
    """Force process a image."""
    data = {ATTR_ENTITY_ID: entity_id} if entity_id else None
    hass.services.call(DOMAIN, SERVICE_SCAN, data)


@asyncio.coroutine
def async_setup(hass, config):
    """Set up image processing."""
    component = EntityComponent(_LOGGER, DOMAIN, hass, SCAN_INTERVAL)

    yield from component.async_setup(config)

    descriptions = yield from hass.async_add_job(
        load_yaml_config_file,
        os.path.join(os.path.dirname(__file__), 'services.yaml'))

    @asyncio.coroutine
    def async_scan_service(service):
        """Service handler for scan."""
        image_entities = component.async_extract_from_service(service)

        update_task = [entity.async_update_ha_state(True) for
                       entity in image_entities]
        if update_task:
            yield from asyncio.wait(update_task, loop=hass.loop)

    hass.services.async_register(
        DOMAIN, SERVICE_SCAN, async_scan_service,
        descriptions.get(SERVICE_SCAN), schema=SERVICE_SCAN_SCHEMA)

    return True


class ImageProcessingEntity(Entity):
    """Base entity class for image processing."""

    timeout = DEFAULT_TIMEOUT

    @property
    def camera_entity(self):
        """Return camera entity id from process pictures."""
        return None

    @property
    def confidence(self):
        """Return minimum confidence for do some things."""
        return None

    def process_image(self, image):
        """Process image."""
        raise NotImplementedError()

    def async_process_image(self, image):
        """Process image.

        This method must be run in the event loop and returns a coroutine.
        """
        return self.hass.async_add_job(self.process_image, image)

    @asyncio.coroutine
    def async_update(self):
        """Update image and process it.

        This method is a coroutine.
        """
        camera = get_component('camera')
        image = None

        try:
            image = yield from camera.async_get_image(
                self.hass, self.camera_entity, timeout=self.timeout)

        except HomeAssistantError as err:
            _LOGGER.error("Error on receive image from entity: %s", err)
            return

        # process image data
        yield from self.async_process_image(image)
