# Copyright 2018 Ericsson
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

import collections

from heat.common.i18n import _
from heat.engine import properties
from heat.engine.resources.openstack.neutron import neutron
from heat.engine import support


class L2Gateway(neutron.NeutronResource):
    """A resource for managing Neutron L2 Gateways.

    The are a number of use cases that can be addressed by an L2 Gateway API.
    Most notably in cloud computing environments, a typical use case is
    bridging the virtual with the physical. Translate this to Neutron and the
    OpenStack world, and this means relying on L2 Gateway capabilities to
    extend Neutron logical (overlay) networks into physical (provider)
    networks that are outside the OpenStack realm.
    """

    required_service_extension = 'l2-gateway'

    entity = 'l2_gateway'

    support_status = support.SupportStatus(version='12.0.0')

    PROPERTIES = (
        NAME, DEVICES,
    ) = (
        'name', 'devices',
    )

    _DEVICE_KEYS = (
        DEVICE_NAME, INTERFACES,
    ) = (
        'device_name', 'interfaces',
    )

    _INTERFACE_KEYS = (
        INTERFACE_NAME, SEGMENTATION_ID,
    ) = (
        'name', 'segmentation_id',
    )

    _interface_schema = {
        INTERFACE_NAME: properties.Schema(
            properties.Schema.STRING,
            _('The name of the interface on the gateway device.'),
            required=True
        ),
        SEGMENTATION_ID: properties.Schema(
            properties.Schema.LIST,
            _('A list of segmentation ids of the interface.')
        ),
    }

    _device_schema = {
        DEVICE_NAME: properties.Schema(
            properties.Schema.STRING,
            _('The name of the gateway device.'),
            required=True
        ),
        INTERFACES: properties.Schema(
            properties.Schema.LIST,
            _('List of gateway device interfaces.'),
            schema=properties.Schema(
                properties.Schema.MAP,
                schema=_interface_schema
            ),
            required=True
        )
    }

    properties_schema = {
        NAME: properties.Schema(
            properties.Schema.STRING,
            _('A symbolic name for the l2-gateway, '
              'which is not required to be unique.'),
            required=True,
            update_allowed=True
        ),
        DEVICES: properties.Schema(
            properties.Schema.LIST,
            _('List of gateway devices.'),
            schema=properties.Schema(
                properties.Schema.MAP,
                schema=_device_schema
            ),
            required=True,
            update_allowed=True
        ),
    }

    @staticmethod
    def _remove_none_value_props(props):
        if isinstance(props, collections.abc.Mapping):
            return dict((k, L2Gateway._remove_none_value_props(v)) for k, v
                        in props.items() if v is not None)
        elif (isinstance(props, collections.abc.Sequence) and
              not isinstance(props, str)):
            return list(L2Gateway._remove_none_value_props(p) for p in props
                        if p is not None)
        return props

    @staticmethod
    def prepare_properties(properties, name):
        # Overrides method from base class NeutronResource to ensure None
        # values are removed from all levels of value_specs.

        # TODO(neatherweb): move this recursive check for None to
        # prepare_properties in NeutronResource
        props = L2Gateway._remove_none_value_props(dict(properties))

        if 'name' in properties:
            props.setdefault('name', name)

        return props

    def handle_create(self):
        props = self.prepare_properties(
            self.properties,
            self.physical_resource_name())
        l2gw = self.client().create_l2_gateway(
            {'l2_gateway': props})['l2_gateway']
        self.resource_id_set(l2gw['id'])

    def handle_delete(self):
        if self.resource_id is None:
            return
        with self.client_plugin().ignore_not_found:
            self.client().delete_l2_gateway(self.resource_id)

    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        if prop_diff:
            self.prepare_update_properties(prop_diff)
            prop_diff = L2Gateway._remove_none_value_props(prop_diff)
            self.client().update_l2_gateway(
                self.resource_id, {'l2_gateway': prop_diff})


def resource_mapping():
    return {
        'OS::Neutron::L2Gateway': L2Gateway,
    }
