#
# 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.
#
from collections import namedtuple
from numbers import Number

from enum import Enum, unique

from gen.apache.aurora.api.ttypes import Resource

ResourceDetails = namedtuple('ResourceDetails', ['resource_type', 'value'])


@unique
class ResourceType(Enum):
  """Describes Aurora resource types and their traits."""

  CPUS = ('numCpus', 'CPU', ' core(s)', float, 1)
  RAM_MB = ('ramMb', 'RAM', ' MB', int, 2)
  DISK_MB = ('diskMb', 'Disk', ' MB', int, 3)
  PORTS = ('namedPort', 'Port', '', str, 4)
  GPUS = ('numGpus', 'GPU', ' GPU(s)', int, 5)

  def __init__(self, field, display_name, display_unit, value_type, display_position):
    self._field = field
    self._display_name = display_name
    self._display_unit = display_unit
    self._value_type = value_type
    self._display_position = display_position

  @property
  def field(self):
    return self._field

  @property
  def display_name(self):
    return self._display_name

  @property
  def display_unit(self):
    return self._display_unit

  @property
  def value_type(self):
    return self._value_type

  @property
  def display_position(self):
    return self._display_position

  def resource_value(self, resource):
    return resource.__dict__.get(self._field)

  @classmethod
  def from_resource(cls, resource):
    for _, member in cls.__members__.items():
      if resource.__dict__.get(member.field) is not None:
        return member
    else:
      raise ValueError("Unknown resource: %s" % resource)


class ResourceManager(object):
  """Provides helper methods for working with Aurora resources."""

  @classmethod
  def resource_details(cls, resources):
    result = []
    if resources:
      for resource in list(resources):
        r_type = ResourceType.from_resource(resource)
        result.append(ResourceDetails(r_type, r_type.resource_value(resource)))
      return sorted(result, key=lambda rd: rd.resource_type.display_position)
    return result

  @classmethod
  def resource_details_from_quota(cls, quota):
    return cls.resource_details(quota.resources)

  @classmethod
  def resource_details_from_task(cls, task):
    return cls.resource_details(cls._backfill_resources(task))

  @classmethod
  def quantity_of(cls, resource_details, resource_type):
    result = 0.0
    for d in resource_details:
      if d.resource_type is resource_type:
        result += d.value if isinstance(d.value, Number) else 1
    return result

  @classmethod
  def _backfill_resources(cls, r_object):
    resources = list(r_object.resources) if r_object.resources else None
    if resources is None:
      resources = [
          Resource(numCpus=r_object.numCpus),
          Resource(ramMb=r_object.ramMb),
          Resource(diskMb=r_object.diskMb)
      ]
      if hasattr(r_object, 'requestedPorts'):
        resources += [Resource(namedPort=p) for p in r_object.requestedPorts or []]
    return resources
