#!/usr/bin/env python3
"""An object representing EE Features."""

from typing import Any, Dict, Optional, Union

from ee import apifunction
from ee import computedobject
from ee import ee_exception
from ee import element
from ee import geometry


class Feature(element.Element):
  """An object representing EE Features."""

  _initialized = False

  # Tell pytype to not complain about dynamic attributes.
  _HAS_DYNAMIC_ATTRIBUTES = True

  def __init__(
      self,
      geom: Optional[
          Union[
              'Feature',
              geometry.Geometry,
              Dict[str, Any],
              computedobject.ComputedObject,
          ]
      ],
      opt_properties: Optional[
          Union[Dict[str, Any], computedobject.ComputedObject]
      ] = None,
  ):
    """Creates a feature a geometry or computed object.

    Features can be constructed from one of the following arguments plus an
    optional dictionary of properties:
      1) An ee.Geometry.
      2) A GeoJSON Geometry.
      3) A GeoJSON Feature.
      4) A computed object - reinterpreted as a geometry if properties
         are specified, and as a feature if they aren't.

    Args:
      geom: A geometry or feature.
      opt_properties: A dictionary of metadata properties. If the first
          parameter is a Feature (instead of a geometry), this is unused.

    Raises:
      EEException: if the given geometry isn't valid.
    """
    if isinstance(geom, Feature):
      if opt_properties is not None:
        raise ee_exception.EEException(
            'Cannot create Feature out of a Feature and properties.')
      # A pre-constructed Feature. Copy.
      super().__init__(geom.func, geom.args)
      return

    self.initialize()

    feature_constructor = apifunction.ApiFunction.lookup('Feature')
    if geom is None or isinstance(geom, geometry.Geometry):
      # A geometry object.
      super().__init__(feature_constructor, {
          'geometry': geom,
          'metadata': opt_properties or None
      })
    elif isinstance(geom, computedobject.ComputedObject):
      # A custom object to reinterpret as a Feature.
      super().__init__(geom.func, geom.args, geom.varName)
    elif isinstance(geom, dict) and geom.get('type') == 'Feature':
      properties = geom.get('properties', {})
      if 'id' in geom:
        if 'system:index' in properties:
          raise ee_exception.EEException(
              'Cannot specify both "id" and "system:index".')
        properties = properties.copy()
        properties['system:index'] = geom['id']
      # Try to convert a GeoJSON Feature.
      super().__init__(feature_constructor, {
          'geometry': geometry.Geometry(geom.get('geometry', None)),
          'metadata': properties
      })
    else:
      # Try to convert the geometry arg to a Geometry, in the hopes of it
      # turning out to be GeoJSON.
      super().__init__(feature_constructor, {
          'geometry': geometry.Geometry(geom),
          'metadata': opt_properties or None
      })

  @classmethod
  def initialize(cls) -> None:
    """Imports API functions to this class."""
    if not cls._initialized:
      apifunction.ApiFunction.importApi(cls, 'Feature', 'Feature')
      cls._initialized = True

  @classmethod
  def reset(cls) -> None:
    """Removes imported API functions from this class."""
    apifunction.ApiFunction.clearApi(cls)
    cls._initialized = False

  def getMapId(
      self, vis_params: Optional[Dict[str, Any]] = None
  ) -> Dict[str, Any]:
    """Fetch and return a map id and token, suitable for use in a Map overlay.

    Args:
      vis_params: The visualization parameters. Currently only one parameter,
          'color', containing a hex RGB color string is allowed.

    Returns:
      A map ID dictionary as described in ee.data.getMapId, including an
      additional 'image' field containing Collection.draw image wrapping a
      FeatureCollection containing this feature.
    """
    # Create a collection containing this one feature and render it.
    collection = apifunction.ApiFunction.call_('Collection', [self])
    return collection.getMapId(vis_params)

  @staticmethod
  def name() -> str:
    return 'Feature'
