# -*- coding: utf-8 -*-
import os, cPickle, random, thread, sys, hashlib, globalv, xmlcreator, service
import net

class Contacts:
    """Manages the user's own contact list"""

    __clist = {}                            #the contact list: Murray_ID->Alias
    __clistLock = thread.allocate_lock()    #lock for threading
    __online = {}                            #online-status: Murray_ID->(Status, Message)
    __onlineLock = thread.allocate_lock()    #lock for threading
    # Explanation: Status 0 means "offline"

    def add(self, murray_id, alias):
        """adds a new buddy to the contact list.

        returns nothing"""
        self.__clistLock.acquire()
        self.__onlineLock.acquire()
        try:
            self.__clist[murray_id] = alias
            self.__online[murray_id] = (0, '')
        finally:
            self.__clistLock.release()
            self.__onlineLock.release()

    def remove(self, murray_id = None, alias = None):
        """removes a buddy from the list

        returns nothing"""
        #if the murray-id is not given, get it via the alias
        murray_id = self.getMurrayID(alias) if not murray_id else murray_id
        self.__clistLock.acquire()
        self.__onlineLock.acquire()
        try:
            del self.__clist[murray_id]
            del self.__online[murray_id]
        finally:
            self.__clistLock.release()
            self.__onlineLock.release()

    def getAlias(self, murray_id):
        """gets the alias for a given murray_id."""
        self.__clistLock.acquire()
        try:
            alias = self.__clist[murray_id]
        finally:
            self.__clistLock.release()
        return alias

    def getOnlineStatus(self, murray_id = None, alias = None):
        """GUESS"""
        #if murray-id is not given, get it from the alias
        murray_id = self.getMurrayID(alias) if not murray_id else murray_id
        self.__onlineLock.acquire()
        try:
            status = self.__online[murray_id]
        finally:
            self.__onlineLock.release()
        return status

    def getAll(self):
        """returns the full contact list"""
        self.__clistLock.acquire()
        cl = self.__clist
        self.__clistLock.release()
        return cl

    def setOnlineStatus(self, murray_id = None, alias = None, status = 0, message = ''):
        """changes the online status of a peer and his status message"""
        #get murray-id
        murray_id = self.getMurrayID(alias) if not murray_id else murray_id
        self.__onlineLock.acquire()
        try:
            self.__online[murray_id] = (status, message)
        finally:
            self.__onlineLock.release()
        
    def getMurrayID(self, alias):
        """gets the murray_id of a selected alias name"""
        workingDict = self.getAll()
        if alias not in wokingDict.values():
            return None
        else:
            for item in workingDict.iteritems():
                if alias == item[1]:
                    return item[0]

    def setMurrayID(self, murray_id, alias = '', old_murray_id = ''):
        """changes the Murray-ID of a given peer

        since a Murray ID is a unique identifier of a MURRAY-peer,
        this function should NEVER be used. EVER.

        returns nothing"""
        if alias:
            self.__clistLock.acquire()
            self.__clist[alias][0] = murray_id
            self.__clistLock.release()
        else:
            workingList = self.getAll()
            for item in workingList.iteritems():
                if item[1][0] == old_murray_id:
                    self.setMurrayID(murray_id, alias = item[0])
                    break

    def setAlias(self, old, new):
        """changes the alias of a peer

        returns nothing"""
        uid = self.getMurrayID(old)
        self.__clistLock.acquire()
        try:
            self.__clist[uid] = new
        finally:
            self.__clistLock.release()
    

    def store(self, filename = globalv.CLIST_FILE):
        """pickles the users contact list on shutdown"""

        try:
            f = file(os.path.join(globalv.MURRAY_HOME, filename), 'w')
            self.__clistLock.acquire()
            cPickle.dump(self.__clist, f)
        finally:
            self.__clistLock.release()
            f.close()
            
    def load(self, filename = globalv.CLIST_FILE):
        """restores the list after startup"""
        clistfile = os.path.join(globalv.MURRAY_HOME, filename)
        if os.path.exists(clistfile):
            f = file(clistfile, 'r')
            self.__clistLock.acquire()
            try:
                self.__clist = cPickle.load(f)
            finally:
                self.__clistLock.release()
                f.close()
        else:
            pass

class Chats:
    """keeps track of chats the user is involved in"""
    __chats = {}
    __chatsLock = thread.allocate_lock()

    def add(self, members, title, public=False, topic = None, id = None):
        """adds a new chat to the list
        takes the other members (usually one, as uid-alias-tuple), 
        title and - optionally a topic description and an id - if known.

        In case no ID is given, a new one is created and returned"""
        if not id:
            id = random.randint(0, 2**16)
            self.__chatsLock.acquire()
        try:
            self.__chats[id] = {'title' : title, 'topic' : topic, \
            'members' : list(members), 'public' : public }
        finally:
            self.__chatsLock.release()
        return id

    def get(self, id):
        """gets the properties of a chat"""
        self.__chatsLock.acquire()
        try:
            returnValue = self.__chats[id]
        finally:
            self.__chatsLock.release()
        return returnValue

    def newMember(self, id, members):
        """adds a new member to an existing chatroom

        returns nothing"""
        self.__chatsLock.acquire()
        try:
            self.__chats[id]['members'].extend(list(members))
        finally:
            self.__chatsLock.release()
            
    def changeTopic(self, id, topic):
        """changes the topic of the chat"""
        self.__chatsLock.acquire()
        try:
            self.__chats[id]['topic'] = topic
        finally:
            self.__chatsLock.release()

    def removeMember(self, id, member):
        """removes an existing member from a chat

        returns nothin"""
        self.__chatsLock.acquire()
        try:
            self.__chats[id]['members'].remove(member)
        finally:
            self.__chatsLock.release()
            
    def getMembers(self, id):
        """returns all members of a chat as a list of (uid, alias) tuples"""
        self.__chatsLock.acquire
        try:
            retVal = self.__chats['members']
        finally:
            self.__chatsLock.release()
        return retVal
            
    def closeChat(self, id):
        """remove a chat"""
        self.__chatsLock.acquire()
        try:
            del self.__chats[id]
        finally:
            self.__chatsLock.release()
    

class Logic:
    """Sending/Receiving-Logic"""
    __bl = Contacts()
    __cl = Chats()
    __waiting = []
    __waitingLock = thread.allocate_lock()
    __awaitingOnlineStatus = []
    __onlineLock = thread.allocate_lock()
    __tokenList = {}
    __tokenListLock = thread.allocate_lock()
    __creator = xmlcreator.MurrayXMLCreator(None)
    
    def __init__(self):
        self.__xmppserver = globalv.XMPPSERVER

    def addContact(self, murray_id, alias):
        """Add a contact (request its addition to the contact list)"""
        self.__waitingLock.acquire()
        try:
            self.__waiting.append(murray_id)
        finally:
            self.__waitingLock.release()
        request = __creator.presence(None, type='subscribe',\
         to=service.midToJid(murray_id, alias), \
        sender=midToJid(globalv.MURRAY_ID, globalv.ALIAS))
        sendXMPPMessage(list(murray_id), request)
        self.__bl.add(murray_id, alias)
        self.__xmppserver.sendRoster()
        
    def remove(self, murray_id):
        """removes a buddy from the contact list"""
        self.__bl.remove(murray_id)

    def receiveAuthorisation(self, murray_id, alias):
        """Finally add a contact to the buddy list"""
        self.__waitingLock.acquire()
        try:        
            if murray_id in self.__waiting:
                self.__waiting.remove(murray_id)
                self.__bl.add(murray_id, alias)
                self.sendOnlineStatus(murray_id)
        finally:
            self.__waitingLock.release()
            
    def sendOnlineStatus(self, uid, alias):
        """sends my own online status"""
        to = (uid, alias) if uid and alias else None        
        self.__onlineLock.acquire()
        try:
            self.__online.append(murray_id)
        finally:
            self.__onlineLock.release()
        self.__xmppserver.sendPresence(None, globalv.TYPE, to, globalv.SHOW,
        globalv.STATUS, None)

    def receiveOnlineStatus(self, uid, stat, mess):
        """in case an Online Status is received as an "answer", simply update
        online status. In Case it is the first request, respond with our own
        status"""
        self.__bl.setOnlineStatus(murray_id = uid,  \
        status = stat, message = message)
        response = True
        to = (uid, self.__bl.getAlias(uid))
        self.__onlineLock.acquire()
        try:
            workingList = self.__online
            if uid in workingList:
                self.__online.remove(uid)
                response = False
        finally:
            self.__onlineLock.release()    
        if response:
            xmppserver.sendPresence(None, globalv.TYPE, to, globalv.SHOW,
        globalv.STATUS, None)
        else:
            pass
            
    def startChat(self, members, title, topic = None, public = False):
        """start a new chat"""
        id = self.__cl.add(members=members, title=title, topic=topic, \
        public=public)
        for member in members:    
            self.invite(id, member)

    def joinChat(self, id, member, title, token=None):
        """join a chat started by another peer"""
        if token:
            self.__cl.add(id = id, members = list(member), title = title, 
        topic = None, public = False)
        ###            xmppserver.joinChat(member, id, token)
        else:
            self.__cl.add(id = id, members = list(member), title = title, 
        topic = None, public = True)
    ###            xmppserver.joinChat(member, id)
        
    def receiveJoinRequest(self, id, requester, token=None):
        """If a join request is received, check the user and add him
        to the chat"""
        try:
            chat = self.__cl.get(id)
            if chat['public']:
                self.__cl.newMember(id, requester)
                ###                xmppserver.sendinfo(requester, chat)
                for member in chat['members']:
                    pass
            ###                    xmppserver.sendjoininfo(member, id, requester)
            else:
                self.__tokenListLock.acquire()
                tl = self.__tokenList
                self.__tokenListLock.release()
                if token == self.__tokenList[id]:
                    self.__cl.newMember(id, requester)
            ###                    xmppserver.sendinfo(requester, chat)
                    for member in chat['members']:
                        pass
            ###                        xmppserver.sendjoininfo(member, id, requester)
                else:
                    pass
        except ValueError:
            pass
        finally:
            self.__tokenListLock.release()
            
    def invite(self, member, id):
        """invite a peer to a chat"""
        chat = self.__cl.get(id)
        if chat[public]:
            pass
        ###            xmppserver.invite(member, id, title)
        else:
            raw_token = str(random.getrandbits(64))
            token = hashlib.md5(token).hexdigest()
            self.__tokenListLock.acquire()
            self.__tokenList[id] = token
            self.__tokenListLock.release()
        ###            xmppserver.invite(member, id, title, token)

    def receiveJoinInfo(self, member, id):
        """insert new members to the chat"""
        self.__cl.newMember(id, member)
        
    def leaveChat(self, id):
        """leave a chat"""
        chat = self.__cl.get(id)
        self.__cl.closeChat(id)
        for member in chat['members']:
            pass
###            xmppserver.leave(member, id)

    def receiveLeave(self, member, id):
        """react on a information from another member that he leaves a chat"""
        self.__cl.removeMember(id, member)
        
        
def startup():
    print 'Retrieving contact list'
    buddylist = Contacts()
    buddylist.load()
    Logic().sendOnlineStatus(None, None)    

def shutdown():
    buddylist = Contacts()
    buddylist.store()
