#!/usr/bin/python
# -*- coding: utf-8 -*-
# Hah, don't you love the pervasiveness of Emacs?

"""
This module is the Jabber/XMPP server the user's client connects to.
"""

import SocketServer, sys, string,thread, random, re, globalv, net


import pdb


from xml.sax import make_parser
from handler import MurrayHandler
from xmlcreator import MurrayXMLCreator
from buddies import Contacts, Chats, Logic

PORT = 5222
ID = None
__socket_lock = thread.allocate_lock()

class MurrayXMPPServer(SocketServer.StreamRequestHandler):
    """A derivative of the StreamRequestHandler overwriting its
    handle() method to administer an XMPP client."""

    __sessionStartedP = False
    __inMidOfRegistration = True

    __registrationSuccessful = False
    __authenticationSuccessful = False
    __username = ""
    __password = ""

    def __init__(self, request, client_address, server, port=5222):
       """If handed a port use it instead."""
       self.setPort(port)
       self.setConnectionID()
       self.setSessionID()
       self.murrayhandler = MurrayHandler(self)
       self.murrayxmlcreator = MurrayXMLCreator(self)
       self.contacts = Contacts()
       self.chats = Chats()
       self.logic = Logic()
       SocketServer.StreamRequestHandler.__init__(self, request, client_address, server)

    def getUsername(self):
        """FIXME()"""
        return self.__username

    def setUsername(self, username):
        """FIXME()"""
        self.__username = username

    def getPassword(self):
        """FIXME()"""
        return self.__password

    def setPassword(self, password):
        """FIXME()"""
        self.__password = password

    def setConnectionID(self):
        """FIXME()"""
        self.connectionID = "%X" % random.getrandbits(32)

    def getConnectionID(self):
        """FIXME()"""
        return self.connectionID

    def setSessionID(self):
        """FIXME()"""
        self.sessionID = "%X" % random.getrandbits(32)

    def getSessionID(self):
        """FIXME()"""
        return self.sessionID

    def closeSession(self):
        """FIXME()"""
        murrayxmlcreator.destroyStream()

    def startSession(self):
        """Start the Session.  Anything before this will
        FIXME(eventually) be queued and sent as soon as the session
        started."""
        self.__sessionStartedP = True

    def endSession(self):
        """End the Session.  See startSession() for details."""
        self.__sessionStartedP = False

    def getSession(self):
        """Returns True if the session has started.  See
        startSession() for details."""
        return self.__sessionStartedP

    def sendRegistrationRequirements(self, id):
        """Ask the XML creator to send the registration requirements."""
        self.__inMidOfRegistration = True
        self.requestSend(self.murrayxmlcreator.registrationRequirements(id))

    def sendRegistrationResult(self, id, username=None, password=None):
        self.__inMidOfRegistration = False
        self.__registrationSuccessful = True

        self.setUsername(username)
        self.setPassword(password)

        # username has to start with a letter, and then an arbitrary
        # combination of letters and digits is allowed.  Don't know if
        # that's a XMPP requirement, but it certainly is required by
        # me...
        usernameRE = re.compile(r'[A-Za-z][A-Za-z0-9]*')

        # The "is None" parts should not happen, but nevertheless...
        if not (username == "" or password == "" \
               or username is None or password is None) \
               or not usernameRE.match(username):
            self.requestSend(
                self.murrayxmlcreator.registrationResults(id, username, password)
                )
            globalv.ALIAS = username
            globalv.PASS = password

        else:
            self.requestSend(
                self.murrayxmlcreator.registrationError(id, username, password)
                )
            self.__registrationSuccessful = False
            self.setUsername("")
            self.setPassword("")

    def sendAuthenticationInfo(self, id, username=None):
        """Send authentication information."""
        self.setUsername(username)

        self.requestSend(
            self.murrayxmlcreator.authenticationInfo(id, self.getUsername())
            )

    def sendAuthenticationResult(self, id, username, password):
        """FIXME()"""

        if self.getUsername() == username and self.getPassword() == password:
            self.requestSend(
                self.murrayxmlcreator.authenticationResult(True, id, username, password)
                )
            self.__authenticationSuccessful = True
        else:
            # FIXME(eigene methode!)
            self.requestSend(
                self.murrayxmlcreator.authenticationResult(False, id, username,password)
                )

    def setPort(self, port):
        """Check if port is in the valid range and if so set PORT."""
        if 0 < int(port) < 65536:
            self.PORT = port
            return True
        else:
            return False

    def requestSend(self, string):
        """Makes sure a message gets sent because a socket send"""
        __socket_lock.acquire()
        lenString = len(string)
        lenSent = self.request.send(string)

        while lenSent < lenString:
            string = string[lenSent:]
            lenSent = self.request.send(string)
            lenString = len(string)
        __socket_lock.release()

    def sendRoster(self):
        """Sends the roster"""
        self.requestSend(
            self.murrayxmlcreator.roster(id, self.contacts.getAll())
            )

    def handle(self):
        """FIXME(comment!  Uses the rfile/wfile instead of the request
        objects to ease everything up a bit.)"""
        print "Connection from", self.client_address[0], ", port", self.client_address[1]
        print "Initiating session..."

        saxparser = make_parser()
        saxparser.setContentHandler(self.murrayhandler)

        while True:
            # FIXME(exceptions like KeyboardInterrupt have to be caught on
            # some level)
            recvDat = self.request.recv(8192)
            if not recvDat: break
            saxparser.feed(recvDat)

        saxparser.close()
        print "Closing session..."

    def askUserBoolean(self, text):
        print "Ask the user (yes/no) the following:", text
        return True

    def askUser(self, text, answers):
        print "Ask the user the following:", text
        return answers[0]

    def informUser(self, text):
        print "Inform the user about the following:", text

    def sendRoster(self, id):
        """Sends the roster"""
        self.requestSend(
            self.murrayxmlcreator.Roster(id)
            )

    def sendPresence(self, id, type, to, show, status, priority):
        """Send a <presence/> to someone or everyone."""
        if not to is None and not to == "": # to someone
            mid = to[0]
            net.sendXMPPMessage(
                mid,
                self.xmlcreator.presence(id, type, self.midToJid(to[0], to[1], False),
                                         self.midToJid(globalv.MURRAY_ID,
                                                       globalv.ALIAS,
                                                       False),
                                         show, status, 
                                        priority)
                )
            pass

        else: # to everyone
            net.sendXMPPMessage(
                self.contacts.getAll().keys(),
                self.xmlcreator.presence(id, type, self.midToJid(to[0], to[1], False),
                                         self.midToJid(globalv.MURRAY_ID,
                                                       globalv.ALIAS,
                                                       False),
                                         show, status, 
                                        priority)
                )
    def sendMessage(self, id, type, to, body, subject, thread, error):
        """Send a <message/> to someone."""

        if not to is None and not to == "": # to someone
            net.sendXMPPMessage(
                self.contacts.getAll().keys(),
                self.xmlcreator.message(id, type, None,
                                        self.midToJid(crypto.murrayid(),
                                                      self.__username,
                                                      False),
                                        body, subject, thread, error)
                )
            pass

        else: # bounce back
            pass

    def midToJid(self, hashvalue, alias, resourcep):
        """Converts a Murray ID (hash, alias) to a Jabber ID.  If
        resourcep then append Murray as a resource."""

        jid = alias + "@" + hashvalue + ".murray.p2p"
        if resourcep: jid += "/Murray"
        
        return jid

    def jidToMid(self, jid):
        """Converts a Jabber ID (alias@hash.murray.p2p/resource) to a
        Murray ID."""

        jidRE = re.compile(r'@([^.]*)')
        alias, hashvalue, throwaway = jidRE.split(jid)
        


# This is what a caller has to do: create a TCPServer object with this
# class derived from StreamRequestHandler.
if __name__=='__main__':
    server = None
    try:
        # Create an instance of our server class.  The empty hostname is
        # equivalent to localhost.
        print "Creating TCP server..."
        server = SocketServer.TCPServer(('', PORT), MurrayXMPPServer)

        # Enter an infinite loop to serve.
        server.serve_forever()

    finally:
        # FIXME(error handling here?  no...)
        print "Problem creating server."

    server.server_close()
    sys.exit(0)
