class AccountConfig(ConfigBase):

    def __init__(self, account_id):
        ConfigBase.__init__(self, default=ACCOUNT_CFG)
        self.log = logging.getLogger('AccountConfig')
        self.basedir = os.path.join(BASEDIR, 'accounts', account_id)

        if XDG_CACHE:
            cachedir = BaseDirectory.xdg_cache_home
            self.imgdir = os.path.join(cachedir, 'turpial',
                                       account_id, 'images')
        else:
            self.imgdir = os.path.join(self.basedir, 'images')

        self.configpath = os.path.join(self.basedir, 'config')

        self.log.debug('CACHEDIR: %s' % self.imgdir)
        self.log.debug('CONFIGFILE: %s' % self.configpath)

        if not os.path.isdir(self.basedir):
            os.makedirs(self.basedir)
        if not os.path.isdir(self.imgdir):
            os.makedirs(self.imgdir)
        if not self.exists(account_id):
            self.create()

        try:
            self.load()
        except Exception, exc:
            self.load_failsafe()

        if not self.exists(account_id):
            self.write('Login', 'username', get_username_from(account_id))
            self.write('Login', 'protocol', get_protocol_from(account_id))

    @staticmethod
    def exists(account_id):
        basedir = os.path.join(BASEDIR, 'accounts', account_id)
        configpath = os.path.join(basedir, 'config')

        if not os.path.isfile(configpath):
            return False
        return True

    # DEPRECATE: Remove verifier in the next stable version
    def save_oauth_credentials(self, key, secret, verifier=None):
        self.write('OAuth', 'key', key)
        self.write('OAuth', 'secret', secret)

    # DEPRECATE: Remove verifier in the next stable version
    def load_oauth_credentials(self):
        key = self.read('OAuth', 'key')
        secret = self.read('OAuth', 'secret')
        if key and secret:
            return key, secret
        else:
            raise EmptyOAuthCredentials

    def forget_oauth_credentials(self):
        self.write('OAuth', 'key', '')
        self.write('OAuth', 'secret', '')

    def transform(self, pw, us):
        a = base64.b16encode(pw)
        b = us[0] + a + ('%s' % us[-1])
        c = base64.b32encode(b)
        d = ('%s' % us[-1]) + c + us[0]
        e = base64.b64encode(d)
        f = [e[i] for i in range(len(e))]
        f.reverse()
        return ''.join(f)

    def revert(self, pw, us):
        if pw == '':
            return None
        z = [pw[i] for i in range(len(pw))]
        z.reverse()
        y = ''.join(z)
        x = base64.b64decode(y)
        w = ('%s' % x[1:len(x)])[:-1]
        v = base64.b32decode(w)
        u = ('%s' % v[:len(v) - 1])[1:]
        return base64.b16decode(u)

    # TODO: Return True on success?
    def dismiss(self):
        if os.path.isdir(self.imgdir):
            shutil.rmtree(self.imgdir)
            self.log.debug('Removed cache directory')
        if os.path.isfile(self.configpath):
            os.remove(self.configpath)
            self.log.debug('Removed configuration file')
        if os.path.isdir(self.basedir):
            shutil.rmtree(self.basedir)
            self.log.debug('Removed base directory')

    def delete_cache(self):
        """
        Returns a list of unsusccessful deletions
        """
        unsuccessful = list()
        for root, dirs, files in os.walk(self.imgdir):
            for f in files:
                try:
                    path = os.path.join(root, f)
                    self.log.debug("Deleting %s" % path)
                    os.remove(path)
                except AttributeError:
                    unsuccessful.append(path)
        self.log.debug('There were unsuccessful deletions %s' % unsuccessful)
        return unsuccessful 

    def calculate_cache_size(self):
        size = 0
        for root, dirs, files in os.walk(self.imgdir):
            for f in files:
                path = os.path.join(root, f)
                size += os.path.getsize(path)
        return size class List(object):
    """
    This class handles the information about a user list. It receives the
    *id_*, the *user* who owns it, the *name* (also known as slug), the *title*
    or caption, the number of *suscribers*, it *description* and the units
    used for plural and singular: *singular_unit*, *plural_unit*.

    :ivar id_: List id
    :ivar user: User that owns the list
    :ivar slug: List name
    :ivar title: List title
    :ivar suscribers: Suscribed users (count)
    :ivar description: List description
    :ivar single_unit: Singularized unit ('tweet' for example)
    :ivar plural_unit: Pluralized unit ('tweet' for example)
    """
    # TODO: Initialize with some values (like column)
    def __init__(self):
        self.id_ = None
        self.user = None
        self.slug = None
        self.title = None
        self.suscribers = None
        self.description = None
        self.single_unit = None
        self.plural_unit = None

    def __repr__(self):
        return "libturpial.api.models.List %s" % (self.id_)

    def __str__(self):
        return '{user}: {title}'.format(title=self.title, user=self.user.get('screen_name', ''))

    def __unicode__(self):
        return u'%s' % self.__str__() class Column(object):
    """
    This model represents a column that holds
    :class:`libturpial.api.models.status.Status` objects. You need to specify
    to what *account_id* are they associated, as well as the column *slug*.
    Available column slugs are available in
    :class:`libturpial.common.ColumnType`.

    :ivar id_: Column id (for example: "johndoe-twitter-timeline")
    :ivar slug: Column slug
    :ivar account_id: id of account associated to the column
    :ivar size: max number of statuses that this column can hold
    :ivar singular_unit: unit used to identify one status (e.g: 'tweet')
    :ivar plural_unit: unit used to identify more than one status
          (e.g: 'tweets')
    """
    def __init__(self, account_id, slug, singular_unit='tweet',
                 plural_unit='tweets'):
        self.size = 0
        self.id_ = "%s-%s" % (account_id, slug)  # username-protocol-column
        self.slug = slug
        self.account_id = account_id
        self.updating = False
        self.singular_unit = singular_unit
        self.plural_unit = plural_unit

    def __repr__(self):
        return "libturpial.api.models.Column %s" % (self.id_)

    def __str__(self):
        return '%s: %s' % (self.account_id, self.slug)

    def __unicode__(self):
        return u'%s' % self.__str__() class Profile:
    """
    This model handles all the information stored for a user profile.

    :ivar id_: Profile id
    :ivar account_id: Account id that fetched this profile
    :ivar fullname: Full name of the user
    :ivar avatar: Display image
    :ivar location: User location
    :ivar url: User URL
    :ivar bio: User bio or description
    :ivar following: Indicate if the user is following to the account_id
                     owner (`True`or `False`)
    :ivar followed_by: Indicate if the user is followed by the account_id
                       owner (`True`or `False`)
    :ivar follow_request: Indicate if there is a pending follow request
                          of this profile
    :ivar followers_count: Number of followers of this user
    :ivar friends_count: Number of friends of this user
    :ivar favorites_count: Number of favorite statuses of this user
    :ivar statuses_count: Number of statuses this user has done
    :ivar link_color: Color used to highlight entities (URLs, hashtags, etc)
    :ivar last_update: Text of the last status updated
    :ivar last_update_id: Id of the last status updated
    :ivar protected: Indicate if the profile is protected (`True`or `False`)
    :ivar verified: Indicate if the profile is verified (`True`or `False`)

    """

    def __init__(self):
        self.id_ = None
        self.account_id = None
        self.fullname = None
        self.username = None
        self.avatar = None
        self.location = ''
        self.url = ''
        self.bio = ''
        self.following = None
        self.followed_by = None
        self.follow_request = False
        self.followers_count = 0
        self.friends_count = 0
        self.link_color = None
        self.statuses_count = 0
        self.favorites_count = 0
        self.last_update = None
        self.last_update_id = None
        self.recent_updates = []
        self.tmp_avatar_path = None
        self.protected = False
        self.verified = False
        self.muted = False

    def __repr__(self):
        return "libturpial.api.models.Profile %s" % (self.username)

    def __str__(self):
        return '%s: %s' % (self.username, self.fullname)

    def __unicode__(self):
        return u'%s' % self.__str__()

    def is_me(self):
        """
        Return `True` if the username of the profile is the same of the
        associated account, otherwise `False`. This method can be useful to
        determinate if a status belongs to given account.
        """
        if self.username == self.account_id.split('-')[0]:
            return True
        return False

    def get_protocol_id(self):
        """
        Returns the *protocol_id* for this profile
        """
        if self.account_id:
            return self.account_id.split('-')[1]
        else:
            return None class Account(object):
    """
    This class represents an user account and holds all it related methods.
    This is done thanks to one :class:`libturpial.lib.interfaces.protocol.Protocol`
    instance associated to the user account that handles all the dirty work
    against the service (Twitter, Identi.ca, etc) as well as one
    :class:`libturpial.api.models.profile.Profile` model that store the user
    details.

    This is the class you must instantiate if you want to handle/authenticate
    a user account.

    *Account* let you perform three actions to build an account: create a new
    account from scratch, create a new account from parameters and load a
    previously registered account. To create a new account from scratch do:

    >>> account = Account.new('twitter')

    If you know the user name too, then you can pass it as argument:

    >>> account = Account.new('twitter', 'username')

    At this point, that account is not a valid account yet because it
    hasn't been authenticated. You should do the authentication by yourself.
    This is, request OAuth access:

    >>> url = account.request_oauth_access()

    That method will return an URL that your user must visit to authorize the
    application. After that, you must to ask for the PIN returned by the
    service and execute:

    >>> account.authorize_oauth_access('the_pin')

    And voilá! You now have a valid and fully authenticated account ready to be
    registered in :class:`libturpial.api.core.Core`.

    But *Account* let you create accounts passing all the parameters needed for
    the OAuth authentication. If you already know those parameters (user key,
    user secret and PIN) then you just need to execute:

    >>> account = Account.new('twitter', 'username', 'key', \
                              'secret', 'the_pin')

    And you will have a valid and fully authenticated account ready to be
    registered in :class:`libturpial.api.core.Core` too.

    Now, what if you did all this process before and registered the account
    in :class:`libturpial.api.core.Core`? Well, you just need to load the
    account then:

    >>> account = Account.load('username-twitter')

    And you will have an account already authenticated and ready to be used.

    From this point you can use the method described here to handle the
    account object.

    """

    def __init__(self, protocol_id, username=None):
        self.id_ = None
        self.config = None
        self.protocol_id = protocol_id

        if username:
            self.__setup(username)

        self.columns = []
        self.profile = None
        self.friends = None
        self.lists = None

        if self.protocol_id == Protocol.TWITTER:
            self.protocol = twitter.Main()
        elif self.protocol_id == Protocol.IDENTICA:
            self.protocol = identica.Main()

    def __str__(self):
        try:
            value = self.username
        except AttributeError:
            value = 'Unknown %s account' % self.protocol
        finally:
            return value

    def __unicode__(self):
        return u'%s' % self.__str__()

    def __repr__(self):
        return "libturpial.api.models.Account %s-%s" % (self.username, self.protocol_id)

    def __setup(self, username):
        self.id_ = build_account_id(username, self.protocol_id)
        self.username = username

    @staticmethod
    def new(protocol_id, username=None, key=None, secret=None):
        """
        Return a new account object associated to the protocol identified by
        *protocol_id*. If *username* is not None it will build the account_id
        for the account.

        If you don't enter the `key` and `secret` parameters this account is
        empty and must be authenticated before it can be registered
        in :class:`libturpial.api.core.Core`. Otherwise, this account is
        authenticated after their creation, so it can be registered in
        :class:`libturpial.api.core.Core` immediately.

        .. warning::
            None information is stored at disk at this point.

        """
        account = Account(protocol_id, username)
        if key and secret:
            account.setup_user_credentials(account.id_, key, secret)
        return account

    @staticmethod
    def new_from_params(protocol_id, username, key, secret, verifier):
        """
        .. deprecated:: 2.0

        Use :meth:`new` instead.

        """
        warnings.warn("Shouldn't use this method anymore, please use new",
                      DeprecationWarning)
        account = Account.new(protocol_id, username, key, secret)

        return account

    @staticmethod
    def load(account_id):
        """
        Return the account object associated to *account_id* loaded from
        existing configuration. If the *account_id* does not correspond to a
        valid account it returns a
        :class:`libturpial.exceptions.ErrorLoadingAccount` exception.

        If credentials in configuration file are empty it returns a
        :class:`libturpial.exceptions.EmptyOAuthCredentials` exception.
        """
        if not AccountConfig.exists(account_id):
            raise ErrorLoadingAccount("Account has no stored credentials")

        username = get_username_from(account_id)
        protocol_id = get_protocol_from(account_id)

        account = Account.new(protocol_id, username)
        account.config = AccountConfig(account_id)
        key, secret = account.config.load_oauth_credentials()
        account.setup_user_credentials(account.id_, key, secret)
        account.fetch()
        return account

    def request_oauth_access(self):
        """
        Ask for an OAuth token. Return an URL that must be visited for the user
        in order to authenticate the app.
        """
        return self.protocol.request_token()

    def authorize_oauth_access(self, pin):
        """
        Take the *pin* returned by OAuth service and authenticate the token
        requested with *request_oauth_access*
        """
        self.profile = self.protocol.authorize_token(pin)
        self.__setup(self.profile.username)

    # TODO: Return True if success?
    def save(self):
        """
        Save to disk the configuration and credentials for the account. If the
        account hasn't been authenticated it will raise an
        :class:`libturpial.exceptions.AccountNotAuthenticated` exception.
        """
        if not self.is_authenticated():
            raise AccountNotAuthenticated

        self.config = AccountConfig(self.id_)
        token = self.get_oauth_token()
        if token:
            self.config.save_oauth_credentials(token.key, token.secret)

    def fetch(self):
        """
        Retrieve the user profile information and return the id of the account
        on success. This method authenticate the account.
        """
        self.profile = self.protocol.verify_credentials()
        self.lists = self.protocol.get_lists(self.profile.username)

        self.columns = [
            Column(self.id_, ColumnType.TIMELINE),
            Column(self.id_, ColumnType.REPLIES),
            Column(self.id_, ColumnType.DIRECTS),
            Column(self.id_, ColumnType.SENT),
            Column(self.id_, ColumnType.FAVORITES)] + self.lists
        return self.id_

    def fetch_friends(self):
        """
        Retrieve all the friends for the user and return an array of string
        where each element correspond to the user_id of the friend.
        """
        self.friends = self.protocol.get_following(only_id=True)
        return self.friends

    def get_columns(self):
        """
        Return an array of :class:`libturpial.api.models.column.Column` with
        all the available columns for the user
        """
        return self.columns

    def get_list_id(self, list_name):
        if not self.lists:
            return None

        for li in self.lists:
            if li.slug == list_name:
                return li.id_
        return None

    # TODO: Return True?
    def purge_config(self):
        """
        Delete all config files related to this account
        """
        self.config.dismiss()

    # TODO: Return True?
    def delete_cache(self):
        """
        Delete all files cached for this account
        """
        self.config.delete_cache()

    def get_cache_size(self):
        """
        Return disk space used by cache in bytes
        """
        return self.config.calculate_cache_size()

    def is_authenticated(self):
        """
        Return `True` if the current account has been logged in, `False`
        otherwise
        """
        return self.profile is not None and self.id_ is not None

    def update_profile(self, fullname=None, url=None, bio=None, location=None):
        """
        Update the *fullname*, *url*, *bio* or *location* of the user profile.
        You may specify one or more arguments. Return an
        :class:`libturpial.api.models.profile.Profile` object containing the
        user profile
        """
        self.profile = self.protocol.update_profile(fullname, url, bio,
                                                    location)
        return self.profile

    def __getattr__(self, name):
        try:
            return getattr(self.protocol, name)
        except:
            try:
                return getattr(self.profile, name)
            except:
                raise AttributeError class Main(Protocol):
    """Twitter implementation for libturpial"""
    def __init__(self):
        self.id_ = 'twitter'
        self.base_url = 'https://api.twitter.com/1.1'
        self.search_url = 'https://api.twitter.com/1.1'
        self.hashtags_url = 'https://twitter.com/search?q=%23'
        self.profiles_url = 'https://www.twitter.com'

        Protocol.__init__(self)

    def __build_basic_args(self, count, since_id):
        args = {'count': count, 'include_entities': True}
        if since_id:
            args['since_id'] = since_id
        return args

    def check_for_errors(self, response):
        """
        Receives a json response and raise an exception if there are errors
        """
        if type(response) == list:
            return

        if 'errors' in response:
            code = response['errors'][0]['code']
            message = response['errors'][0].get('message', '')
            exception = APIExceptionManager.get_exception_class(code)
            raise exception(message)

    def initialize_http(self):
        app_config = AppConfig()
        proxy = app_config.get_proxy()
        timeout = app_config.get_socket_timeout()
        self.http = TurpialHTTPOAuth(self.base_url, OAUTH_OPTIONS, proxies=proxy.to_url_config(),
                timeout=timeout)

    def setup_user_info(self, account_id):
        self.account_id = account_id
        self.uname = account_id.split('-')[0]

    def setup_user_credentials(self, account_id, key, secret, verifier=None):
        self.setup_user_info(account_id)
        self.http.set_token_info(key, secret, verifier)

    def request_token(self):
        return self.http.request_token()

    def authorize_token(self, pin):
        self.http.authorize_token(pin)
        self.http.set_token_info(self.http.token.key, self.http.token.secret,
                                 self.http.token.verifier)
        return self.verify_credentials()

    def get_oauth_token(self):
        return self.http.token

    #################################################################
    # Methods related to Twitter service
    #################################################################

    def verify_credentials_provider(self, format_='json'):
        return "%s/account/verify_credentials.%s" % (self.base_url, format_)

    def verify_credentials(self):
        rtn = self.http.get('/account/verify_credentials', secure=True)
        self.check_for_errors(rtn)
        profile = self.json_to_profile(rtn)
        self.uname = profile.username
        self.account_id = build_account_id(self.uname, self.id_)
        return profile

    def get_timeline(self, count=NUM_STATUSES, since_id=None):
        args = self.__build_basic_args(count, since_id)
        rtn = self.http.get('/statuses/home_timeline', args)
        self.check_for_errors(rtn)
        return self.json_to_status(rtn, StatusColumn.TIMELINE)

    def get_replies(self, count=NUM_STATUSES, since_id=None):
        args = self.__build_basic_args(count, since_id)
        rtn = self.http.get('/statuses/mentions_timeline', args)
        self.check_for_errors(rtn)
        return self.json_to_status(rtn, StatusColumn.REPLIES)

    def get_directs(self, count=NUM_STATUSES, since_id=None):
        args = self.__build_basic_args(count, since_id)
        rtn = self.http.get('/direct_messages', args)
        self.check_for_errors(rtn)
        return self.json_to_status(rtn, StatusColumn.DIRECTS,
                                   type_=Status.DIRECT)

    def get_directs_sent(self, count=NUM_STATUSES, since_id=None):
        args = self.__build_basic_args(count, since_id)
        rtn = self.http.get('/direct_messages/sent', args)
        self.check_for_errors(rtn)
        return self.json_to_status(rtn, StatusColumn.DIRECTS,
                                   type_=Status.DIRECT)

    def get_sent(self, count=NUM_STATUSES, since_id=None):
        args = self.__build_basic_args(count, since_id)
        args['include_rts'] = True
        rtn = self.http.get('/statuses/user_timeline', args)
        self.check_for_errors(rtn)
        return self.json_to_status(rtn, StatusColumn.SENT)

    def get_favorites(self, count=NUM_STATUSES):
        rtn = self.http.get('/favorites/list', {'include_entities': True})
        self.check_for_errors(rtn)
        return self.json_to_status(rtn, StatusColumn.FAVORITES)

    def get_public_timeline(self, count=NUM_STATUSES, since_id=None):
        args = self.__build_basic_args(count, since_id)
        rtn = self.http.get('/statuses/firehose', args)
        self.check_for_errors(rtn)
        return self.json_to_status(rtn, StatusColumn.PUBLIC)

    def get_lists(self, username):
        rtn = self.http.get('/lists/list', {'screen_name': username})
        self.check_for_errors(rtn)
        lists = self.json_to_list(rtn)
        return lists

    def get_list_statuses(self, list_id, count=NUM_STATUSES, since_id=None):
        args = {'list_id': list_id, 'per_page': count,
                'include_entities': True}
        if since_id:
            args['since_id'] = since_id
        rtn = self.http.get('/lists/statuses', args)
        self.check_for_errors(rtn)
        return self.json_to_status(rtn, list_id)

    def get_conversation(self, status_id):
        conversation = []

        while True:
            rtn = self.http.get('/statuses/show', {'id': status_id,
                                'include_entities': True})
            conversation.append(self.json_to_status(rtn,
                                StatusColumn.CONVERSATION))

            if rtn['in_reply_to_status_id']:
                status_id = str(rtn['in_reply_to_status_id'])
            else:
                break
        return conversation

    def get_status(self, status_id):
        rtn = self.http.get('/statuses/show', {'id': status_id,
                            'include_entities': True})
        self.check_for_errors(rtn)
        return self.json_to_status(rtn)

    def get_followers(self, only_id=False):
        cursor = -1
        current = 0
        max_friends_req = 100
        followers = []

        while 1:
            # Fetch user_ids (up to 5000 for each request)
            rtn = self.http.get('/followers/ids', {'cursor': cursor})
            total = len(rtn['ids'])
            if total == 0:
                break

            if only_id:
                for id_ in rtn['ids']:
                    followers.append(str(id_))
            else:
                while 1:
                    if current + max_friends_req <= total:
                        batch = rtn['ids'][current:current + max_friends_req]
                        current += max_friends_req
                    else:
                        batch = rtn['ids'][current:total]
                        current = total

                    # Fetch user details (up to 100 for each request)
                    user_ids = ','.join([str(x) for x in batch])
                    rtn2 = self.http.get('/users/lookup',
                                         {'user_id': user_ids})
                    for user in rtn2:
                        followers.append(self.json_to_profile(user))
                    if current == total:
                        break

            if rtn['next_cursor'] > 0:
                cursor = rtn['next_cursor']
            else:
                break

        return followers

    def get_following(self, only_id=False):
        cursor = -1
        current = 0
        max_friends_req = 100
        following = []

        while 1:
            # Fetch user_ids (up to 5000 for each request)
            rtn = self.http.get('/friends/ids', {'cursor': cursor})
            total = len(rtn['ids'])
            if total == 0:
                break

            if only_id:
                for id_ in rtn['ids']:
                    following.append(str(id_))
            else:
                while 1:
                    if current + max_friends_req <= total:
                        batch = rtn['ids'][current:current + max_friends_req]
                        current += max_friends_req
                    else:
                        batch = rtn['ids'][current:total]
                        current = total

                    # Fetch user details (up to 100 for each request)
                    user_ids = ','.join([str(x) for x in batch])
                    rtn2 = self.http.get('/users/lookup',
                                         {'user_id': user_ids})
                    for user in rtn2:
                        following.append(self.json_to_profile(user))
                    if current == total:
                        break

            if rtn['next_cursor'] > 0:
                cursor = rtn['next_cursor']
            else:
                break

        return following

    def get_profile(self, user):
        rtn = self.http.get('/users/show', {'screen_name': user})
        profile = self.json_to_profile(rtn)
        rtn = self.http.get('/statuses/user_timeline',
                            {'screen_name': user, 'count': 10,
                             'include_entities': True})
        profile.recent_updates = self.json_to_status(rtn)
        return profile

    def get_blocked(self):
        rtn = self.http.get('/blocks/list')
        self.check_for_errors(rtn)
        return self.json_to_profile(rtn['users'])

    def get_repeaters(self, status_id, only_username=False):
        users = []
        rtn = self.http.get('/statuses/retweets/%s' % status_id)
        self.check_for_errors(rtn)
        for item in rtn:
            if only_username:
                users.append(item['user']['screen_name'])
            else:
                profile = self.json_to_profile(item['user'])
                users.append(profile)
        return users

    def update_profile(self, fullname=None, url=None, bio=None, location=None):
        args = {}
        if fullname:
            args['name'] = fullname
        if url:
            args['url'] = url
        if location:
            args['location'] = location
        if bio:
            args['description'] = bio
        if not args:
            raise InvalidOrMissingArguments

        rtn = self.http.post('/account/update_profile', args)
        self.check_for_errors(rtn)
        return self.json_to_profile(rtn)

    def update_status(self, text, in_reply_id=None, media=None):
        if in_reply_id:
            args = {'status': text, 'in_reply_to_status_id': in_reply_id}
        else:
            args = {'status': text}
        args['include_entities'] = True

        if media is not None:
            files = {'media[]': open(media, 'rb')}
            rtn = self.http.post('/statuses/update_with_media', args, files=files)
        else:
            rtn = self.http.post('/statuses/update', args)
        self.check_for_errors(rtn)
        return self.json_to_status(rtn)

    def destroy_status(self, status_id):
        rtn = self.http.post('/statuses/destroy', {'id': status_id,
                             'include_entities': True}, id_in_url=True)
        self.check_for_errors(rtn)
        return self.json_to_status(rtn)

    def repeat_status(self, status_id):
        rtn = self.http.post('/statuses/retweet', {'id': status_id}, id_in_url=True)
        self.check_for_errors(rtn)
        status = self.json_to_status(rtn)
        status.repeated_by = self.get_repeaters(status_id)
        return status

    def mark_as_favorite(self, status_id):
        rtn = self.http.post('/favorites/create', {'id': status_id,
                             'include_entities': True})
        self.check_for_errors(rtn)
        return self.json_to_status(rtn)

    def unmark_as_favorite(self, status_id):
        rtn = self.http.post('/favorites/destroy', {'id': status_id,
                             'include_entities': True})
        self.check_for_errors(rtn)
        return self.json_to_status(rtn)

    def follow(self, screen_name, by_id=False):
        if by_id:
            arg = {'user_id': screen_name}
        else:
            arg = {'screen_name': screen_name}
        rtn = self.http.post('/friendships/create', arg)
        self.check_for_errors(rtn)
        return self.json_to_profile(rtn)

    def unfollow(self, screen_name):
        rtn = self.http.post('/friendships/destroy',
                             {'screen_name': screen_name})
        self.check_for_errors(rtn)
        return self.json_to_profile(rtn)

    def send_direct_message(self, screen_name, text):
        args = {'screen_name': screen_name, 'text': text,
                'include_entities': True}
        rtn = self.http.post('/direct_messages/new', args)
        self.check_for_errors(rtn)
        return self.json_to_status(rtn)

    def destroy_direct_message(self, direct_message_id):
        rtn = self.http.post('/direct_messages/destroy',
                             {'id': direct_message_id,
                              'include_entities': True})
        self.check_for_errors(rtn)
        return self.json_to_status(rtn)

    def block(self, screen_name):
        rtn = self.http.post('/blocks/create', {'screen_name': screen_name})
        self.check_for_errors(rtn)
        return self.json_to_profile(rtn)

    def unblock(self, screen_name):
        rtn = self.http.post('/blocks/destroy', {'screen_name': screen_name})
        self.check_for_errors(rtn)
        return self.json_to_profile(rtn)

    def report_as_spam(self, screen_name):
        rtn = self.http.post('/users/report_spam',
                             {'screen_name': screen_name})
        self.check_for_errors(rtn)
        return self.json_to_profile(rtn)

    def search(self, query, count=NUM_STATUSES, since_id=None, extra=None):
        args = self.__build_basic_args(count, since_id)
        args['q'] = query
        if extra:
            args = dict(args.items() + extra.items())

        rtn = self.http.get('/search/tweets', args)
        self.check_for_errors(rtn)
        return self.json_to_status(rtn['statuses'])

    def is_friend(self, user):
        result = self.http.get('/friendships/show',
                               {'source_screen_name': self.uname,
                                'target_screen_name': user})
        return result['relationship']['target']['following']

    def get_profile_image(self, user):
        rtn = self.http.get('/users/show', {'screen_name': user})
        return rtn['profile_image_url'].replace('_normal', '')

    def available_trend_locations(self):
        rtn = self.http.get('/trends/available')
        return self.json_to_trend_location(rtn)

    def trends(self, location_id):
        rtn = self.http.get('/trends/place', {'id': location_id}, id_in_url=False)
        return self.json_to_trend(rtn[0]['trends'])

    def update_profile_image(self, image_path):
        with open(image_path, 'rb') as fd:
            image = fd.read()
        rtn = self.http.post('/account/update_profile_image', files={'image': image})
        return self.json_to_profile(rtn)

    #################################################################
    # Methods to convert JSON responses into objects
    #################################################################

    # TODO: Try to do this with metaprogramming (a single JSON object that
    # knows how to become a turpial object

    def json_to_profile(self, response):

        if isinstance(response, list):
            profiles = []
            for pf in response:
                profile = self.json_to_profile(pf)
                profiles.append(profile)
            return profiles
        else:
            profile = Profile()
            profile.id_ = str(response['id'])
            profile.account_id = self.account_id
            profile.fullname = response['name']
            profile.username = response['screen_name']
            profile.avatar = response['profile_image_url']
            profile.location = response['location']
            profile.url = response['url']
            profile.bio = response['description']
            profile.following = response['following']
            profile.followers_count = response['followers_count']
            profile.friends_count = response['friends_count']
            profile.statuses_count = response['statuses_count']
            profile.follow_request = response['follow_request_sent']
            profile.favorites_count = response['favourites_count']
            profile.protected = response['protected']
            profile.verified = response['verified']
            if 'status' in response:
                profile.last_update = response['status']['text']
                profile.last_update_id = response['status']['id']
            profile.link_color = ('#' + response['profile_link_color']) or \
                Profile.DEFAULT_LINK_COLOR
            return profile

    def json_to_status(self, response, column_id='', type_=Status.NORMAL):
        if isinstance(response, list):
            statuses = []
            for resp in response:
                if not resp:
                    continue
                status = self.json_to_status(resp, column_id, type_)
                statuses.append(status)
            return statuses
        else:
            repeated_by = None
            retweeted_id = None
            if 'retweeted_status' in response:
                repeated_by = response['user']['screen_name']
                retweeted_id = response['id']
                post = response['retweeted_status']
            else:
                post = response

            protected = False
            verified = False
            if 'user' in post:
                username = post['user']['screen_name']
                avatar = post['user']['profile_image_url']
                protected = post['user']['protected']
                verified = post['user']['verified']
            elif 'sender' in post:
                username = post['sender']['screen_name']
                avatar = post['sender']['profile_image_url']
                protected = post['sender']['protected']
                verified = post['sender']['verified']
            elif 'from_user' in post:
                username = post['from_user']
                avatar = post['profile_image_url']

            in_reply_to_id = None
            in_reply_to_user = None
            if 'in_reply_to_status_id' in post and \
                    post['in_reply_to_status_id']:
                in_reply_to_id = post['in_reply_to_status_id']
                if 'in_reply_to_screen_name' in post:
                    in_reply_to_user = post['in_reply_to_screen_name']

            fav = False
            if 'favorited' in post:
                fav = post['favorited']

            repeated = False
            if 'retweeted' in post:
                repeated = post['retweeted']

            source = None
            if 'source' in post:
                source = post['source']

            retweet_count = None
            if 'retweet_count' in post:
                retweet_count = int(post['retweet_count'])

            quoted_status = None
            if 'quoted_status' in post:
                quoted_status = self.json_to_status(post['quoted_status'])

            is_quote_status = False
            if 'is_quote_status' in post:
                is_quote_status = post['is_quote_status']

            status = Status()
            status.id_ = str(post['id'])
            status.original_status_id = retweeted_id
            status.created_at = post['created_at']
            status.username = username
            status.avatar = avatar
            status.text = post['text']
            status.in_reply_to_id = in_reply_to_id
            status.in_reply_to_user = in_reply_to_user
            status.is_favorite = fav
            status.is_protected = protected
            status.is_verified = verified
            status.repeated_by = repeated_by
            status.datetime = self.get_str_time(post['created_at'])
            status.timestamp = self.get_int_time(post['created_at'])
            status.entities = self.get_entities(post)
            status.type_ = type_
            status.account_id = self.account_id
            status.is_own = (username.lower() == self.uname.lower())
            status.repeated = repeated
            status.repeated_count = retweet_count
            status.local_timestamp = timestamp_to_localtime(status.timestamp)
            status.get_source(source)
            status.quoted_status = quoted_status
            status.is_quote_status = is_quote_status
            return status

    def json_to_list(self, response):
        if isinstance(response, list):
            lists = []
            for li in response:
                _list = self.json_to_list(li)
                lists.append(_list)
            return lists
        else:
            _list = List()
            _list.id_ = str(response['id'])
            _list.user = response['user']
            _list.slug = response['slug']
            _list.title = response['name']
            _list.suscribers = response['subscriber_count']
            _list.description = response['description']
            _list.single_unit = 'tweet'
            _list.plural_unit = 'tweets'
            return _list

    def json_to_trend(self, response):
        if isinstance(response, list):
            trends = []
            for tr in response:
                trend = self.json_to_trend(tr)
                trends.append(trend)
            return trends
        else:
            trend = Trend(response['name'])
            trend.query = response['query']
            trend.url = response['url']
            if response['promoted_content']:
                trend.is_promoted = True
            return trend

    def json_to_trend_location(self, response):
        if isinstance(response, list):
            locations = []
            for tr in response:
                location = self.json_to_trend_location(tr)
                locations.append(location)
            return locations
        else:
            location = TrendLocation(response['name'], response['woeid'])
            location.country = response['country']
            location.country_code = response['countryCode']
            location.parent_id = response['parentid']
            location.placetype_code = int(response['placeType']['code'])
            location.placetype_name = response['placeType']['name']
            return location

    def get_entities(self, tweet):
        if 'entities' in tweet:
            entities = {
                'urls': [],
                'hashtags': [],
                'mentions': [],
                'groups': [],
            }
            for mention in tweet['entities']['user_mentions']:
                text = '@' + mention['screen_name']
                entities['mentions'].append(Entity(self.account_id,
                                            mention['screen_name'], text,
                                            text))

            for url in tweet['entities']['urls']:
                try:
                    expanded_url = url['expanded_url']
                except KeyError:
                    expanded_url = url['url']

                try:
                    display_url = ''.join(['http://', url['display_url']])
                except KeyError:
                    display_url = url['url']

                entities['urls'].append(Entity(self.account_id, expanded_url,
                                        display_url, url['url']))

            if 'media' in tweet['entities']:
                for url in tweet['entities']['media']:
                    display_url = ''.join(['http://', url['display_url']])
                    entities['urls'].append(Entity(self.account_id,
                                            url['media_url'], display_url,
                                            url['url']))

            for ht in tweet['entities']['hashtags']:
                text = ''.join(['#', ht['text']])
                url = "%s%s" % (self.hashtags_url, ht['text'])
                entities['hashtags'].append(Entity(self.account_id, url, text,
                                            text))
        return entities class Main(Protocol):
    """Identi.ca implementation for libturpial"""
    GROUP_PATTERN = re.compile('(?<![\w])![\wáéíóúÁÉÍÓÚñÑçÇ]+')

    def __init__(self):
        self.uname = None
        self.base_url = 'https://identi.ca/api'
        self.search_url = 'http://identi.ca/api'
        self.hashtags_url = 'http://identi.ca/tag'
        self.profiles_url = 'http://identi.ca'
        self.groups_url = 'http://identi.ca/group'

        Protocol.__init__(self)

    def __build_basic_args(self, count, since_id):
        args = {'count': count, 'include_entities': True}
        if since_id:
            args['since_id'] = since_id
        return args

    def check_for_errors(self, response):
        """
        Receives a json response and raise an exception if there are errors
        """
        if 'error' in response:
            print response
            message = response['error']
            if message.find('Could not authenticate you') >= 0:
                raise InvalidOrMissingCredentials
            elif message.find('duplicated messages') > 0:
                raise StatusDuplicated
            elif message.find("to users who aren't your friend") > 0:
                raise ErrorSendingDirectMessage('User is not following you')
            elif message.find('Maximum notice size is 140 characters') > 0:
                raise StatusMessageTooLong

            # elif code == 34 or code == 404:
            #    raise ResourceNotFound
            # elif code == 64:
            #    raise AccountSuspended
            # elif code == 88:
            #    raise RateLimitExceeded
            # elif code == 89:
            #    raise InvalidOAuthToken
            # elif code == 130 or code == 503 or code == 504:
            #    raise ServiceOverCapacity
            # elif code == 131 or code == 500:
            #    raise InternalServerError
            # elif code == 135:
            #    raise BadOAuthTimestamp
            # elif code == 502:
            #    raise ServiceDown

    def initialize_http(self):
        self.http = TurpialHTTPBasicAuth(self.base_url)

    def setup_user_credentials(self, account_id, username, password):
        self.account_id = account_id
        self.http.set_user_info(username, password)
        self.uname = account_id.split('-')[0]

    #################################################################
    # Methods related to Twitter service
    #################################################################

    def verify_credentials(self):
        rtn = self.http.get('/account/verify_credentials', secure=True)
        self.check_for_errors(rtn)
        profile = self.json_to_profile(rtn)
        self.uname = profile.username
        return profile

    def get_timeline(self, count=NUM_STATUSES, since_id=None):
        args = self.__build_basic_args(count, since_id)
        rtn = self.http.get('/statuses/home_timeline', args)
        self.check_for_errors(rtn)
        return self.json_to_status(rtn, StatusColumn.TIMELINE)

    def get_replies(self, count=NUM_STATUSES, since_id=None):
        args = self.__build_basic_args(count, since_id)
        rtn = self.http.get('/statuses/mentions', args)
        self.check_for_errors(rtn)
        return self.json_to_status(rtn, StatusColumn.REPLIES)

    def get_directs(self, count=NUM_STATUSES, since_id=None):
        args = self.__build_basic_args(count, since_id)
        rtn = self.http.get('/direct_messages', args)
        self.check_for_errors(rtn)
        return self.json_to_status(rtn, StatusColumn.DIRECTS,
                                   type_=Status.DIRECT)

    def get_directs_sent(self, count=NUM_STATUSES, since_id=None):
        args = self.__build_basic_args(count, since_id)
        rtn = self.http.get('/direct_messages/sent', args)
        self.check_for_errors(rtn)
        return self.json_to_status(rtn, StatusColumn.DIRECTS,
                                   type_=Status.DIRECT)

    def get_sent(self, count=NUM_STATUSES, since_id=None):
        args = self.__build_basic_args(count, since_id)
        rtn = self.http.get('/statuses/user_timeline', args)
        self.check_for_errors(rtn)
        return self.json_to_status(rtn, StatusColumn.SENT)

    def get_favorites(self, count=NUM_STATUSES):
        rtn = self.http.get('/favorites')
        self.check_for_errors(rtn)
        return self.json_to_status(rtn, StatusColumn.FAVORITES)

    def get_public_timeline(self, count=NUM_STATUSES, since_id=None):
        args = self.__build_basic_args(count, since_id)
        rtn = self.http.get('/statuses/public_timeline', args)
        self.check_for_errors(rtn)
        return self.json_to_status(rtn, StatusColumn.PUBLIC)

    def get_lists(self, username):
        return []

    def get_list_statuses(self, list_id, user, count=NUM_STATUSES,
                          since_id=None):
        return []

    def get_conversation(self, status_id):
        conversation = []

        while 1:
            rtn = self.http.get('/statuses/show', {'id': status_id})
            conversation.append(self.json_to_status(rtn,
                                StatusColumn.CONVERSATION))

            if rtn['in_reply_to_status_id']:
                status_id = str(rtn['in_reply_to_status_id'])
            else:
                break
        return conversation

    def get_status(self, status_id):
        rtn = self.http.get('/statuses/show', {'id': status_id})
        self.check_for_errors(rtn)
        return self.json_to_status(rtn)

    def get_followers(self, only_id=False):
        followers = []

        if only_id:
            rtn = self.http.get('/followers/ids')
            for id_ in rtn:
                followers.append(str(id_))
        else:
            rtn = self.http.get('/statuses/followers',
                                {'screen_name': self.account_id.split('-')[0]})
            for user in rtn:
                followers.append(self.json_to_profile(user))

        return followers

    def get_following(self, only_id=False):
        following = []

        if only_id:
            rtn = self.http.get('/friends/ids')
            for id_ in rtn:
                following.append(str(id_))
        else:
            rtn = self.http.get('/statuses/friends',
                                {'screen_name': self.account_id.split('-')[0]})
            following = [self.json_to_profile(user) for user in rtn]

        return following

    def get_profile(self, user):
        rtn = self.http.get('/users/show', {'screen_name': user})
        self.check_for_errors(rtn)
        profile = self.json_to_profile(rtn)
        rtn = self.http.get('/statuses/user_timeline',
                            {'screen_name': user, 'count': 10})
        profile.recent_updates = self.json_to_status(rtn)
        return profile

    def get_blocked(self):
        return []

    def get_rate_limits(self):
        rtn = self.http.get('/account/rate_limit_status')
        self.check_for_errors(rtn)
        return self.json_to_ratelimit(rtn)

    def update_profile(self, p_args):
        # We use if's instead update method to guarantee valid arguments
        args = {}
        if 'name' in p_args:
            args['name'] = p_args['name']
        if 'url' in p_args:
            args['url'] = p_args['url']
        if 'description' in p_args:
            args['description'] = p_args['description']
        if 'location' in p_args:
            args['location'] = p_args['location']

        rtn = self.http.post('/account/update_profile', args, secure=True)
        self.check_for_errors(rtn)
        return self.json_to_profile(rtn)

    def update_status(self, text, in_reply_id=None):
        if in_reply_id:
            args = {'status': text, 'in_reply_to_status_id': in_reply_id}
        else:
            args = {'status': text}

        rtn = self.http.post('/statuses/update', args, secure=True)
        self.check_for_errors(rtn)
        return self.json_to_status(rtn)

    def destroy_status(self, status_id):
        rtn = self.http.post('/statuses/destroy', {'id': status_id},
                             secure=True)
        self.check_for_errors(rtn)
        return self.json_to_status(rtn)

    def repeat_status(self, status_id):
        rtn = self.http.post('/statuses/retweet', {'id': status_id},
                             secure=True)
        self.check_for_errors(rtn)
        status = self.json_to_status(rtn)
        return status

    def mark_as_favorite(self, status_id):
        rtn = self.http.post('/favorites/create', {'id': status_id},
                             secure=True)
        self.check_for_errors(rtn)
        return self.json_to_status(rtn)

    def unmark_as_favorite(self, status_id):
        rtn = self.http.post('/favorites/destroy', {'id': status_id},
                             secure=True)
        self.check_for_errors(rtn)
        return self.json_to_status(rtn)

    def follow(self, screen_name, by_id=False):
        if by_id:
            arg = {'user_id': screen_name}
        else:
            arg = {'screen_name': screen_name}
        rtn = self.http.post('/friendships/create', arg, secure=True)
        self.check_for_errors(rtn)
        return self.json_to_profile(rtn)

    def unfollow(self, screen_name):
        rtn = self.http.post('/friendships/destroy',
                             {'screen_name': screen_name}, secure=True)
        self.check_for_errors(rtn)
        return self.json_to_profile(rtn)

    def send_direct_message(self, screen_name, text):
        args = {'screen_name': screen_name, 'text': text}
        rtn = self.http.post('/direct_messages/new', args, secure=True)
        self.check_for_errors(rtn)
        return self.json_to_status(rtn)

    def destroy_direct_message(self, direct_message_id):
        return None

    def block(self, screen_name):
        rtn = self.http.post('/blocks/create', {'screen_name': screen_name},
                             secure=True)
        self.check_for_errors(rtn)
        return self.json_to_profile(rtn)

    def unblock(self, screen_name):
        rtn = self.http.post('/blocks/destroy', {'screen_name': screen_name},
                             secure=True)
        self.check_for_errors(rtn)
        return self.json_to_profile(rtn)

    def search(self, query, count=NUM_STATUSES, since_id=None):
        rtn = self.http.get('/search', {'q': query, 'rpp': count},
                            base_url=self.urls['search'])
        self.check_for_errors(rtn)
        return self.json_to_status(rtn['results'])

    def is_friend(self, user):
        args = {'source_screen_name': self.uname, 'target_screen_name': user}
        result = self.http.get('/friendships/show', args)
        if 'error' in result:
            return None
        else:
            return result['relationship']['target']['following']

    def get_profile_image(self, user):
        result = self.http.get('/users/show', {'screen_name': user})
        return result['profile_image_url']

    #################################################################
    # Methods to convert JSON responses into objects
    #################################################################

    # TODO: Try to do this with metaprogramming (a single JSON object that
    # knows how to become a turpial object

    def json_to_profile(self, response):
        if isinstance(response, list):
            profiles = []
            for pf in response:
                profile = self.json_to_profile(pf)
                profiles.append(profile)
            return profiles
        else:
            profile = Profile()
            profile.id_ = str(response['id'])
            profile.account_id = self.account_id
            profile.fullname = response['name']
            profile.username = response['screen_name']
            profile.avatar = response['profile_image_url']
            profile.location = response['location']
            profile.url = response['url']
            profile.bio = response['description']
            profile.following = response['following']
            profile.followers_count = response['followers_count']
            profile.friends_count = response['friends_count']
            profile.statuses_count = response['statuses_count']
            profile.favorites_count = response['favourites_count']
            profile.protected = response['protected']
            if 'status' in response:
                profile.last_update = response['status']['text']
                profile.last_update_id = response['status']['id']
            # profile.link_color = Profile.DEFAULT_LINK_COLOR
            return profile

    def json_to_status(self, response, column_id='', type_=Status.NORMAL):
        if isinstance(response, list):
            statuses = []
            for resp in response:
                if not resp:
                    continue
                status = self.json_to_status(resp, column_id, type_)
                statuses.append(status)
            return statuses
        else:
            reposted_by = None
            if 'retweeted_status' in response:
                reposted_by = response['user']['screen_name']
                post = response['retweeted_status']
            else:
                post = response

            protected = False
            if 'user' in post:
                username = post['user']['screen_name']
                avatar = post['user']['profile_image_url']
                protected = post['user']['protected']
            elif 'sender' in post:
                username = post['sender']['screen_name']
                avatar = post['sender']['profile_image_url']
                protected = post['sender']['protected']
            elif 'from_user' in post:
                username = post['from_user']
                avatar = post['profile_image_url']

            in_reply_to_id = None
            in_reply_to_user = None
            if 'in_reply_to_status_id' in post and \
                    post['in_reply_to_status_id']:
                in_reply_to_id = post['in_reply_to_status_id']
                in_reply_to_user = post['in_reply_to_screen_name']

            fav = False
            if 'favorited' in post:
                fav = post['favorited']

            source = None
            if 'source' in post:
                source = post['source']

            status = Status()
            status.id_ = str(post['id'])
            status.username = username
            status.avatar = avatar
            status.text = post['text']
            status.in_reply_to_id = in_reply_to_id
            status.in_reply_to_user = in_reply_to_user
            status.is_favorite = fav
            status.is_protected = protected
            status.is_verified = False
            status.reposted_by = reposted_by
            status.datetime = self.get_str_time(post['created_at'])
            status.timestamp = self.get_int_time(post['created_at'])
            status.entities = self.get_entities(post)
            status.type_ = type_
            status.account_id = self.account_id
            status.is_own = (username.lower() == self.uname.lower())
            status.set_display_id(column_id)
            status.get_source(source)
            return status

    def get_entities(self, status):
        entities = Protocol.get_entities(self, status)
        for item in self.GROUP_PATTERN.findall(status['text']):
            url = "%s/%s" % (self.groups_url, item[1:])
            entities['groups'].append(Entity(self.account_id, url, item, item))
        return entities class AccountNotAuthenticated(Exception):
    pass class ErrorLoadingAccount(Exception):
    def __init__(self, message):
        self.message = message

    def __str__(self):
        return repr(self.message) class DummyFileHandler:
    def __init__(self, array=None):
        if array:
            self.array = array
        else:
            self.array = []
    def __iter__(self):
        return iter(self.array)
    def close(self):
        pass
    def write(self, argument):
        pass class DummyAccount:
    def __init__(self, arg1=None, arg2=None):
        self.id_ = arg1
        self.config = DummyConfig()
        self.protocol = None
    def setup_user_credentials(self, arg1, arg2, arg3):
        pass
    def fetch(self):
        pass
    def get_profile_image(self, arg):
        return "http://dummy.url"
    def verify_credentials_provider(self):
        return 'http://provider.com'
    @staticmethod
    def new(arg1, arg2):
        return DummyAccount(arg1, arg2) class DummyProtocol:
    def __init__(self):
        self.profile = Profile()
        self.friends = []
        self.http = DummyHttp()
    def request_token(self):
        return 'token'
    def authorize_token(self, pin):
        return self.profile
    def verify_credentials(self):
        return self.profile
    def get_lists(self, arg):
        return []
    def get_following(self, only_id):
        return self.friends
    def update_profile(self, fullname, url, bio, location):
        self.profile.fullname = fullname
        self.profile.url = url
        self.profile.bio = bio
        self.profile.location = location
        return self.profile class DummyConfig:
    def __init__(self, arg=None):
        self.cache_size = 0
        self.imgdir = "/path/to/ble"
    @staticmethod
    def exists(value):
        return False
    def save_oauth_credentials(self, arg1, arg2):
        pass
    def load_oauth_credentials(self):
        pass
    def dismiss(self):
        pass
    def delete_cache(self):
        pass
    def calculate_cache_size(self):
        return self.cache_size class DummyToken:
    def __init__(self):
        self.key = '123'
        self.secret = '456' class DummyProfile:
    def is_me(self):
        return True
import os
import pytest
import __builtin__
from libturpial.config import AccountConfig
from libturpial.api.models.list import List
from libturpial.api.models.column import Column
from libturpial.api.models.profile import Profile
from libturpial.api.models.account import Account
from libturpial.lib.protocols.twitter.twitter import Main as MainTwitter
from libturpial.lib.protocols.identica.identica import Main as MainIdentica
from libturpial.exceptions import AccountNotAuthenticated, ErrorLoadingAccount
from tests.helpers import DummyFileHandler, DummyAccount, DummyProtocol, DummyConfig,\
        DummyToken, DummyProfile
            Account.load('foo-twitter')

        # Monkeypatching Account.exists
        monkeypatch.setattr(os.path, 'isfile', lambda x: True)

        # Monkeypatching AppConfig create
        monkeypatch.setattr(os, 'makedirs', lambda x: None)
        monkeypatch.setattr(__builtin__, 'open', lambda x, y: DummyFileHandler())
        monkeypatch.setattr(AccountConfig, 'create', lambda: None)
        monkeypatch.setattr(AccountConfig, 'load_oauth_credentials', lambda x: ('123', '456'))

        monkeypatch.setattr('libturpial.api.models.account.Account', DummyAccount)
        Account.load('foo-twitter')

    def test_request_oauth_access(self, monkeypatch):
        monkeypatch.setattr(self.account, 'protocol', DummyProtocol())

        assert self.account.request_oauth_access() == 'token'

    def test_authorize_oauth_access(self, monkeypatch):
        protocol = DummyProtocol()
        protocol.profile.username = 'foo'
        monkeypatch.setattr(self.account, 'protocol', protocol)

        self.account.authorize_oauth_access('123')
        assert self.account.profile.username == 'foo'

    def test_save(self, monkeypatch):
        monkeypatch.setattr(self.account, 'is_authenticated', lambda: False)

        with pytest.raises(AccountNotAuthenticated):
            self.account.save()

        # Monkeypatching AppConfig create
        monkeypatch.setattr(os, 'makedirs', lambda x: None)
        monkeypatch.setattr(AccountConfig, 'create', lambda x: None)
        monkeypatch.setattr(AccountConfig, 'load', lambda x: None)
        monkeypatch.setattr(AccountConfig, 'load_failsafe', lambda x: None)
        monkeypatch.setattr(AccountConfig, 'exists', lambda x, y: True)
        monkeypatch.setattr(AccountConfig, 'save_oauth_credentials', lambda x, y, z: None)

        monkeypatch.setattr(self.account, 'is_authenticated', lambda: True)
        monkeypatch.setattr(self.account, 'get_oauth_token', lambda: DummyToken())

        # TODO: How to test that this works?
        assert self.account.save() == None

    def test_fetch(self, monkeypatch):
        monkeypatch.setattr(self.account, 'protocol', DummyProtocol())

        self.account.fetch()
        assert len(self.account.columns) == 5
        for col in self.account.columns:
            assert isinstance(col, Column)

    def test_fetch_friends(self, monkeypatch):
        protocol = DummyProtocol()
        protocol.profile.username = 'foo'
        protocol.friends = ['bar', 'baz']
        monkeypatch.setattr(self.account, 'protocol', protocol)

        friends = self.account.fetch_friends()
        assert friends == ['bar', 'baz']

    def test_get_column(self, monkeypatch):
        monkeypatch.setattr(self.account, 'columns', [])
        assert self.account.get_columns() == []

    def test_get_list_id(self, monkeypatch):
        monkeypatch.setattr(self.account, 'lists', None)

        assert self.account.get_list_id('bla') == None

        l1 = List()
        l1.id_ = '123'
        l1.slug = 'foolist'
        lists = [l1]
        monkeypatch.setattr(self.account, 'lists', lists)

        assert self.account.get_list_id('foolist') == '123'
        assert self.account.get_list_id('ble') == None

    def test_purge_config(self, monkeypatch):
        monkeypatch.setattr(self.account, 'config', DummyConfig('bla'))

        # TODO: How to test that this works?
        assert self.account.purge_config() == None

    def test_delete_cache(self, monkeypatch):
        monkeypatch.setattr(self.account, 'config', DummyConfig('bla'))

        # TODO: How to test that this works?
        assert self.account.delete_cache() == None

    def test_get_cache_size(self, monkeypatch):
        config = DummyConfig('bla')
        config.cache_size = 1000

        monkeypatch.setattr(self.account, 'config', config)

        assert self.account.get_cache_size() == 1000

    def test_is_authenticated(self, monkeypatch):
        monkeypatch.setattr(self.account, 'profile', None)
        monkeypatch.setattr(self.account, 'id_', None)

        assert self.account.is_authenticated() == False

        monkeypatch.setattr(self.account, 'id_', '123')
        assert self.account.is_authenticated() == False

        profile = Profile()
        monkeypatch.setattr(self.account, 'profile', profile)
        monkeypatch.setattr(self.account, 'id_', None)
        assert self.account.is_authenticated() == False

        monkeypatch.setattr(self.account, 'id_', '123')
        assert self.account.is_authenticated() == True

    def test_update_profile(self, monkeypatch):

        monkeypatch.setattr(self.account, 'protocol', DummyProtocol())