# Copyright (c) 2015, The MITRE Corporation. All rights reserved.
# See LICENSE.txt for complete terms.

# internal
import stix
import stix.bindings.exploit_target as exploit_target_binding
from stix.common.related import (
    GenericRelationshipList, RelatedCOA, RelatedExploitTarget,
    RelatedPackageRefs
)
from stix.data_marking import Marking

# relative
from .vulnerability import Vulnerability, _Vulnerabilities  # noqa
from .weakness import Weakness, _Weaknesses  # noqa
from .configuration import Configuration, _Configurations  # noqa


class ExploitTarget(stix.BaseCoreComponent):
    """Implementation of STIX ``ExploitTarget``.

    Args:
        id_ (optional): An identifier. If ``None``, a value will be generated
            via ``stix.utils.create_id()``. If set, this will unset the
            ``idref`` property.
        idref (optional): An identifier reference. If set this will unset the
            ``id_`` property.
        title (optional): A string title.
        timestamp (optional): A timestamp value. Can be an instance of
            ``datetime.datetime`` or ``str``.
        description (optional): A string description.
        short_description (optional): A string short description.

    """
    _binding = exploit_target_binding
    _binding_class = _binding.ExploitTargetType
    _namespace = "http://stix.mitre.org/ExploitTarget-1"
    _version = "1.1.1"
    _ALL_VERSIONS = ("1.0", "1.0.1", "1.1", "1.1.1")
    _ID_PREFIX = 'et'

    def __init__(self, id_=None, idref=None, timestamp=None, title=None,
                 description=None, short_description=None):

        super(ExploitTarget, self).__init__(
            id_=id_,
            idref=idref,
            timestamp=timestamp,
            title=title,
            description=description,
            short_description=short_description
        )

        self.handling = None
        self.potential_coas = PotentialCOAs()
        self.related_exploit_targets = RelatedExploitTargets()
        self.vulnerabilities = None
        self.weaknesses = None
        self.configuration = None
        self.related_packages = RelatedPackageRefs()

    @property
    def handling(self):
        
        return self._handling

    @handling.setter
    def handling(self, value):
        self._set_var(Marking, try_cast=False, handling=value)
 
    @property
    def vulnerabilities(self):
        """A list of ``Vulnerability`` objects 
        
        Default Value: ``None``

        Returns:
            A list of
            :class:`stix.exploit_target.vulnerability`

        Raises:
            ValueError: If set to a value that is not ``None`` and not an
                instance of
                :class:`stix.exploit_target.vulnerability`

        """
        
        return self._vulnerabilities
    
    @vulnerabilities.setter
    def vulnerabilities(self, value):
        self._vulnerabilities = _Vulnerabilities(value)

    def add_vulnerability(self, v):
        """Adds a vulnerability to the ``vulnerabilies`` list property.

        Note:
            If ``None`` is passed in no value is added 

        Args:
            v: A Vulnerability value.
            
        Raises: ValueError if the ``v`` param is of type
            :class:`stix.exploit_target.vulnerability`
    

        """
        self.vulnerabilities.append(v)
 
    @property
    def weaknesses(self):
        """A list of ``Weakness`` objects 
        
        Default Value: ``None``

        Returns:
            A list of
            :class:`stix.exploit_target.weakness`

        Raises:
            ValueError: If set to a value that is not ``None`` and not an
                instance of
                :class:`stix.exploit_target.weakness`

        """
        
        return self._weaknesses
    
    @weaknesses.setter
    def weaknesses(self, value):
        self._weaknesses = _Weaknesses(value)
            
    def add_weakness(self, v):
        """Adds a weakness to the ``weaknesses`` list property.

        Note:
            If ``None`` is passed in no value is added 

        Args:
            v: A weakness value.
            
        Raises: ValueError if the ``v`` param is of type :class:`stix.exploit_target.weakness`
    

        """
        self.weaknesses.append(v)
 
    @property
    def configuration(self):
        """A list of ``Configuration`` objects 
        
        Default Value: ``None``

        Returns:
            A list of
            :class:`stix.exploit_target.configuration`

        Raises:
            ValueError: If set to a value that is not ``None`` and not an
                instance of
                :class:`stix.exploit_target.configuration`

        """
        
        return self._configuration
    
    @configuration.setter
    def configuration(self, value):
        self._configuration = _Configurations(value)

    def add_configuration(self, v):
        """Adds a configuration to the ``configurations`` list property.

        Note:
            If ``None`` is passed in no value is added 

        Args:
            v: A configuration value.
            
        Raises: ValueError if the ``v`` param is of type :class:`stix.exploit_target.configuration`

        """
        self.configuration.append(v)
 
    def to_obj(self, return_obj=None, ns_info=None):
        if not return_obj:
            return_obj = self._binding_class()

        super(ExploitTarget, self).to_obj(return_obj=return_obj, ns_info=ns_info)

        if self.handling:
            return_obj.Handling = self.handling.to_obj(ns_info=ns_info)
        if self.potential_coas:
            return_obj.Potential_COAs = self.potential_coas.to_obj(ns_info=ns_info)
        if self.related_exploit_targets:
            return_obj.Related_Exploit_Targets = self.related_exploit_targets.to_obj(ns_info=ns_info)
        if self.vulnerabilities:
            return_obj.Vulnerability = self.vulnerabilities.to_obj(ns_info=ns_info)
        if self.weaknesses:
            return_obj.Weakness = self.weaknesses.to_obj(ns_info=ns_info)
        if self.configuration:
            return_obj.Configuration = self.configuration.to_obj(ns_info=ns_info)
        if self.related_packages:
            return_obj.Related_Packages = self.related_packages.to_obj(ns_info=ns_info)
            
        return return_obj

    @classmethod
    def from_obj(cls, obj, return_obj=None):
        if not obj:
            return None

        if not return_obj:
            return_obj = cls()

        super(ExploitTarget, cls).from_obj(obj, return_obj=return_obj)

        if isinstance(obj, cls._binding_class):
            return_obj.handling = Marking.from_obj(obj.Handling)
            return_obj.potential_coas = PotentialCOAs.from_obj(obj.Potential_COAs)
            return_obj.related_exploit_targets = RelatedExploitTargets.from_obj(obj.Related_Exploit_Targets)
            return_obj.vulnerabilities = _Vulnerabilities.from_obj(obj.Vulnerability)
            return_obj.weaknesses = _Weaknesses.from_obj(obj.Weakness)
            return_obj.configuration = _Configurations.from_obj(obj.Configuration)
            return_obj.related_packages = RelatedPackageRefs.from_obj(obj.Related_Packages)

        return return_obj

    def to_dict(self):
        return super(ExploitTarget, self).to_dict()

    @classmethod
    def from_dict(cls, dict_repr, return_obj=None):
        if not dict_repr:
            return None

        if not return_obj:
            return_obj = cls()

        super(ExploitTarget, cls).from_dict(dict_repr, return_obj=return_obj)

        get = dict_repr.get
        return_obj.handling = Marking.from_dict(get('handling'))
        return_obj.potential_coas = PotentialCOAs.from_dict(get('potential_coas'))
        return_obj.related_exploit_targets = RelatedExploitTargets.from_dict(get('related_exploit_targets'))
        return_obj.vulnerabilities = _Vulnerabilities.from_dict(get('vulnerabilities'))
        return_obj.weaknesses = _Weaknesses.from_dict(get('weaknesses'))
        return_obj.configuration = _Configurations.from_dict(get('configuration'))
        return_obj.related_packages = RelatedPackageRefs.from_dict(get('related_packages'))
        
        return return_obj


class PotentialCOAs(GenericRelationshipList):
    """
    A list of ``Potential_COA`` objects, defaults to empty array
    """
    _namespace = "http://stix.mitre.org/ExploitTarget-1"
    _binding = exploit_target_binding
    _binding_class = exploit_target_binding.PotentialCOAsType
    _binding_var = "Potential_COA"
    _contained_type = RelatedCOA
    _inner_name = "coas"

    def __init__(self, coas=None, scope=None):
        if coas is None:
            coas = []
        super(PotentialCOAs, self).__init__(scope, *coas)


class RelatedExploitTargets(GenericRelationshipList):
    """
    A list of ``RelatedExploitTargets`` objects, defaults to empty array
    """
    _namespace = "http://stix.mitre.org/ExploitTarget-1"
    _binding = exploit_target_binding
    _binding_class = exploit_target_binding.RelatedExploitTargetsType
    _binding_var = "Related_Exploit_Target"
    _contained_type = RelatedExploitTarget
    _inner_name = "related_exploit_targets"

    def __init__(self, related_exploit_targets=None, scope=None):
        super(RelatedExploitTargets, self).__init__(scope, related_exploit_targets)
