"""Radiance Source.
http://radsite.lbl.gov/radiance/refer/ray.html#Source
"""
from .geometrybase import Geometry
import honeybee.typing as typing
[docs]
class Source(Geometry):
    """Radiance Source.
    A source is not really a surface, but a solid angle. It is used for specifying light
    sources that are very distant. The direction to the center of the source and the
    number of degrees subtended by its disk are given as follows:
    .. code-block:: shell
        mod source id
        0
        0
        4 xdir ydir zdir angle
    Args:
        identifier: Text string for a unique Geometry ID. Must not contain spaces
            or special characters. This will be used to identify the object across
            a model and in the exported Radiance files.
        direction: A vector to set source direction (x, y, z) (Default: (0, 0 ,-1)).
        angle: Source solid angle (Default: 0.533).
        modifier: Geometry modifier (Default: None).
        dependencies: A list of primitives that this primitive depends on. This
            argument is only useful for defining advanced primitives where the
            primitive is defined based on other primitives. (Default: [])
    Properties:
        * identifier
        * display_name
        * direction
        * angle
        * modifier
        * dependencies
        * values
    Usage:
    .. code-block:: python
        source = Source("test_source", (0, 0, 10), 10)
        print(source)
    """
    __slots__ = ('_direction', '_angle')
    def __init__(self, identifier, direction=None, angle=0.533, modifier=None,
                 dependencies=None):
        """Radiance Source."""
        Geometry.__init__(self, identifier, modifier=modifier, dependencies=dependencies)
        self.direction = direction or (0, 0, -1)
        self.angle = angle if angle is not None else 0.533
        self._update_values()
    def _update_values(self):
        """update values dictionary."""
        self._values[2] = \
            
[self.direction[0], self.direction[1], self.direction[2], self.angle]
    @property
    def direction(self):
        """A vector to set source direction (x, y, z) (Default is (0, 0 ,-1))."""
        return self._direction
    @direction.setter
    def direction(self, value):
        self._direction = tuple(float(v) for v in value)
        assert len(self._direction) == 3, \
            
'Radiance Source direction must have 3 values for (x, y, z).'
    @property
    def angle(self):
        """Source solid angle. Default is 0.533."""
        return self._angle
    @angle.setter
    def angle(self, value):
        self._angle = typing.float_positive(value)
[docs]
    @classmethod
    def from_primitive_dict(cls, primitive_dict):
        """Initialize a Source from a primitive dict.
        Args:
            data: A dictionary in the format below.
        .. code-block:: python
            {
            "modifier": {},  # primitive modifier (Default: None)
            "type": "source",  # primitive type
            "identifier": "",  # primitive identifier
            "display_name": "",  # primitive display name
            "values": [],  # values
            "dependencies": []
            }
        """
        assert 'type' in primitive_dict, 'Input dictionary is missing "type".'
        if primitive_dict['type'] != cls.__name__.lower():
            raise ValueError(
                'Type must be %s not %s.' % (cls.__name__.lower(), primitive_dict['type'])
            )
        modifier, dependencies = cls.filter_dict_input(primitive_dict)
        values = primitive_dict['values'][2]
        cls_ = cls(
            identifier=primitive_dict['identifier'],
            direction=values[0:3],
            angle=values[3],
            modifier=modifier,
            dependencies=dependencies
        )
        if 'display_name' in primitive_dict and primitive_dict['display_name'] is not None:
            cls_.display_name = primitive_dict['display_name']
        # this might look redundant but it is NOT. see glass for explanation.
        cls_.values = primitive_dict['values']
        return cls_ 
[docs]
    @classmethod
    def from_dict(cls, data):
        """Initialize a Ring from a dictionary.
        Args:
            data: A dictionary in the format below.
        .. code-block:: python
            {
            "type": "source",  # Geometry type
            "modifier": {} ,
            "identifier": "",  # Geometry identifier
            "display_name": "",  # Geometry display name
            "direction": {"x": float, "y": float, "z": float},
            "angle": float,
            "dependencies": []
            }
        """
        assert 'type' in data, 'Input dictionary is missing "type".'
        if data['type'] != cls.__name__.lower():
            raise ValueError(
                'Type must be %s not %s.' % (cls.__name__.lower(),
                    data['type'])
            )
        modifier, dependencies = cls.filter_dict_input(data)
        new_obj = cls(identifier=data["identifier"],
                      direction=(data["direction"]),
                      angle=data["angle"],
                      modifier=modifier,
                      dependencies=dependencies)
        if 'display_name' in data and data['display_name'] is not None:
            new_obj.display_name = data['display_name']
        return new_obj 
[docs]
    def to_dict(self):
        """Translate this object to a dictionary."""
        base = {
            "modifier": self.modifier.to_dict(),
            "type": self.__class__.__name__.lower(),
            "identifier": self.identifier,
            "direction": self.direction,
            "angle": self.angle,
            'dependencies': [dp.to_dict() for dp in self.dependencies]
        }
        if self._display_name is not None:
            base['display_name'] = self.display_name
        return base 
    def __copy__(self):
        mod, depend = self._dup_mod_and_depend()
        new_obj = self.__class__(
            self.identifier, self.direction, self.angle, mod, depend)
        new_obj._display_name = self._display_name
        return new_obj