# Copyright 2010-2011 Florent Le Coz <louiz@louiz.org>
#
# This file is part of Poezio.
#
# Poezio is free software: you can redistribute it and/or modify
# it under the terms of the zlib license. See the COPYING file.


"""
Defines the Roster and RosterGroup classes
"""
import logging
log = logging.getLogger(__name__)

from config import config
from os import path as p
from contact import Contact
from sleekxmpp.xmlstream.stanzabase import JID

class Roster(object):
    def __init__(self):
        self._contact_filter = None # A tuple(function, *args)
                                    # function to filter contacts,
                                    # on search, for example
        self._contacts = {}     # key = bare jid; value = Contact()
        self._roster_groups = []

    def export(self, path):
        if p.isfile(path):
            return
        try:
            f = open(path, 'w+')
            f.writelines([i + "\n" for i in self._contacts])
            f.close()
            return True
        except IOError:
            return

    def empty(self):
        self._contacts = {}
        self._roster_groups = []

    def add_contact(self, contact, jid):
        """
        Add a contact to the contact list
        """
        self._contacts[jid] = contact

    def remove_contact(self, jid):
        """
        Remove a contact from the contact list
        """
        contact = self.get_contact_by_jid(jid)
        for group in contact._groups:
            self.remove_contact_from_group(group, contact)
        del self._contacts[jid]

    def get_contact_len(self):
        """
        Return the number of contacts in this group
        """
        return len(self._contacts.keys())

    def get_contact_by_jid(self, jid):
        """
        Returns the contact with the given bare JID
        """
        # Use only the bare jid
        jid = JID(jid)
        if jid.bare in self._contacts:
            return self._contacts[jid.bare]
        return None

    def edit_groups_of_contact(self, contact, groups):
        """
        Edit the groups the contact is in
        Add or remove RosterGroup if needed
        """
        # add the contact to each group he is in
        # If the contact hasn't any group, we put her in
        # the virtual default 'none' group
        if not len(groups):
            groups = ['none']
        for group in groups:
            if group not in contact._groups:
                # create the group if it doesn't exist yet
                contact._groups.append(group)
            self.add_contact_to_group(group, contact)
        # remove the contact from each group he is not in
        for group in contact._groups:
            if group not in groups:
                # the contact is not in the group anymore
                contact._groups.remove(group)
                self.remove_contact_from_group(group, contact)

    def remove_contact_from_group(self, group_name, contact):
        """
        Remove the contact from the group.
        Delete the group if this makes it empty
        """
        for group in self._roster_groups:
            if group.name == group_name:
                group.remove_contact(contact)
                if group.is_empty():
                    self._roster_groups.remove(group)
                return

    def add_contact_to_group(self, group_name, contact):
        """
        Add the contact to the group.
        Create the group if it doesn't already exist
        """
        for group in self._roster_groups:
            if group.name == group_name:
                if not group.has_contact(contact):
                    group.add_contact(contact)
                return
        folded_groups = config.get('folded_roster_groups', '', section='var').split(':')
        new_group = RosterGroup(group_name, folded=group_name in folded_groups)
        self._roster_groups.append(new_group)
        new_group.add_contact(contact)

    def get_groups(self):
        """
        Returns the list of groups
        """
        return self._roster_groups

    def get_contacts(self):
        """
        Return a list of all the contact
        """
        return [contact for contact in self._contacts.values()]

    def save_to_config_file(self):
        """
        Save various information to the config file
        e.g. the folded groups
        """
        folded_groups = ':'.join([group.name for group in self._roster_groups\
                                      if group.folded])
        log.debug('folded:%s\n' %folded_groups)
        config.set_and_save('folded_roster_groups', folded_groups, 'var')

    def get_nb_connected_contacts(self):
        """
        Return the number of contacts connected
        """
        length = 0
        for group in self._roster_groups:
            length += group.get_nb_connected_contacts()
        return length

    def __len__(self):
        """
        Return the number of line that would be printed
        for the whole roster
        """
        length = 0
        show_offline = config.get('roster_show_offline', 'false') == 'true'
        for group in self._roster_groups:
            if not show_offline and group.get_nb_connected_contacts() == 0:
                continue
            length += 1              # One for the group's line itself
            if not group.folded:
                for contact in group.get_contacts(self._contact_filter):
                    # We do not count the offline contacts (depending on config)
                    if not show_offline and\
                            contact.get_nb_resources() == 0:
                        continue
                    length += 1      # One for the contact's line
                    if not contact._folded:
                        # One for each resource, if the contact is unfolded
                        length += contact.get_nb_resources()
        return length

    def __repr__(self):
        ret = '== Roster:\nContacts:\n'
        for contact in self._contacts:
            ret += '%s\n' % (contact,)
        ret += 'Groups\n'
        for group in self._roster_groups:
            ret += '%s\n' % (group,)
        return ret + '\n'

PRESENCE_PRIORITY = {'unavailable': 0,
                     'xa': 1,
                     'away': 2,
                     'dnd': 3,
                     '': 4,
                     'available': 4}

class RosterGroup(object):
    """
    A RosterGroup is a group containing contacts
    It can be Friends/Family etc, but also can be
    Online/Offline or whatever
    """
    def __init__(self, name, folded=False):
        self._contacts = []
        self.name = name
        self.folded = folded    # if the group content is to be shown

    def is_empty(self):
        return len(self._contacts) == 0

    def has_contact(self, contact):
        """
        Return a bool, telling if the contact
        is already in the group
        """
        if contact in self._contacts:
            return True
        return False

    def remove_contact(self, contact):
        """
        Remove a Contact object from the list
        """
        try:
            self._contacts.remove(contact)
        except ValueError:
            pass

    def add_contact(self, contact):
        """
        append a Contact object to the list
        """
        assert isinstance(contact, Contact)
        assert contact not in self._contacts
        self._contacts.append(contact)

    def get_contacts(self, contact_filter):
        def compare_contact(a):
            if not a.get_highest_priority_resource():
                return 0
            show = a.get_highest_priority_resource()
            if show not in PRESENCE_PRIORITY:
                return 5
            return PRESENCE_PRIORITY[show]
        contact_list = self._contacts if not contact_filter\
            else [contact for contact in self._contacts if contact_filter[0](contact, contact_filter[1])]
        return sorted(contact_list, key=compare_contact, reverse=True)

    def toggle_folded(self):
        self.folded = not self.folded

    def __repr__(self):
        return '<Roster_group: %s; %s>' % (self.name, self._contacts)

    def __len__(self):
        return len(self._contacts)

    def get_nb_connected_contacts(self):
        l = 0
        for contact in self._contacts:
            if contact.get_highest_priority_resource():
                l += 1
        return l

roster = Roster()