from typing import Optional, List
from decimal import Decimal
from validator_collection import validators
from highcharts_maps import errors
from highcharts_maps.decorators import class_sensitive
from highcharts_maps.metaclasses import HighchartsMeta
from highcharts_maps.utility_classes.gradients import Gradient
from highcharts_maps.utility_classes.patterns import Pattern
from highcharts_maps.utility_classes.projections import ProjectionOptions
from highcharts_maps.utility_classes.geojson import MultiLineString, Polygon
from highcharts_maps.utility_functions import validate_color, validate_bounding_array
[docs]class InsetOptions(HighchartsMeta):
    """Generic configuration settings for the placement and appearance of
    :term:`map insets <map inset>`, such as those used for non-contiguous territories."""
    def __init__(self, **kwargs):
        self._border_color = None
        self._border_width = None
        self._padding = None
        self._relative_to = None
        self._units = None
        self.border_color = kwargs.get('border_color', None)
        self.border_width = kwargs.get('border_width', None)
        self.padding = kwargs.get('padding', None)
        self.relative_to = kwargs.get('relative_to', None)
        self.units = kwargs.get('units', None)
    @property
    def border_color(self) -> Optional[str | Gradient | Pattern]:
        """The border color drawn around the inset. Defaults to
        ``'#cccccc'``.
        :returns: The color of the inset border.
        :rtype: :class:`str <python:str>`, :class:`Gradient`, :class:`Pattern``, or
          :obj:`None <python:None>`
        """
        return self._border_color
    @border_color.setter
    def border_color(self, value):
        self._border_color = validate_color(value)
    @property
    def border_width(self) -> Optional[int | float | Decimal]:
        """The border width (in pixels) applied to the inset border. Defaults to
        ``1``.
        :returns: The border width to apply to the inset border.
        :rtype: numeric or :obj:`None <python:None>`
        """
        return self._border_width
    @border_width.setter
    def border_width(self, value):
        self._border_width = validators.numeric(value, allow_empty = True)
    @property
    def padding(self) -> Optional[str | int | float | Decimal | List[str | int | float | Decimal]]:
        """The padding of the insets. Defaults to ``'10%'``.
        Accepts:
          * a number, representing pixels
          * a percentage string, relative to the plot area
          * an array of numbers or percentage strings, corresponding to top, right,
            bottom, and left respectively
        :rtype: :class:`str <python:str>`, :class:`int <python:int>`,
          4-member :class:`list <python:list>` of :class:`str <python:str>` or
          :class:`int <python:int>`, or :obj:`None <python:None>`
        """
        return self._padding
    @padding.setter
    def padding(self, value):
        self._padding = validate_bounding_array(value)
    @property
    def relative_to(self) -> Optional[str]:
        """The coordinate system that the inset's
        :meth:`.field <highcharts_maps.options.map_views.insets.Inset.field>` and
        :meth:`.border_path <highcharts_maps.options.map_views.insets.Inset.border_path>`
        should relate to. Defaults to ``'mapBoundingBox'``.
        Accepts either:
          * ``'mapBoundingBox'`` (default)
          * ``'plotBox'``
        .. note::
          If ``'plotBox'``, they will be fixed to the plot box and responsively move in
          relation to the main map.
          If ``'mapBoundingBox'``, they will be fixed to the map bounding box, which is
          constant and centered in different chart sizes and ratios.
        :rtype: :class:`str <python:str>` or :obj:`None <python:None>`
        """
        return self._relative_to
    @relative_to.setter
    def relative_to(self, value):
        if not value:
            self._relative_to = None
        else:
            value = validators.string(value)
            value = value.lower()
            if value not in ['mapBoundingBox', 'plotBox']:
                raise errors.HighchartsValueError(f'relative_to expects either '
                                                  f'"mapBoundingBox" or "plotBox". '
                                                  f'Received: "{value}"')
            self._relative_to = value
    @property
    def units(self) -> Optional[str]:
        """The units to use for the inset's
        :meth:`.field <highcharts_maps.options.map_views.insets.Inset.field>` and
        :meth:`.border_path <highcharts_maps.options.map_views.insets.Inset.border_path>`
        settings. Defaults to ``'percent'``.
        Accepts either:
          * ``'percent'`` (default)
          * ``'pixels'``
        .. note::
          If ``'percent'``, they are expressed as a percentage of the item referenced in
          :meth:`.relative_to <highcharts_maps.options.map_views.insets.Inset.relative_to>`.
          If ``'pixels'``, they are expressed in absolute values.
        :rtype: :class:`str <python:str>`
        """
        return self._units
    @units.setter
    def units(self, value):
        if not value:
            self._units = None
        else:
            value = validators.string(value)
            value = value.lower()
            if value not in ['percent', 'pixels']:
                raise errors.HighchartsValueError(f'units expects either "percent" or '
                                                  f'"pixels". Received: "{value}".')
            self._units = value
    @classmethod
    def _get_kwargs_from_dict(cls, as_dict):
        kwargs = {
            'border_color': as_dict.get('borderColor', None),
            'border_width': as_dict.get('borderWidth', None),
            'padding': as_dict.get('padding', None),
            'relative_to': as_dict.get('relativeTo', None),
            'units': as_dict.get('units', None),
        }
        return kwargs
    def _to_untrimmed_dict(self, in_cls = None) -> dict:
        untrimmed = {
            'borderColor': self.border_color,
            'borderWidth': self.border_width,
            'padding': self.padding,
            'relativeTo': self.relative_to,
            'units': self.units,
        }
        return untrimmed 
[docs]class Inset(InsetOptions):
    """Configuration of a specific :term:`map inset`."""
    def __init__(self, **kwargs):
        self._border_path = None
        self._field = None
        self._geo_bounds = None
        self._id = None
        self._projection = None
        self.border_path = kwargs.get('border_path', None)
        self.field = kwargs.get('field', None)
        self.geo_bounds = kwargs.get('geo_bounds', None)
        self.projection = kwargs.get('projection', None)
        super().__init__(**kwargs)
    @property
    def border_path(self) -> Optional[MultiLineString]:
        """A :class:`MultiLineString <highcharts_maps.utility_classes.geojson.MultiLineString>`
        geometry that defines the border path of the inset in units as per
        :meth:`.units <highcharts_maps.options.map_views.insets.Inset.units>`. Defaults to
        :obj:`None <python:None>`.
        If :obj:`None <python:None>`, a border is rendered around the
        :meth:`.field <highcharts_maps.options.map_views.insets.Inset.field>` geometry.
        .. tip::
          **Best practice!**
          It is recommended that the border path partly follows the outline of the field
          in order to make pointer positioning consistent.
        :rtype: :class:`MultiLineString <highcharts_maps.utility_classes.geojson.MultiLineString>`
          or :obj:`None <python:None>`
        """
        return self._border_path
    @border_path.setter
    @class_sensitive(MultiLineString)
    def border_path(self, value):
        self._border_path = value
    @property
    def field(self) -> Optional[Polygon]:
        """A :class:`Polygon <highcharts_maps.utility_classes.geojson.Polygon>` geometry
        that defines where in the chart the inset should be rendered in units as per
        :meth:`.units <highcharts_maps.options.map_views.insets.Inset.units>`. Defaults to
        :obj:`None <python:None>`.
        If :obj:`None <python:None>`, the inset is rendered in the full plot area.
        :rtype: :class:`Polygon <highcharts_maps.utility_classes.geojson.Polygon>` or
          :obj:`None <python:None>`
        """
        return self._field
    @field.setter
    @class_sensitive(Polygon)
    def field(self, value):
        self._field = value
    @property
    def geo_bounds(self) -> Optional[Polygon]:
        """A :class:`Polygon <highcharts_maps.utility_classes.geojson.Polygon>` geometry
        that encircles the shapes that should be rendered inside the inset. Geometries
        that are found within this geometry are removed from the default map view and
        rendered in the inset. Defaults to :obj:`None <python:None>`.
        :rtype: :class:`Polygon <highcharts_maps.utility_classes.geojson.Polygon>` or
          :obj:`None <python:None>`
        """
        return self._geo_bounds
    @geo_bounds.setter
    @class_sensitive(Polygon)
    def geo_bounds(self, value):
        self._geo_bounds = value
    @property
    def id(self) -> Optional[str]:
        """The identifier given to the inset. Defaults to :obj:`None <python:None>`.
        :rtype: :class:`str <python:str>` or :obj:`None <python:None>`
        """
        return self._id
    @id.setter
    def id(self, value):
        self._id = validators.string(value, allow_empty = True)
    @property
    def projection(self) -> Optional[ProjectionOptions]:
        """The projection options for the inset. Defaults to :obj:`None <python:None>`.
        :rtype: :class:`ProjectionOptions <highcharts_maps.utility_classes.projection.ProjectionOptions>`
          or :obj:`None <python:None>`
        """
        return self._projection
    @projection.setter
    @class_sensitive(ProjectionOptions)
    def projection(self, value):
        self._projection = value
    @classmethod
    def _get_kwargs_from_dict(cls, as_dict):
        kwargs = {
            'border_color': as_dict.get('borderColor', None),
            'border_width': as_dict.get('borderWidth', None),
            'padding': as_dict.get('padding', None),
            'relative_to': as_dict.get('relativeTo', None),
            'units': as_dict.get('units', None),
            'border_path': as_dict.get('borderPath', None),
            'field': as_dict.get('field', None),
            'geo_bounds': as_dict.get('geoBounds', None),
            'id': as_dict.get('id', None),
            'projection': as_dict.get('projection', None),
        }
        return kwargs
    def _to_untrimmed_dict(self, in_cls = None) -> dict:
        untrimmed = {
            'borderPath': self.border_path,
            'field': self.field,
            'geoBounds': self.geo_bounds,
            'id': self.id,
            'projection': self.projection
        }
        parent_as_dict = super()._to_untrimmed_dict(in_cls = in_cls)
        for key in parent_as_dict:
            untrimmed[key] = parent_as_dict[key]
        return untrimmed