"""Demo platform for the geolocation component."""
from datetime import timedelta
import logging
from math import cos, pi, radians, sin
import random
from typing import Optional

from homeassistant.helpers.event import track_time_interval

from homeassistant.components.geo_location import GeolocationEvent

_LOGGER = logging.getLogger(__name__)

AVG_KM_PER_DEGREE = 111.0
DEFAULT_UNIT_OF_MEASUREMENT = "km"
DEFAULT_UPDATE_INTERVAL = timedelta(minutes=1)
MAX_RADIUS_IN_KM = 50
NUMBER_OF_DEMO_DEVICES = 5

EVENT_NAMES = [
    "Bushfire",
    "Hazard Reduction",
    "Grass Fire",
    "Burn off",
    "Structure Fire",
    "Fire Alarm",
    "Thunderstorm",
    "Tornado",
    "Cyclone",
    "Waterspout",
    "Dust Storm",
    "Blizzard",
    "Ice Storm",
    "Earthquake",
    "Tsunami",
]

SOURCE = "demo"


def setup_platform(hass, config, add_entities, discovery_info=None):
    """Set up the Demo geolocations."""
    DemoManager(hass, add_entities)


class DemoManager:
    """Device manager for demo geolocation events."""

    def __init__(self, hass, add_entities):
        """Initialise the demo geolocation event manager."""
        self._hass = hass
        self._add_entities = add_entities
        self._managed_devices = []
        self._update(count=NUMBER_OF_DEMO_DEVICES)
        self._init_regular_updates()

    def _generate_random_event(self):
        """Generate a random event in vicinity of this HA instance."""
        home_latitude = self._hass.config.latitude
        home_longitude = self._hass.config.longitude

        # Approx. 111km per degree (north-south).
        radius_in_degrees = random.random() * MAX_RADIUS_IN_KM / AVG_KM_PER_DEGREE
        radius_in_km = radius_in_degrees * AVG_KM_PER_DEGREE
        angle = random.random() * 2 * pi
        # Compute coordinates based on radius and angle. Adjust longitude value
        # based on HA's latitude.
        latitude = home_latitude + radius_in_degrees * sin(angle)
        longitude = home_longitude + radius_in_degrees * cos(angle) / cos(
            radians(home_latitude)
        )

        event_name = random.choice(EVENT_NAMES)
        return DemoGeolocationEvent(
            event_name, radius_in_km, latitude, longitude, DEFAULT_UNIT_OF_MEASUREMENT
        )

    def _init_regular_updates(self):
        """Schedule regular updates based on configured time interval."""
        track_time_interval(
            self._hass, lambda now: self._update(), DEFAULT_UPDATE_INTERVAL
        )

    def _update(self, count=1):
        """Remove events and add new random events."""
        # Remove devices.
        for _ in range(1, count + 1):
            if self._managed_devices:
                device = random.choice(self._managed_devices)
                if device:
                    _LOGGER.debug("Removing %s", device)
                    self._managed_devices.remove(device)
                    self._hass.add_job(device.async_remove())
        # Generate new devices from events.
        new_devices = []
        for _ in range(1, count + 1):
            new_device = self._generate_random_event()
            _LOGGER.debug("Adding %s", new_device)
            new_devices.append(new_device)
            self._managed_devices.append(new_device)
        self._add_entities(new_devices)


class DemoGeolocationEvent(GeolocationEvent):
    """This represents a demo geolocation event."""

    def __init__(self, name, distance, latitude, longitude, unit_of_measurement):
        """Initialize entity with data provided."""
        self._name = name
        self._distance = distance
        self._latitude = latitude
        self._longitude = longitude
        self._unit_of_measurement = unit_of_measurement

    @property
    def source(self) -> str:
        """Return source value of this external event."""
        return SOURCE

    @property
    def name(self) -> Optional[str]:
        """Return the name of the event."""
        return self._name

    @property
    def should_poll(self):
        """No polling needed for a demo geolocation event."""
        return False

    @property
    def distance(self) -> Optional[float]:
        """Return distance value of this external event."""
        return self._distance

    @property
    def latitude(self) -> Optional[float]:
        """Return latitude value of this external event."""
        return self._latitude

    @property
    def longitude(self) -> Optional[float]:
        """Return longitude value of this external event."""
        return self._longitude

    @property
    def unit_of_measurement(self):
        """Return the unit of measurement."""
        return self._unit_of_measurement
