"""Alexa Resources and Assets."""


class AlexaGlobalCatalog:
    """The Global Alexa catalog.

    https://developer.amazon.com/docs/device-apis/resources-and-assets.html#global-alexa-catalog

    You can use the global Alexa catalog for pre-defined names of devices, settings, values, and units.
    This catalog is localized into all the languages that Alexa supports.

    You can reference the following catalog of pre-defined friendly names.
    Each item in the following list is an asset identifier followed by its supported friendly names.
    The first friendly name for each identifier is the one displayed in the Alexa mobile app.
    """

    # Air Purifier, Air Cleaner,Clean Air Machine
    DEVICE_NAME_AIR_PURIFIER = "Alexa.DeviceName.AirPurifier"

    # Fan, Blower
    DEVICE_NAME_FAN = "Alexa.DeviceName.Fan"

    # Router, Internet Router, Network Router, Wifi Router, Net Router
    DEVICE_NAME_ROUTER = "Alexa.DeviceName.Router"

    # Shade, Blind, Curtain, Roller, Shutter, Drape, Awning, Window shade, Interior blind
    DEVICE_NAME_SHADE = "Alexa.DeviceName.Shade"

    # Shower
    DEVICE_NAME_SHOWER = "Alexa.DeviceName.Shower"

    # Space Heater, Portable Heater
    DEVICE_NAME_SPACE_HEATER = "Alexa.DeviceName.SpaceHeater"

    # Washer, Washing Machine
    DEVICE_NAME_WASHER = "Alexa.DeviceName.Washer"

    # 2.4G Guest Wi-Fi, 2.4G Guest Network, Guest Network 2.4G, 2G Guest Wifi
    SETTING_2G_GUEST_WIFI = "Alexa.Setting.2GGuestWiFi"

    # 5G Guest Wi-Fi, 5G Guest Network, Guest Network 5G, 5G Guest Wifi
    SETTING_5G_GUEST_WIFI = "Alexa.Setting.5GGuestWiFi"

    # Auto, Automatic, Automatic Mode, Auto Mode
    SETTING_AUTO = "Alexa.Setting.Auto"

    # Direction
    SETTING_DIRECTION = "Alexa.Setting.Direction"

    # Dry Cycle, Dry Preset, Dry Setting, Dryer Cycle, Dryer Preset, Dryer Setting
    SETTING_DRY_CYCLE = "Alexa.Setting.DryCycle"

    # Fan Speed, Airflow speed, Wind Speed, Air speed, Air velocity
    SETTING_FAN_SPEED = "Alexa.Setting.FanSpeed"

    # Guest Wi-fi, Guest Network, Guest Net
    SETTING_GUEST_WIFI = "Alexa.Setting.GuestWiFi"

    # Heat
    SETTING_HEAT = "Alexa.Setting.Heat"

    # Mode
    SETTING_MODE = "Alexa.Setting.Mode"

    # Night, Night Mode
    SETTING_NIGHT = "Alexa.Setting.Night"

    # Opening, Height, Lift, Width
    SETTING_OPENING = "Alexa.Setting.Opening"

    # Oscillate, Swivel, Oscillation, Spin, Back and forth
    SETTING_OSCILLATE = "Alexa.Setting.Oscillate"

    # Preset, Setting
    SETTING_PRESET = "Alexa.Setting.Preset"

    # Quiet, Quiet Mode, Noiseless, Silent
    SETTING_QUIET = "Alexa.Setting.Quiet"

    # Temperature, Temp
    SETTING_TEMPERATURE = "Alexa.Setting.Temperature"

    # Wash Cycle, Wash Preset, Wash setting
    SETTING_WASH_CYCLE = "Alexa.Setting.WashCycle"

    # Water Temperature, Water Temp, Water Heat
    SETTING_WATER_TEMPERATURE = "Alexa.Setting.WaterTemperature"

    # Handheld Shower, Shower Wand, Hand Shower
    SHOWER_HAND_HELD = "Alexa.Shower.HandHeld"

    # Rain Head, Overhead shower, Rain Shower, Rain Spout, Rain Faucet
    SHOWER_RAIN_HEAD = "Alexa.Shower.RainHead"

    # Degrees, Degree
    UNIT_ANGLE_DEGREES = "Alexa.Unit.Angle.Degrees"

    # Radians, Radian
    UNIT_ANGLE_RADIANS = "Alexa.Unit.Angle.Radians"

    # Feet, Foot
    UNIT_DISTANCE_FEET = "Alexa.Unit.Distance.Feet"

    # Inches, Inch
    UNIT_DISTANCE_INCHES = "Alexa.Unit.Distance.Inches"

    # Kilometers
    UNIT_DISTANCE_KILOMETERS = "Alexa.Unit.Distance.Kilometers"

    # Meters, Meter, m
    UNIT_DISTANCE_METERS = "Alexa.Unit.Distance.Meters"

    # Miles, Mile
    UNIT_DISTANCE_MILES = "Alexa.Unit.Distance.Miles"

    # Yards, Yard
    UNIT_DISTANCE_YARDS = "Alexa.Unit.Distance.Yards"

    # Grams, Gram, g
    UNIT_MASS_GRAMS = "Alexa.Unit.Mass.Grams"

    # Kilograms, Kilogram, kg
    UNIT_MASS_KILOGRAMS = "Alexa.Unit.Mass.Kilograms"

    # Percent
    UNIT_PERCENT = "Alexa.Unit.Percent"

    # Celsius, Degrees Celsius, Degrees, C, Centigrade, Degrees Centigrade
    UNIT_TEMPERATURE_CELSIUS = "Alexa.Unit.Temperature.Celsius"

    # Degrees, Degree
    UNIT_TEMPERATURE_DEGREES = "Alexa.Unit.Temperature.Degrees"

    # Fahrenheit, Degrees Fahrenheit, Degrees F, Degrees, F
    UNIT_TEMPERATURE_FAHRENHEIT = "Alexa.Unit.Temperature.Fahrenheit"

    # Kelvin, Degrees Kelvin, Degrees K, Degrees, K
    UNIT_TEMPERATURE_KELVIN = "Alexa.Unit.Temperature.Kelvin"

    # Cubic Feet, Cubic Foot
    UNIT_VOLUME_CUBIC_FEET = "Alexa.Unit.Volume.CubicFeet"

    # Cubic Meters, Cubic Meter, Meters Cubed
    UNIT_VOLUME_CUBIC_METERS = "Alexa.Unit.Volume.CubicMeters"

    # Gallons, Gallon
    UNIT_VOLUME_GALLONS = "Alexa.Unit.Volume.Gallons"

    # Liters, Liter, L
    UNIT_VOLUME_LITERS = "Alexa.Unit.Volume.Liters"

    # Pints, Pint
    UNIT_VOLUME_PINTS = "Alexa.Unit.Volume.Pints"

    # Quarts, Quart
    UNIT_VOLUME_QUARTS = "Alexa.Unit.Volume.Quarts"

    # Ounces, Ounce, oz
    UNIT_WEIGHT_OUNCES = "Alexa.Unit.Weight.Ounces"

    # Pounds, Pound, lbs
    UNIT_WEIGHT_POUNDS = "Alexa.Unit.Weight.Pounds"

    # Close
    VALUE_CLOSE = "Alexa.Value.Close"

    # Delicates, Delicate
    VALUE_DELICATE = "Alexa.Value.Delicate"

    # High
    VALUE_HIGH = "Alexa.Value.High"

    # Low
    VALUE_LOW = "Alexa.Value.Low"

    # Maximum, Max
    VALUE_MAXIMUM = "Alexa.Value.Maximum"

    # Medium, Mid
    VALUE_MEDIUM = "Alexa.Value.Medium"

    # Minimum, Min
    VALUE_MINIMUM = "Alexa.Value.Minimum"

    # Open
    VALUE_OPEN = "Alexa.Value.Open"

    # Quick Wash, Fast Wash, Wash Quickly, Speed Wash
    VALUE_QUICK_WASH = "Alexa.Value.QuickWash"


class AlexaCapabilityResource:
    """Base class for Alexa capabilityResources, modeResources, and presetResources objects.

    Resources objects labels must be unique across all modeResources and presetResources within the same device.
    To provide support for all supported locales, include one label from the AlexaGlobalCatalog in the labels array.
    You cannot use any words from the following list as friendly names:
    https://developer.amazon.com/docs/alexa/device-apis/resources-and-assets.html#names-you-cannot-use

    https://developer.amazon.com/docs/device-apis/resources-and-assets.html#capability-resources
    """

    def __init__(self, labels):
        """Initialize an Alexa resource."""
        self._resource_labels = []
        for label in labels:
            self._resource_labels.append(label)

    def serialize_capability_resources(self):
        """Return capabilityResources object serialized for an API response."""
        return self.serialize_labels(self._resource_labels)

    @staticmethod
    def serialize_configuration():
        """Return ModeResources, PresetResources friendlyNames serialized for an API response."""
        return []

    @staticmethod
    def serialize_labels(resources):
        """Return resource label objects for friendlyNames serialized for an API response."""
        labels = []
        for label in resources:
            if label in AlexaGlobalCatalog.__dict__.values():
                label = {"@type": "asset", "value": {"assetId": label}}
            else:
                label = {"@type": "text", "value": {"text": label, "locale": "en-US"}}

            labels.append(label)

        return {"friendlyNames": labels}


class AlexaModeResource(AlexaCapabilityResource):
    """Implements Alexa ModeResources.

    https://developer.amazon.com/docs/device-apis/resources-and-assets.html#capability-resources
    """

    def __init__(self, labels, ordered=False):
        """Initialize an Alexa modeResource."""
        super().__init__(labels)
        self._supported_modes = []
        self._mode_ordered = ordered

    def add_mode(self, value, labels):
        """Add mode to the supportedModes object."""
        self._supported_modes.append({"value": value, "labels": labels})

    def serialize_configuration(self):
        """Return configuration for ModeResources friendlyNames serialized for an API response."""
        mode_resources = []
        for mode in self._supported_modes:
            result = {
                "value": mode["value"],
                "modeResources": self.serialize_labels(mode["labels"]),
            }
            mode_resources.append(result)

        return {"ordered": self._mode_ordered, "supportedModes": mode_resources}


class AlexaPresetResource(AlexaCapabilityResource):
    """Implements Alexa PresetResources.

    Use presetResources with RangeController to provide a set of friendlyNames for each RangeController preset.

    https://developer.amazon.com/docs/device-apis/resources-and-assets.html#presetresources
    """

    def __init__(self, labels, min_value, max_value, precision, unit=None):
        """Initialize an Alexa presetResource."""
        super().__init__(labels)
        self._presets = []
        self._minimum_value = min_value
        self._maximum_value = max_value
        self._precision = precision
        self._unit_of_measure = None
        if unit in AlexaGlobalCatalog.__dict__.values():
            self._unit_of_measure = unit

    def add_preset(self, value, labels):
        """Add preset to configuration presets array."""
        self._presets.append({"value": value, "labels": labels})

    def serialize_configuration(self):
        """Return configuration for PresetResources friendlyNames serialized for an API response."""
        configuration = {
            "supportedRange": {
                "minimumValue": self._minimum_value,
                "maximumValue": self._maximum_value,
                "precision": self._precision,
            }
        }

        if self._unit_of_measure:
            configuration["unitOfMeasure"] = self._unit_of_measure

        if self._presets:
            preset_resources = [
                {
                    "rangeValue": preset["value"],
                    "presetResources": self.serialize_labels(preset["labels"]),
                }
                for preset in self._presets
            ]
            configuration["presets"] = preset_resources

        return configuration


class AlexaSemantics:
    """Class for Alexa Semantics Object.

    You can optionally enable additional utterances by using semantics. When you use semantics,
    you manually map the phrases "open", "close", "raise", and "lower" to directives.

    Semantics is supported for the following interfaces only: ModeController, RangeController, and ToggleController.

    Semantics stateMappings are only supported for one interface of the same type on the same device. If a device has
    multiple RangeControllers only one interface may use stateMappings otherwise discovery will fail.

    You can support semantics actionMappings on different controllers for the same device, however each controller must
    support different phrases. For example, you can support "raise" on a RangeController, and "open" on a ModeController,
    but you can't support "open" on both RangeController and ModeController. Semantics stateMappings are only supported
    for one interface on the same device.

    https://developer.amazon.com/docs/device-apis/alexa-discovery.html#semantics-object
    """

    MAPPINGS_ACTION = "actionMappings"
    MAPPINGS_STATE = "stateMappings"

    ACTIONS_TO_DIRECTIVE = "ActionsToDirective"
    STATES_TO_VALUE = "StatesToValue"
    STATES_TO_RANGE = "StatesToRange"

    ACTION_CLOSE = "Alexa.Actions.Close"
    ACTION_LOWER = "Alexa.Actions.Lower"
    ACTION_OPEN = "Alexa.Actions.Open"
    ACTION_RAISE = "Alexa.Actions.Raise"

    STATES_OPEN = "Alexa.States.Open"
    STATES_CLOSED = "Alexa.States.Closed"

    DIRECTIVE_RANGE_SET_VALUE = "SetRangeValue"
    DIRECTIVE_RANGE_ADJUST_VALUE = "AdjustRangeValue"
    DIRECTIVE_TOGGLE_TURN_ON = "TurnOn"
    DIRECTIVE_TOGGLE_TURN_OFF = "TurnOff"
    DIRECTIVE_MODE_SET_MODE = "SetMode"
    DIRECTIVE_MODE_ADJUST_MODE = "AdjustMode"

    def __init__(self):
        """Initialize an Alexa modeResource."""
        self._action_mappings = []
        self._state_mappings = []

    def _add_action_mapping(self, semantics):
        """Add action mapping between actions and interface directives."""
        self._action_mappings.append(semantics)

    def _add_state_mapping(self, semantics):
        """Add state mapping between states and interface directives."""
        self._state_mappings.append(semantics)

    def add_states_to_value(self, states, value):
        """Add StatesToValue stateMappings."""
        self._add_state_mapping(
            {"@type": self.STATES_TO_VALUE, "states": states, "value": value}
        )

    def add_states_to_range(self, states, min_value, max_value):
        """Add StatesToRange stateMappings."""
        self._add_state_mapping(
            {
                "@type": self.STATES_TO_RANGE,
                "states": states,
                "range": {"minimumValue": min_value, "maximumValue": max_value},
            }
        )

    def add_action_to_directive(self, actions, directive, payload):
        """Add ActionsToDirective actionMappings."""
        self._add_action_mapping(
            {
                "@type": self.ACTIONS_TO_DIRECTIVE,
                "actions": actions,
                "directive": {"name": directive, "payload": payload},
            }
        )

    def serialize_semantics(self):
        """Return semantics object serialized for an API response."""
        semantics = {}
        if self._action_mappings:
            semantics[self.MAPPINGS_ACTION] = self._action_mappings
        if self._state_mappings:
            semantics[self.MAPPINGS_STATE] = self._state_mappings

        return semantics
