"""
  Copyright (c) 2006 Jan-Klaas Kollhof

  This file is part of jsonrpc.

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

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

  You should have received a copy of the GNU Lesser General Public License
  along with this software; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
"""

from threading import  Event,  Lock

from errors import *

try:
    from simplejson import JSONDecoder, JSONEncoder
except ImportError:
    from json import JSONDecoder, JSONEncoder
    
    
class JSONRPCEncoder(JSONEncoder):
    def default(self, obj):
        if isinstance(obj, JSONRPCError):
            return obj.__class__.__name__
        else:
            return JSONEncoder.default(self, obj)


class Timeout(Exception):
    pass
    
class ResponseEvent:
    """Event which is fired when the response is returned for a request.
    
        For each request sent this event is created. 
        An application can wait for the event to create a blocking request.
    """
    def __init__(self):
        self.__evt = Event()
        
    def waiting(self):
        return not self.__evt.isSet()
        
    def waitForResponse(self, timeOut=None):
        """blocks until the response arrived or timeout is reached."""
        self.__evt.wait(timeOut)
        if self.waiting():
            raise Timeout()
        else:
            if self.response["error"]:
                raise Exception(self.response["error"])
            else:
                return self.response["result"]
        
    def handleResponse(self, resp):
        self.response = resp
        self.__evt.set()
        
        
class SimpleMessageHandler:
    def __init__(self,  DecoderClass=JSONDecoder, EncoderClass=JSONRPCEncoder, messageDelimiter=""):
        self.decoder = DecoderClass()
        self.encoder = EncoderClass()
        self.partialData = ""
        self.respEvents={}
        self.respLock = Lock()
        self.messageDelimiter=messageDelimiter
        
    def close(self):
        pass
        
    def send(self, data):
        pass
        
    def sendMessage(self, msg):
        self.send(self.encoder.encode(msg) + self.messageDelimiter)
    
    def handlePartialData(self, data):
        data = self.partialData + data.replace("\r","").replace("\n", "")
        msgs=[]
        while data != "":
            pos = data.find("{")
            if(pos > -1):
                data=data[pos:]
                try:
                    (obj, pos) = self.decoder.raw_decode(data)
                    data = data[pos:]
                    msgs.append(obj)    
                except:
                    break
            else:
                break
        self.partialData = data
        
        self.handleMessages(msgs)

    def sendNotify(self, name, args):
        """sends a notification object to the peer"""
        self.sendMessage({"method":name, "params": args})
        
    def sendRequest(self, name, args):
        """sends a request to the peer"""
        (respEvt, id) = self.newResponseEvent()
        self.sendMessage({"id":id, "method":name, "params": args})
        return respEvt
        
    def sendResponse(self, id, result, error):
        """sends a response to the peer"""
        self.sendMessage({"result":result, "error": error, "id":id})
    
    def newResponseEvent(self):
        """creates a response event and adds it to a waiting list
           When the reponse arrives it will be removed from the list. 
        """
        respEvt = ResponseEvent()
        self.respLock.acquire()
        eid = id(respEvt)
        self.respEvents[eid] = respEvt
        self.respLock.release()
        return (respEvt,eid)
        
    def handleMessages(self, msgs):
        for msg in msgs:
            if msg.has_key("method") and msg.has_key("params"):
                if msg.has_key("id"):
                    if msg["id"]:
                        self.handleRequest(msg)    
                    else:
                        self.handleNotification(msg)
                else:
                    self.handleNotification(msg)
            elif msg.has_key("result") and msg.has_key("error"):
                self.handleResponse(msg)
            else:#unknown object 
                self.sendResponse(None, InvalidJSONMessage())
                self.close()
                
    def handleResponse(self, resp):
        """handles a response by fireing the response event for the response coming in"""
        id=resp["id"]
        evt = self.respEvents[id]
        del(self.respEvents[id])
        evt.handleResponse(resp)
    
    def handleRequest(self, request):
        pass
    def handleNotification(self, notification):
        pass
        
        
import re
NameAllowedRegExp=re.compile("^[a-zA-Z]\w*$")
def nameAllowed(name):
    """checks if a name is allowed.
    """
    if NameAllowedRegExp.match(name):
        return 1
    else:
        return 0
    
def getMethodByName(obj, name):

    """searches for an object with the name given inside the object given.
       "obj.child.meth" will return the meth obj.
    """
    try:#to get a method by asking the service
        obj = obj._getMethodByName(name)
    except:
        #assumed a childObject is ment 
        #split the name from objName.childObjName... -> [objName, childObjName, ...]
        #and get all objects up to the last in list with name checking from the service object
        names = name.split(".")
        for name in names:
            if nameAllowed(name):
                obj = getattr(obj, name)
            else:
                raise MethodNameNotAllowed()
        
    return obj  
    


class SimpleServiceHandler(SimpleMessageHandler):
    def __init__(self, service, DecoderClass=JSONDecoder, EncoderClass=JSONRPCEncoder, messageDelimiter=""):
        self.service = service
        SimpleMessageHandler.__init__(self, DecoderClass, EncoderClass, messageDelimiter)
        try:
            service._newConnection(self)
        except:
            pass
        
    def close(self):
        try:
            self.service._closingConnection(self)
        except:
            pass
        
    def handleRequest(self, req):
        """handles a request by calling the appropriete method the service exposes"""
        name = req["method"]
        params = req["params"]
        id=req["id"]
        obj=None
        try: #to get a callable obj 
            obj = getMethodByName(self.service, name)
        except MethodNameNotAllowed,e:
            self.sendResponse(id, None, e)
        except:
            self.sendResponse(id, None, MethodNotFound())
        if obj:
            try: #to call the object with parameters
                rslt = obj(*params)
                self.sendResponse(id, rslt, None)
            except TypeError: # wrong arguments
                #todo what if the TypeError was not thrown directly by the callable obj
                s=getTracebackStr()
                self.sendResponse(id, None, InvalidMethodParameters())
            except: #error inside the callable object
                s=getTracebackStr()
                self.sendResponse(id, None, s)
                
    def handleNotification(self, req):
        """handles a notification request by calling the appropriete method the service exposes"""
        name = req["method"]
        params = req["params"]
        try: #to get a callable obj 
            obj = getMethodByName(self.service, name)
            rslt = obj(*params)
        except:
            pass
                
    
    

