
__author__    = "Andre Merzky, Ole Weidner"
__copyright__ = "Copyright 2012-2013, The SAGA Project"
__license__   = "MIT"


import radical.utils.signatures as rus

from .adaptors import base   as sab
from . import attributes as sa
from . import base       as sb

from .constants import TYPE,       SERVER,    USER_CERT,   CERT_REPOSITORY
from .constants import USER_PROXY, USER_KEY,  USER_ID,     USER_PASS,   USER_VO
from .constants import LIFE_TIME,  REMOTE_ID, REMOTE_HOST, REMOTE_PORT, TOKEN

# ------------------------------------------------------------------------------
#
class Context (sb.Base, sa.Attributes) :
    '''A SAGA Context object as defined in GFD.90.

    A security context is a description of a security token.  It is important to
    understand that, in general, a context really just *describes* a token, but
    that a context *is not* a token (*). For example, a context may point to
    a X509 certificate -- but it will in general not hold the certificate
    contents.

    Context classes are used to inform the backends used by SAGA on what
    security tokens are expected to be used.  By default, SAGA will be able to
    pick up such tokens from their default location, but in some cases it might
    be necessary to explicitly point to them - then use Session with
    context instances to do so.

    The usage example for contexts is below::

        # define an ssh context
        ctx = saga.Context("SSH")
        ctx.user_cert = '$HOME/.ssh/special_id_rsa'
        ctx.user_key  = '$HOME/.ssh/special_id_rsa.pub'

        # add the context to a session
        session = saga.Session()
        session.add_context(ctx)

        # create a job service in this session -- that job service can now
        # *only* use that ssh context.
        j = saga.job.Service('ssh://remote.host.net/', session=session)


    The Session argument to the job.Service constructor is fully optional
    -- if left out, SAGA will use default session, which picks up some default
    contexts as described above -- that will suffice for the majority of use
    cases.

    ----

    (*) The only exception to this rule is the 'UserPass' key, which is used to
    hold plain-text passwords.  Use this key with care -- it is not good
    practice to hard-code passwords in the code base, or in config files.
    Also, be aware that the password may show up in log files, when debugging or
    analyzing your application.

    '''

    # --------------------------------------------------------------------------
    #
    @rus.takes   ('Context',
                  str,
                  rus.optional (sab.Base),
                  rus.optional (dict))
    @rus.returns (rus.nothing)
    def __init__ (self, ctype, _adaptor=None, _adaptor_state={}) :
        '''
        ctype: string
        ret:   None
        '''

        sb.Base.__init__ (self, ctype.lower(), _adaptor, _adaptor_state, ctype, ttype=None)


        from . import attributes as sa

        # set attribute interface propertiesP
        self._attributes_extensible  (False)
        self._attributes_camelcasing (True)

        # register properties with the attribute interface
        self._attributes_register  (TYPE,            None, sa.STRING, sa.SCALAR, sa.WRITEABLE)
        self._attributes_register  (SERVER,          None, sa.STRING, sa.SCALAR, sa.WRITEABLE)
        self._attributes_register  (TOKEN,           None, sa.STRING, sa.SCALAR, sa.WRITEABLE)
        self._attributes_register  (CERT_REPOSITORY, None, sa.STRING, sa.SCALAR, sa.WRITEABLE)
        self._attributes_register  (USER_PROXY,      None, sa.STRING, sa.SCALAR, sa.WRITEABLE)
        self._attributes_register  (USER_CERT,       None, sa.STRING, sa.SCALAR, sa.WRITEABLE)
        self._attributes_register  (USER_KEY,        None, sa.STRING, sa.SCALAR, sa.WRITEABLE)
        self._attributes_register  (USER_ID,         None, sa.STRING, sa.SCALAR, sa.WRITEABLE)
        self._attributes_register  (USER_PASS,       None, sa.STRING, sa.SCALAR, sa.WRITEABLE)
        self._attributes_register  (USER_VO,         None, sa.STRING, sa.SCALAR, sa.WRITEABLE)
        self._attributes_register  (LIFE_TIME,       -1,   sa.INT,    sa.SCALAR, sa.WRITEABLE)
        self._attributes_register  (REMOTE_ID,       None, sa.STRING, sa.SCALAR, sa.WRITEABLE)
        self._attributes_register  (REMOTE_HOST,     None, sa.STRING, sa.SCALAR, sa.WRITEABLE)
        self._attributes_register  (REMOTE_PORT,     None, sa.STRING, sa.VECTOR, sa.WRITEABLE)

        self.type = ctype


    # --------------------------------------------------------------------------
    #
    @rus.takes   ('Context')
    @rus.returns (str)
    def __str__  (self) :

        d = self.as_dict ()
        s = "{"

        for key in sorted (d.keys ()) :
            if  key == 'UserPass' and d[key] :
                s += "'UserPass' : '%s'" % ('x'*len(d[key]))
            else :
                s += "'%s' : '%s'" % (key, d[key])
            s += ', '

        return "%s}" % s[0:-2]


    # --------------------------------------------------------------------------
    #
    @rus.takes   ('Context')
    @rus.returns (str)
    def __repr__ (self) :

        return str(self)


    # --------------------------------------------------------------------------
    #
    @rus.takes      ('Context',
                     ('Session', '_DefaultSession'))
    @rus.returns    (rus.nothing)
    def _initialize (self, session) :
        '''
        ret:  None
        '''
        self._adaptor._initialize (session)


    # --------------------------------------------------------------------------
    #
    def __deepcopy__(self, memo):

        ret = Context(self.type)

        for a in self.list_attributes():
            ret.set_attribute(a, self.get_attribute(a))

        return ret


# ------------------------------------------------------------------------------

