# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# 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.


"""
Redfish Resource Types
"""

import base64
import gzip
import hashlib
import httplib
import json
import ssl
import StringIO
import sys
import urllib2
from urlparse import urlparse

#from oslo_log import log as logging
from redfish import exception

#LOG = logging.getLogger('redfish')


class Base(object):
    def __init__(self, obj, connection=None):
        self._conn = connection
        """handle to the redfish connection"""

        self._attrs = []
        """list of discovered attributes"""

        self._links = []
        """list of linked resources"""

        # parse the individual resources, appending them to
        # the list of object attributes
        for k in obj.keys():
            ref = k.lower()
            if ref in ["links", "oem", "items"]:
                continue
            setattr(self, ref, obj[k])
            self._attrs.append(ref)

        # make sure the required attributes are present
        if not getattr(self, 'name', False):
            raise ObjectLoadException(
                    "Failed to load object. Reason: could not determine name.")
        if not getattr(self, 'type', False):
            raise ObjectLoadException(
                    "Failed to load object. Reason: could not determine type.")

        if getattr(self, 'serviceversion', False):
            self.type = self.type.replace('.' + self.serviceversion, '')
        else:
            # TODO: use a regex here to strip and store the version
            # instead of assuming it is 7 chars long
            self.type = self.type[:-7]

        # Lastly, parse the 'links' resource.
        # Note that this may have different nested structure, depending on
        # what type of resource this is, or what vendor it is.
        # subclasses may follow this by parsing other resources / collections
        self._parse_links(obj)

    def _parse_links(self, obj):
        """Map linked resources to getter functions

        The root resource returns a dict of links to top-level resources
        """
        def getter(connection, href):
            def _get():
                return connection.rest_get(href, {})
            return _get

        for k in obj['links']:
            ref = "get_" + k.lower()
            self._links.append(ref)
            href = obj['links'][k]['href']
            setattr(self, ref, getter(self._conn, href))

    def __repr__(self):
        """Return this object's _attrs as a dict"""
        res = {}
        for a in self._attrs:
            res[a] = getattr(self, a)
        return res

    def __str__(self):
        """Return the string representation of this object's _attrs"""
        return json.dumps(self.__repr__())


class BaseCollection(Base):
    """Base class for collection types"""
    def __init__(self, obj, connection=None):
        super(BaseCollection, self).__init__(obj, connection=connection)
        self._parse_items(obj)
        self._attrs.append('items')

    def _parse_links(self, obj):
        """links are special on a chassis; dont parse them"""
        pass

    def _parse_items(self, obj):
        """Map linked items to getter methods

        The chassis resource returns a list of items and corresponding
        link data in a separate entity.
        """
        def getter(connection, href):
            def _get():
                return connection.rest_get(href, {})
            return _get

        self.items = []
        self._item_getters = []

        if 'links' in obj and 'Member' in obj['links']:
            # NOTE: this assumes the lists are ordered the same
            counter = 0
            for item in obj['links']['Member']:
                self.items.append(obj['Items'][counter])
                self._item_getters.append(
                        getter(self._conn, item['href']))
                counter+=1
        elif 'Items' in obj:
            # TODO: find an example of this format and make sure it works
            for item in obj['Items']:
                if 'links' in item and 'self' in item['links']:
                    href = item['links']['self']['href']
                    self.items.append(item)

        # TODO: implement paging support
        # if 'links' in obj and 'NextPage' in obj['links']:
        #    next_page = THIS_URI + '?page=' + str(obj['links']['NextPage']['page'])
        #    do something with next_page URI

    def __iter__(self):
        for getter in self._item_getters:
            yield getter()


class Root(Base):
    """Root '/' resource class"""
    def _parse_links(self, obj):
        """Map linked resources to getter functions

        The root resource returns a dict of links to top-level resources

        TODO: continue implementing customizations for top-level resources

        """
        mapping = {
                'Systems': Systems,
                'Chassis': Chassis,
                'Managers': Base,
                'Schemas': Base,
                'Registries': Base,
                'Tasks': Base,
                'AccountService': Base,
                'Sessions': Base,
                'EventService': Base,
                }

        def getter(connection, href, type):
            def _get():
                return mapping[type](connection.rest_get(href, {}), self._conn)
            return _get

        for k in obj['links']:
            ref = "get_" + k.lower()
            self._links.append(ref)
            href = obj['links'][k]['href']
            setattr(self, ref, getter(self._conn, href, k))


class Chassis(BaseCollection):
    """Chassis resource class"""
    def __len__(self):
        return len(self.items)


class Systems(Base):
    pass