# vim: tabstop=4 shiftwidth=4 softtabstop=4

# Copyright 2012 Nicira Networks, Inc.
#
#    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 json
import urlparse

from neutron.openstack.common import log as logging
from neutron.openstack.common import uuidutils
from neutron.plugins.nicira import NvpApiClient


LOG = logging.getLogger(__name__)
MAX_NAME_LEN = 40


def _validate_name(name):
    if name and len(name) > MAX_NAME_LEN:
        raise Exception("Logical switch name exceeds %d characters",
                        MAX_NAME_LEN)


def _validate_resource(body):
    _validate_name(body.get('display_name'))


class FakeClient:

    LSWITCH_RESOURCE = 'lswitch'
    LPORT_RESOURCE = 'lport'
    LROUTER_RESOURCE = 'lrouter'
    NAT_RESOURCE = 'nat'
    LQUEUE_RESOURCE = 'lqueue'
    SECPROF_RESOURCE = 'securityprofile'
    LSWITCH_STATUS = 'lswitchstatus'
    LROUTER_STATUS = 'lrouterstatus'
    LSWITCH_LPORT_RESOURCE = 'lswitch_lport'
    LROUTER_LPORT_RESOURCE = 'lrouter_lport'
    LROUTER_NAT_RESOURCE = 'lrouter_nat'
    LSWITCH_LPORT_STATUS = 'lswitch_lportstatus'
    LSWITCH_LPORT_ATT = 'lswitch_lportattachment'
    LROUTER_LPORT_STATUS = 'lrouter_lportstatus'
    LROUTER_LPORT_ATT = 'lrouter_lportattachment'
    GWSERVICE_RESOURCE = 'gatewayservice'

    RESOURCES = [LSWITCH_RESOURCE, LROUTER_RESOURCE, LQUEUE_RESOURCE,
                 LPORT_RESOURCE, NAT_RESOURCE, SECPROF_RESOURCE,
                 GWSERVICE_RESOURCE]

    FAKE_GET_RESPONSES = {
        LSWITCH_RESOURCE: "fake_get_lswitch.json",
        LSWITCH_LPORT_RESOURCE: "fake_get_lswitch_lport.json",
        LSWITCH_LPORT_STATUS: "fake_get_lswitch_lport_status.json",
        LSWITCH_LPORT_ATT: "fake_get_lswitch_lport_att.json",
        LROUTER_RESOURCE: "fake_get_lrouter.json",
        LROUTER_LPORT_RESOURCE: "fake_get_lrouter_lport.json",
        LROUTER_LPORT_STATUS: "fake_get_lrouter_lport_status.json",
        LROUTER_LPORT_ATT: "fake_get_lrouter_lport_att.json",
        LROUTER_STATUS: "fake_get_lrouter_status.json",
        LROUTER_NAT_RESOURCE: "fake_get_lrouter_nat.json",
        SECPROF_RESOURCE: "fake_get_security_profile.json",
        LQUEUE_RESOURCE: "fake_get_lqueue.json",
        GWSERVICE_RESOURCE: "fake_get_gwservice.json"
    }

    FAKE_POST_RESPONSES = {
        LSWITCH_RESOURCE: "fake_post_lswitch.json",
        LROUTER_RESOURCE: "fake_post_lrouter.json",
        LSWITCH_LPORT_RESOURCE: "fake_post_lswitch_lport.json",
        LROUTER_LPORT_RESOURCE: "fake_post_lrouter_lport.json",
        LROUTER_NAT_RESOURCE: "fake_post_lrouter_nat.json",
        SECPROF_RESOURCE: "fake_post_security_profile.json",
        LQUEUE_RESOURCE: "fake_post_lqueue.json",
        GWSERVICE_RESOURCE: "fake_post_gwservice.json"
    }

    FAKE_PUT_RESPONSES = {
        LSWITCH_RESOURCE: "fake_post_lswitch.json",
        LROUTER_RESOURCE: "fake_post_lrouter.json",
        LSWITCH_LPORT_RESOURCE: "fake_post_lswitch_lport.json",
        LROUTER_LPORT_RESOURCE: "fake_post_lrouter_lport.json",
        LROUTER_NAT_RESOURCE: "fake_post_lrouter_nat.json",
        LSWITCH_LPORT_ATT: "fake_put_lswitch_lport_att.json",
        LROUTER_LPORT_ATT: "fake_put_lrouter_lport_att.json",
        SECPROF_RESOURCE: "fake_post_security_profile.json",
        LQUEUE_RESOURCE: "fake_post_lqueue.json",
        GWSERVICE_RESOURCE: "fake_post_gwservice.json"
    }

    MANAGED_RELATIONS = {
        LSWITCH_RESOURCE: [],
        LROUTER_RESOURCE: [],
        LSWITCH_LPORT_RESOURCE: ['LogicalPortAttachment'],
        LROUTER_LPORT_RESOURCE: ['LogicalPortAttachment'],
    }

    _validators = {
        LSWITCH_RESOURCE: _validate_resource,
        LSWITCH_LPORT_RESOURCE: _validate_resource,
        LROUTER_LPORT_RESOURCE: _validate_resource,
        SECPROF_RESOURCE: _validate_resource,
        LQUEUE_RESOURCE: _validate_resource,
        GWSERVICE_RESOURCE: _validate_resource
    }

    def __init__(self, fake_files_path):
        self.fake_files_path = fake_files_path
        self._fake_lswitch_dict = {}
        self._fake_lrouter_dict = {}
        self._fake_lswitch_lport_dict = {}
        self._fake_lrouter_lport_dict = {}
        self._fake_lrouter_nat_dict = {}
        self._fake_lswitch_lportstatus_dict = {}
        self._fake_lrouter_lportstatus_dict = {}
        self._fake_securityprofile_dict = {}
        self._fake_lqueue_dict = {}
        self._fake_gatewayservice_dict = {}

    def _get_tag(self, resource, scope):
        tags = [tag['tag'] for tag in resource['tags']
                if tag['scope'] == scope]
        return len(tags) > 0 and tags[0]

    def _get_filters(self, querystring):
        if not querystring:
            return (None, None, None, None)
        params = urlparse.parse_qs(querystring)
        tag_filter = None
        attr_filter = None
        if 'tag' in params and 'tag_scope' in params:
            tag_filter = {'scope': params['tag_scope'][0],
                          'tag': params['tag'][0]}
        elif 'uuid' in params:
            attr_filter = {'uuid': params['uuid'][0]}
        # Handle page length and page cursor parameter
        page_len = params.get('_page_length')
        page_cursor = params.get('_page_cursor')
        if page_len:
            page_len = int(page_len[0])
        else:
            # Explicitly set it to None (avoid 0 or empty list)
            page_len = None
        return (tag_filter, attr_filter, page_len, page_cursor)

    def _add_lswitch(self, body):
        fake_lswitch = json.loads(body)
        fake_lswitch['uuid'] = uuidutils.generate_uuid()
        self._fake_lswitch_dict[fake_lswitch['uuid']] = fake_lswitch
        # put the tenant_id and the zone_uuid in the main dict
        # for simplyfying templating
        zone_uuid = fake_lswitch['transport_zones'][0]['zone_uuid']
        fake_lswitch['zone_uuid'] = zone_uuid
        fake_lswitch['tenant_id'] = self._get_tag(fake_lswitch, 'os_tid')
        fake_lswitch['lport_count'] = 0
        # set status value
        fake_lswitch['status'] = 'true'
        return fake_lswitch

    def _build_lrouter(self, body, uuid=None):
        fake_lrouter = json.loads(body)
        if uuid:
            fake_lrouter['uuid'] = uuid
        fake_lrouter['tenant_id'] = self._get_tag(fake_lrouter, 'os_tid')
        default_nexthop = fake_lrouter['routing_config'].get(
            'default_route_next_hop')
        if default_nexthop:
            fake_lrouter['default_next_hop'] = default_nexthop.get(
                'gateway_ip_address', '0.0.0.0')
        else:
            fake_lrouter['default_next_hop'] = '0.0.0.0'
        # NOTE(salv-orlando): We won't make the Fake NVP API client
        # aware of NVP version. The long term plan is to replace it
        # with behavioral mocking of NVP API requests
        if 'distributed' not in fake_lrouter:
            fake_lrouter['distributed'] = False
        distributed_json = ('"distributed": %s,' %
                            str(fake_lrouter['distributed']).lower())
        fake_lrouter['distributed_json'] = distributed_json
        return fake_lrouter

    def _add_lrouter(self, body):
        fake_lrouter = self._build_lrouter(body,
                                           uuidutils.generate_uuid())
        self._fake_lrouter_dict[fake_lrouter['uuid']] = fake_lrouter
        fake_lrouter['lport_count'] = 0
        # set status value
        fake_lrouter['status'] = 'true'
        return fake_lrouter

    def _add_lqueue(self, body):
        fake_lqueue = json.loads(body)
        fake_lqueue['uuid'] = uuidutils.generate_uuid()
        self._fake_lqueue_dict[fake_lqueue['uuid']] = fake_lqueue
        return fake_lqueue

    def _add_lswitch_lport(self, body, ls_uuid):
        fake_lport = json.loads(body)
        new_uuid = uuidutils.generate_uuid()
        fake_lport['uuid'] = new_uuid
        # put the tenant_id and the ls_uuid in the main dict
        # for simplyfying templating
        fake_lport['ls_uuid'] = ls_uuid
        fake_lport['tenant_id'] = self._get_tag(fake_lport, 'os_tid')
        fake_lport['neutron_port_id'] = self._get_tag(fake_lport,
                                                      'q_port_id')
        fake_lport['neutron_device_id'] = self._get_tag(fake_lport, 'vm_id')
        fake_lport['att_type'] = "NoAttachment"
        fake_lport['att_info_json'] = ''
        self._fake_lswitch_lport_dict[fake_lport['uuid']] = fake_lport

        fake_lswitch = self._fake_lswitch_dict[ls_uuid]
        fake_lswitch['lport_count'] += 1
        fake_lport_status = fake_lport.copy()
        fake_lport_status['ls_tenant_id'] = fake_lswitch['tenant_id']
        fake_lport_status['ls_uuid'] = fake_lswitch['uuid']
        fake_lport_status['ls_name'] = fake_lswitch['display_name']
        fake_lport_status['ls_zone_uuid'] = fake_lswitch['zone_uuid']
        # set status value
        fake_lport['status'] = 'true'
        self._fake_lswitch_lportstatus_dict[new_uuid] = fake_lport_status
        return fake_lport

    def _build_lrouter_lport(self, body, new_uuid=None, lr_uuid=None):
        fake_lport = json.loads(body)
        if new_uuid:
            fake_lport['uuid'] = new_uuid