#!/usr/bin/env python

# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.


from twisted.internet import reactor
from twisted.python import log
from twisted.spread import jelly, pb


class MyException(pb.Error):
    pass


class MyOtherException(pb.Error):
    pass


class ScaryObject:
    # not safe for serialization
    pass


def worksLike(obj):
    # the callback/errback sequence in class One works just like an
    # asynchronous version of the following:
    try:
        response = obj.callMethod(name, arg)
    except pb.DeadReferenceError:
        print(" stale reference: the client disconnected or crashed")
    except jelly.InsecureJelly:
        print(" InsecureJelly: you tried to send something unsafe to them")
    except (MyException, MyOtherException):
        print(" remote raised a MyException")  # or MyOtherException
    except BaseException:
        print(" something else happened")
    else:
        print(" method successful, response:", response)


class One:
    def worked(self, response):
        print(" method successful, response:", response)

    def check_InsecureJelly(self, failure):
        failure.trap(jelly.InsecureJelly)
        print(" InsecureJelly: you tried to send something unsafe to them")
        return None

    def check_MyException(self, failure):
        which = failure.trap(MyException, MyOtherException)
        if which == MyException:
            print(" remote raised a MyException")
        else:
            print(" remote raised a MyOtherException")
        return None

    def catch_everythingElse(self, failure):
        print(" something else happened")
        log.err(failure)
        return None

    def doCall(self, explanation, arg):
        print(explanation)
        try:
            deferred = self.remote.callRemote("fooMethod", arg)
            deferred.addCallback(self.worked)
            deferred.addErrback(self.check_InsecureJelly)
            deferred.addErrback(self.check_MyException)
            deferred.addErrback(self.catch_everythingElse)
        except pb.DeadReferenceError:
            print(" stale reference: the client disconnected or crashed")

    def callOne(self):
        self.doCall("callOne: call with safe object", "safe string")

    def callTwo(self):
        self.doCall("callTwo: call with dangerous object", ScaryObject())

    def callThree(self):
        self.doCall("callThree: call that raises remote exception", "panic!")

    def callShutdown(self):
        print("telling them to shut down")
        self.remote.callRemote("shutdown")

    def callFour(self):
        self.doCall("callFour: call on stale reference", "dummy")

    def got_obj(self, obj):
        self.remote = obj
        reactor.callLater(1, self.callOne)
        reactor.callLater(2, self.callTwo)
        reactor.callLater(3, self.callThree)
        reactor.callLater(4, self.callShutdown)
        reactor.callLater(5, self.callFour)
        reactor.callLater(6, reactor.stop)


factory = pb.PBClientFactory()
reactor.connectTCP("localhost", 8800, factory)
deferred = factory.getRootObject()
deferred.addCallback(One().got_obj)
reactor.run()
