class _GidcLite(_BaseLite):
    ''' Lightweight description of a GIDC.
    '''
    __slots__ = [
        'identity'
    ]
    
    def __init__(self, ghid, identity):
        self.ghid = ghid
        self.identity = identity
        
    @classmethod
    def from_golix(cls, golix_obj):
        ''' Convert the golix object to a lightweight representation.
        '''
        identity = SecondParty.from_identity(golix_obj)
        return cls(
            ghid = golix_obj.ghid,
            identity = identity,
        ) class API(type):
    ''' Metaclass for defining an interfaceable API.
    '''
    
    def __new__(mcls, clsname, bases, namespace, *args, _short_circuit=False,
                **kwargs):
        ''' Modify the existing namespace:
        1.  remove any @fixture_api methods.
        2.  extract any @public_api.fixture methods
        3.  extract any @public_api.interface methods
        4.  create cls.__fixture__ class object
        5.  create cls.__interface__ class object
        '''
        # Dead-end for fixtures and interfaces
        if _short_circuit:
            cls = super().__new__(mcls, clsname, bases, namespace, *args,
                                  **kwargs)
        
        # For the parent/public class...
        else:
            public_name = clsname
            fixture_name = clsname + 'Fixture'
            interface_name = clsname + 'Interface'
                
            public_namespace = {}
            fixture_namespace = {}
            interface_namespace = {}
        
            # No need to modify bases, either for the actual type or the
            # fixture/interface
            
            # Iterate over the entire defined namespace.
            for name, obj in namespace.items():
                is_fixture = getattr(obj, '__is_fixture__', False)
                is_interface = getattr(obj, '__is_interface__', False)
                
                # If the .fixture magic attr was defined, use it; else, just
                # fall back to inheritance
                fixture = getattr(obj, '__fixture__', None)
                interface = getattr(obj, '__interface__', None)
                
                # __is_fixture__ get sent only to the fixture.
                if is_fixture:
                    fixture_namespace[name] = obj
                    
                elif is_interface:
                    interface_namespace[name] = obj
                
                # All other objects pass via inheritance.
                else:
                    public_namespace[name] = obj
                
                # If we have an actual fixture defined, then it supercedes
                # public_namespace
                if fixture is not None:
                    fixture_namespace[name] = fixture
                    
                # Same goes with the interface.
                if interface is not None:
                    interface_namespace[name] = interface
            
            # Create the class
            cls = super().__new__(
                mcls,
                public_name,
                bases,
                public_namespace,
                *args,
                **kwargs
            )
            
            # Figure out the fixture inheritance tree. Fixtures always take
            # precedent over parents, but also always inherit them.
            fixture_bases = (cls, *(
                base.__fixture__ if hasattr(base, '__fixture__') else base
                for base in bases
            ))
            interface_bases = (cls, *(
                base.__interface__ if hasattr(base, '__interface__') else base
                for base in bases
            ))
            
            # NOTE:
            # See Python docs on super() with zero args: "the compiler fills in
            # the necessary details to correctly retrieve the class being
            # defined". Therefore, this is necessary when using fixture_bases:
            # super(Class, self).__init__(*args, **kwargs)
            
            # Now add in the types for both the fixture and the interface.
            # Reuse same bases for both, but use the REAL metaclass for it.
            # TODO: make the fixture inherit from the cls above, so that it can
            # have access to the original methods using super()
            cls.__fixture__ = mcls(
                fixture_name,
                fixture_bases,
                fixture_namespace,
                *args,
                _short_circuit = True,
                **kwargs
            )
            cls.__interface__ = mcls(
                interface_name,
                interface_bases,
                interface_namespace,
                *args,
                _short_circuit = True,
                **kwargs
            )
        
        # And don't forget to return the final cls object.
        return cls
        
    def __init__(cls, *args, _short_circuit=False, **kwargs):
        # Just strip out _short_circuit
        super().__init__(*args, **kwargs) def public_api(func):
    ''' Decorator to automatically mark the object as the normal thing.
    '''
    
    def fixture_closure(fixture_func, public_func=func):
        ''' Defines the decorator for @method.fixture.
        '''
        # This is the actual __fixture__ method, to be defined via decorator
        public_func.__fixture__ = fixture_func
        return public_func
    
    def interface_closure(interface_func, public_func=func):
        ''' Defines the decorator for @method.fixture.
        '''
        # This is the actual __interface method, to be defined via decorator
        public_func.__interface__ = interface_func
        return public_func
        
    func.fixture = fixture_closure
    func.interface = interface_closure
    
    # This denotes that it is an API
    func.__is_api__ = True
    
    return func def fixture_noop(func):
    ''' Create a no-op version of the function to use as its fixture.
    Should be used as a decorator above @public_api.
    '''
    signature = inspect.Signature.from_callable(func)
    
    if inspect.iscoroutinefunction(func):
        @func.fixture
        @functools.wraps(func)
        async def noop_fixture(*args, __signature=signature, **kwargs):
            # Make sure the passed args, kwargs are correct, but otherwise,
            # do nothing.
            __signature.bind(*args, **kwargs)
    
    else:
        @func.fixture
        @functools.wraps(func)
        def noop_fixture(*args, __signature=signature, **kwargs):
            # Make sure the passed args, kwargs are correct, but otherwise,
            # do nothing.
            __signature.bind(*args, **kwargs)
        
    return noop_fixture def fixture_api(func):
    ''' Decorator to mark the method as a fixture-only object.
    '''
    ''' Decorator to automatically mark the object as the normal thing.
    '''
    
    # Huh, well, this is easy.
    func.__is_fixture__ = True
    return func def immortal_property(name):
    ''' Create a property that cannot be deleted.
    '''
    @property
    def prop(self, name=name):
        return getattr(self, name)
        
    @prop.setter
    def prop(self, value, name=name):
        return setattr(self, name, value)
        
    return prop def weak_property(name):
    ''' Make a weakly-referenced property using name.
    '''
    @_WeakProperty
    def prop(self, name=name):
        return getattr(self, name)
        
    @prop.setter
    def prop(self, value, name=name):
        return setattr(self, name, value)
        
    return prop class SetMap:
    ''' Combines a mapping with a set. Threadsafe.
    '''
    
    def __init__(self):
        ''' Create a lookup!
        
        Currently does not support pre-population during __init__().
        '''
        self._mapping = {}
        self._lock = threading.RLock()
    
    def __getitem__(self, key):
        ''' Pass-through to the core lookup. Will return a frozenset.
        Raises keyerror if missing.
        '''
        with self._lock:
            return frozenset(self._mapping[key])
        
    def get_any(self, key):
        ''' Pass-through to the core lookup. Will return a frozenset.
        Will never raise a keyerror; if key not in self, returns empty
        frozenset.
        '''
        with self._lock:
            try:
                return frozenset(self._mapping[key])
            except KeyError:
                return frozenset()
                
    def pop_any(self, key):
        ''' Unlike other methods, pop_any returns the actual set,
        instead of a frozenset copy.
        '''
        with self._lock:
            try:
                return self._mapping.pop(key)
            except KeyError:
                return set()
        
    def __contains__(self, key):
        ''' Check to see if the key exists.
        '''
        with self._lock:
            return key in self._mapping
        
    def contains_within(self, key, value):
        ''' Check to see if the key exists, AND the value exists at key.
        '''
        with self._lock:
            try:
                return value in self._mapping[key]
            except KeyError:
                return False
        
    def add(self, key, value):
        ''' Adds the value to the set at key. Creates a new set there if
        none already exists.
        '''
        with self._lock:
            try:
                self._mapping[key].add(value)
            except KeyError:
                self._mapping[key] = {value}
                
    def update(self, key, value):
        ''' Updates the key with the value. Value must support being
        passed to set.update(), and the set constructor.
        '''
        with self._lock:
            try:
                self._mapping[key].update(value)
            except KeyError:
                self._mapping[key] = set(value)
                
    def update_all(self, other):
        ''' Updates all keys in other to self.
        '''
        with self._lock:
            for key in other:
                try:
                    self._mapping[key].update(other[key])
                except KeyError:
                    self._mapping[key] = set(other[key])
            
    def _remove_if_empty(self, key):
        ''' Removes a key entirely if it no longer has any values. Will
        suppress KeyError if the key is not found.
        '''
        try:
            if len(self._mapping[key]) == 0:
                del self._mapping[key]
        except KeyError:
            pass
        
    def remove(self, key, value):
        ''' Removes the value from the set at key. Will raise KeyError
        if either the key is missing, or the value is not contained at
        the key.
        '''
        with self._lock:
            try:
                self._mapping[key].remove(value)
            finally:
                self._remove_if_empty(key)
        
    def discard(self, key, value):
        ''' Same as remove, but will never raise KeyError.
        '''
        with self._lock:
            try:
                self._mapping[key].discard(value)
            except KeyError:
                pass
            finally:
                self._remove_if_empty(key)
        
    def clear(self, key):
        ''' Clears the specified key. Raises KeyError if key is not
        found.
        '''
        with self._lock:
            del self._mapping[key]
            
    def clear_any(self, key):
        ''' Clears the specified key, if it exists. If not, suppresses
        KeyError.
        '''
        with self._lock:
            try:
                del self._mapping[key]
            except KeyError:
                pass
        
    def clear_all(self):
        ''' Clears the entire mapping.
        '''
        with self._lock:
            self._mapping.clear()
            
    def __len__(self):
        ''' Returns the length of the mapping only.
        '''
        return len(self._mapping)
            
    def __iter__(self):
        ''' Send this through to the dict, bypassing the usual route,
        because otherwise we'll have a deadlock.
        '''
        with self._lock:
            for key in self._mapping:
                yield key
            
    def __eq__(self, other):
        ''' Expand comparison to search insides.
        '''
        with self._lock, other._lock:
            # First make sure both have same mapping keys.
            if set(self._mapping) != set(other._mapping):
                return False
            
            # Okay, now check each key has identical sets
            else:
                for this_key, this_set in self._mapping.items():
                    if other._mapping[this_key] != this_set:
                        return False
                        
        # If we successfully got through all of that, they are identical.
        return True
        
    def combine(self, other):
        ''' Returns a new SetMap with the union of both.
        '''
        new = type(self)()
        # Note that the iterator will take the lock, so we don't need to.
        for key in self:
            new.update(key, self._mapping[key])
        for key in other:
            new.update(key, other._mapping[key])
        return new

    def __getstate__(self):
        ''' Ignore self._lock to support pickling. Basically boilerplate
        copy from the pickle reference docs.
        '''
        # Copy the object's state from self.__dict__ which contains
        # all our instance attributes. Always use the dict.copy()
        # method to avoid modifying the original state.
        state = self.__dict__.copy()
        # Remove the unpicklable entries.
        del state['_lock']
        return state

    def __setstate__(self, state):
        ''' Ignore self._lock to support pickling. Basically boilerplate
        copy from the pickle reference docs.
        '''
        # Restore instance attributes (i.e., filename and lineno).
        self.__dict__.update(state)
        # Restore the lock.
        self._lock = threading.RLock()
        
    def __bool__(self):
        # Pass bool straight to mapping.
        return bool(self._mapping) class GAO(GAOCore):
    ''' A bare-bones GAO that performs trivial serialization (the
    serialization of the state is the state itself).
    '''
    
    def __init__(self, *args, state, **kwargs):
        super().__init__(*args, **kwargs)
        self.state = state
        
    async def pack_gao(self):
        ''' Packs self into a bytes object. May be overwritten in subs
        to pack more complex objects. Should always be a staticmethod or
        classmethod.
        
        May be used to implement, for example, packing self into a
        DispatchableState, etc etc.
        '''
        return self.state
        
    async def unpack_gao(self, packed):
        ''' Unpacks state from a bytes object and applies state to self.
        May be overwritten in subs to unpack more complex objects.
        
        May be used to implement, for example, dicts performing a
        clear() operation before an update() instead of just reassigning
        the object.
        '''
        self.state = packed
        
    def __eq__(self, other):
        ''' Ensure the other object is the same gao with the same state.
        '''
        equal = True
        
        try:
            equal &= (self.dynamic == other.dynamic)
            equal &= (self.ghid == other.ghid)
            equal &= (self.author == other.author)
            equal &= (self.state == other.state)
            
        except AttributeError:
            equal = False
        
        return equal class GAOSet(_GAOPickleBase, _SetMixin):
    ''' Combine GAO sets with pickle serialization.
    '''
    pass class GAODict(_GAOPickleBase, _DictMixin):
    ''' Combine GAO dicts with pickle serialization.
    '''
    pass class GAOSetMap(_GAOPickleBase, _SetMapMixin):
    ''' Combine GAO setmaps with pickle serialization.
    '''
    pass
import logging
import os
import asyncio
import random
from loopa.utils import make_background_future
from hashlib import sha512
from golix import Ghid
from golix import Secret
from golix import FirstParty
from golix._getlow import GIDC
from .persistence import _GidcLite
from .hypothetical import API
from .hypothetical import public_api
from .hypothetical import fixture_noop
from .hypothetical import fixture_api
from .utils import immortal_property
from .utils import weak_property
from .utils import SetMap
from .gao import GAO
from .gao import GAOSet
from .gao import GAODict
from .gao import GAOSetMap
'''
LICENSING
-------------------------------------------------

hypergolix: A python Golix client.
    Copyright (C) 2016 Muterra, Inc.
    
    Contributors
    ------------
    Nick Badger
        badg@muterra.io | badg@nickbadger.com | nickbadger.com

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the
    Free Software Foundation, Inc.,
    51 Franklin Street,
    Fifth Floor,
    Boston, MA  02110-1301 USA

------------------------------------------------------
'''

# External deps

# This is only used for padding **within** encrypted containers


# from golix import SecondParty

# These deps are for publishing our identity elsewhere (super awkwardly)

# Internal deps



# Local dependencies
# from .persistence import _GarqLite
# from .persistence import _GdxxLite


# ###############################################
# Boilerplate
# ###############################################


logger = logging.getLogger(__name__)

# Control * imports.
__all__ = [
    # 'Inquisitor',
]


# ###############################################
# Library
# ###############################################


class Account(metaclass=API):
    ''' Accounts settle all sorts of stuff.
    
    TODO: move GolixCore into account. That way, everything to do with
    the private keys stays within the account, and is never directly
    accessed outside of it.
    '''

    _identity = immortal_property('__identity')