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

# Copyright 2011 Citrix System.
# All Rights Reserved.
#
#    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 urllib

from oslo.config import cfg
from webob import exc

from quantum.common import constants
from quantum.common import exceptions
from quantum.openstack.common import log as logging


LOG = logging.getLogger(__name__)


def get_filters(request, attr_info, skips=[]):
    """
    Extracts the filters from the request string
    Returns a dict of lists for the filters:
    check=a&check=b&name=Bob&
    becomes:
    {'check': [u'a', u'b'], 'name': [u'Bob']}
    """
    res = {}
    for key, values in request.GET.dict_of_lists().iteritems():
        if key in skips:
            continue
        values = [v for v in values if v]
        key_attr_info = attr_info.get(key, {})
        if 'convert_list_to' in key_attr_info:
            values = key_attr_info['convert_list_to'](values)
        elif 'convert_to' in key_attr_info:
            convert_to = key_attr_info['convert_to']
            values = [convert_to(v) for v in values]
        if values:
            res[key] = values
    return res


def get_previous_link(request, items, id_key):
    params = request.GET.copy()
    params.pop('marker', None)
    if items:
        marker = items[0][id_key]
        params['marker'] = marker
    params['page_reverse'] = True
    return "%s?%s" % (request.path_url, urllib.urlencode(params))


def get_next_link(request, items, id_key):
    params = request.GET.copy()
    params.pop('marker', None)
    if items:
        marker = items[-1][id_key]
        params['marker'] = marker
    params.pop('page_reverse', None)
    return "%s?%s" % (request.path_url, urllib.urlencode(params))


def get_limit_and_marker(request):
    """Return marker, limit tuple from request.

    :param request: `wsgi.Request` possibly containing 'marker' and 'limit'
                    GET variables. 'marker' is the id of the last element
                    the client has seen, and 'limit' is the maximum number
                    of items to return. If limit == 0, it means we needn't
                    pagination, then return None.
    """
    max_limit = _get_pagination_max_limit()
    limit = _get_limit_param(request, max_limit)
    if max_limit > 0:
        limit = min(max_limit, limit) or max_limit
    if not limit:
        return None, None
    marker = request.GET.get('marker', None)
    return limit, marker


def _get_pagination_max_limit():
    max_limit = -1
    if (cfg.CONF.pagination_max_limit.lower() !=
        constants.PAGINATION_INFINITE):
        try:
            max_limit = int(cfg.CONF.pagination_max_limit)
            if max_limit == 0:
                raise ValueError()
        except ValueError:
            LOG.warn(_("Invalid value for pagination_max_limit: %s. It "
                       "should be an integer greater to 0"),
                     cfg.CONF.pagination_max_limit)
    return max_limit


def _get_limit_param(request, max_limit):
    """Extract integer limit from request or fail."""
    try:
        limit = int(request.GET.get('limit', 0))
        if limit >= 0:
            return limit
    except ValueError:
        pass
    msg = _("Limit must be an integer 0 or greater and not '%d'")
    raise exceptions.BadRequest(resource='limit', msg=msg)


def list_args(request, arg):
    """Extracts the list of arg from request"""
    return [v for v in request.GET.getall(arg) if v]


def get_sorts(request, attr_info):
    """Extract sort_key and sort_dir from request, return as:
       [(key1, value1), (key2, value2)]
    """
    sort_keys = list_args(request, "sort_key")
    sort_dirs = list_args(request, "sort_dir")
    if len(sort_keys) != len(sort_dirs):
        msg = _("The number of sort_keys and sort_dirs must be same")
        raise exc.HTTPBadRequest(explanation=msg)
    valid_dirs = [constants.SORT_DIRECTION_ASC, constants.SORT_DIRECTION_DESC]
    absent_keys = [x for x in sort_keys if x not in attr_info]
    if absent_keys:
        msg = _("%s is invalid attribute for sort_keys") % absent_keys
        raise exc.HTTPBadRequest(explanation=msg)
    invalid_dirs = [x for x in sort_dirs if x not in valid_dirs]
    if invalid_dirs:
        msg = (_("%(invalid_dirs)s is invalid value for sort_dirs, "
                 "valid value is '%(asc)s' and '%(desc)s'") %
               {'invalid_dirs': invalid_dirs,
                'asc': constants.SORT_DIRECTION_ASC,
                'desc': constants.SORT_DIRECTION_DESC})
        raise exc.HTTPBadRequest(explanation=msg)
    return zip(sort_keys,
               [x == constants.SORT_DIRECTION_ASC for x in sort_dirs])


def get_page_reverse(request):
    data = request.GET.get('page_reverse', 'False')
    return data.lower() == "true"


def get_pagination_links(request, items, limit,
                         marker, page_reverse, key="id"):
    key = key if key else 'id'
    links = []
    if not limit:
        return links
    if not (len(items) < limit and not page_reverse):
        links.append({"rel": "next",
                      "href": get_next_link(request, items,
                                            key)})
    if not (len(items) < limit and page_reverse):