/**
 * @file
 * $Id$
 * $Revision$
 * $Author$
 * $Date$
 *
 * This file is part of The iWear Framework.
 *
 * The iWear Framework is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by the
 * Free Software Foundation as in version 2 of the License.

 * 
 * The iWear Framework 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 General Public License for
 * more details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * The iWear Framework; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef __IWREMOTE_REMOTE_OBJECT_H
#define __IWREMOTE_REMOTE_OBJECT_H

#include <iwear/threadlocked.h>
#include <iwremote/protocol.h>
#include <boost/intrusive_ptr.hpp>
#include <iwear/utility.h>
#include <iwremote/remoteconnection.h>
extern "C" {
#include <stdint.h>
}
#include <vector>
using std::vector;

namespace iwear
{
class uid;
    namespace net
    {

enum rpc_side
{
    rpc_client,
    rpc_server,
    num_rpc_side,
};

class RemoteCaller;
class CallManager;
class RPCStream;
class RemoteConnection;
class CallProvider;

class CallDescriptor /* {{{ */
{
private:
protected:
public:

    /**
     * The RemoteConenction is filled if this is a client object, the
     * RemoteCaller is filled if its a server side object.
     */
    union 
    {
	/**
	 * This is a descriptor for a connection to the server side object.
	 */
	RemoteConnection* rmcn;

	/**
	 * This is the RemoteCaller object of the Remote Host that called a
	 * function. It will be set before a function, and then Reset right
	 * after this function.
	 */
	RemoteCaller* rmcl;
    } rmt;


    /**
     * The class that provided us with the caller or connection.
     * We ask it for rpcstreams.
     * There are two major cases what kind ob object we are
     * - Client Object: In this case the callprovider has been set during the
     *   association and can just be used to transfer the data to the server
     * - Server Object: In this case, the CallProvider has been set together
     *   with the RemoteCaller for the actual object.
     *   
     *   TODO What do we do in case of recursion ? Check if its the same RemoteCaller ?
     * @note We should only need to worry about recursion in server side
     * objects, since the clients are not callable (but instead might call
     * other servers too). So maybe to allow local recursion we should not
     * directly let the object be called, but instead wrap it the same way it
     * would be on a server...
     *
     * XXX Another problem with recursion is, that we often cannot handle
     * incoming data as long as this call is waiting for completion. A
     * workaround could be the thread scheduling on the server side, but as it
     * will be then client-side too, the other will arrive in another thread,
     * which will be stuck in the mutex.
     *
     * Another idea is to check the caller, if its the same host/hostid and let
     * it go through without the mutex. But it could then get stuck on the same
     * internal mutexes on other objects structures. plus if the remote system
     * fools us or is calloing from different threads our functions, it could
     * come in that two functions are running without beeing locked.
     * This could be a potential security risk.
     *
     * So the final decision as of 09/13 is to disallow recursion into objects
     * alltogether and try to detect it and send an error message back.
     */
    CallProvider* cprov;

    chanid_t channel;

    rpc_side myside;

    /**
     * This is true when the object should be associated for calls. When its
     * false, then calls to that object are made without prior association.
     */
    bool associating;
}; /* }}} */

struct oneway_return 
{ 
    // Tag to throw
};

class RemoteObject : virtual public ThreadLocked
{
private:
protected:
friend class RootArtano;
friend class CallManager;
friend class CallProvider;

    virtual void set_uid( const uid& ) { }

    CallDescriptor cd;

    CallManager* cman;

    uint32_t referrer;


    CallProvider* get_callprovider( void ) { return cd.cprov; }

    /**
     * This is only validly filled if we are a client object. As a Server
     * Object, we could server different remoteconnections, and thus different
     * channels.
     */
    chanid_t channel_id;

    float call_timeout;
    /**
     * Get the minimum size of a buffer needed for this kind of call
     */
    virtual size_t get_callminsize( uint32_t callid ) = 0;

    virtual size_t get_retminsize( uint32_t callid ) = 0;

    /**
     * Get the minimum size needed for this object (If an object uses the
     * additional dynamic object allocation functions, it needs to take those
     * size into account)
     */
    virtual size_t get_minsize( void ) = 0;
    
    /**
     * This function should be implemented by the actual object ( it is
     * autogenerated there )
     * It will lookup which function to call, and call it, returning the return
     * stream (if any). This will include the versioning parameter.
     */
    virtual RPCStream* _resolve_call( uint32_t callid, uint32_t verid, RPCStream* ) = 0;

    /**
     * @todo TODO build a more sophisticated visibility and callability cheking.
     */
    bool is_visible();
public:

    hostid_t get_calling_host( void );

    /**
     * Do the association (if not already done). Makes much sense for those who
     * have dererred association and want something.
     */
    void do_association( void );

    CallDescriptor& get_calldescriptor( void ) { return cd; }
    CallManager* get_callmanager( void ) { return cman; }
    /**
     * An dieser stelle haben wir das objekt bereits lokalisiert. Auf diesem
     * Objekt wird nun der aufruf gestartet, indem der datenstrom geparsed
     * wird. Vorher wurden RemoteCaller gesetzt und das objekt gelockt.
     * (das sollten wir jetzt in do_call machen. das objekt selbst wird eine
     * _resolve_call() methode implementieren...
     *
     * Jedes Objekt hat natürlich seine eigene art die daten zu parsen und die
     * funktion festzustellen, daher hier pv.
     *
     * Die Position im strom is an der stelle wo die crcid liegt (danach kommt
     * dann die version_id)
     *
     * TODO können wir das hier intern im objekt lösen, also das "externe"
     * locken ? Wie ist das mit der rekursivität ? wenn wir ein andres objekt
     * aufrufen das uns wiederum aufruft ?
     * [Ja, non-virtual, wir wollen nicht dass uns jemand das überschreibt und
     * was kaput macht]
     */
    RPCStream* do_call( const hostid_t hid, RPCStream* rpcs, RemoteConnectionPtr rcon,
	    chanid_t chan, uint32_t func, uint32_t fver );


    /**
     * This function will fill the stream with the object from the copy (e.g.
     * function parameter, or associated object) to the originating original
     * object. This means that "readonly" fields dont need to be
     * (re-)transmitted, since they wont be written to the object.
     */
PH2 virtual void fill_stream_to_owner( RPCStream* rpcs ) const = 0;
    
    /**
     * This fills a stream from the original object to a copied object.
     * This means that the server has to fill in all the data thats really
     * needed to (re-)assemble the object properly.
     */
PH2 virtual void fill_stream_to_copy( RPCStream* rpcs ) const = 0;

    /**
     * Fills the object from a clientstream (previously filled with
     * fill_stream_client()) and writes only the values that are not set to
     * readonly.
     */
PH2 virtual void fill_from_copy( RPCStream* rpcs ) = 0;

    /**
     * On Client side, fill this object from a serverstream so that all fields
     * are set.
     */
PH2 virtual void fill_from_owner( RPCStream* rpcs ) = 0;

    /**
     * This needs to be called from every class somehow deriving from
     * RemoteObject since from this class only virtual derivation is possible.
     */
PH2 RemoteObject( CallManager* );

    virtual ~RemoteObject( void );
    /**
     * This is the uid the server/client recognized the object from.
     */
PH2 virtual const uid& get_uid( void ) const = 0;

    virtual const string& get_name( void ) const = 0;

    /**
     * Every automatically generated object will have this implemented.
     */
    virtual RemoteObject* clone( void ) const = 0;

    /**
     * This will return all the class_ids we are supposed to implement.
     */
    virtual vector<cid_t> get_cid( void ) = 0;

    /**
     * returns true if this remote object somewhere mplements the give cid
     */
    virtual bool implements( cid_t ) = 0;

    /**
     * This is highly experimental, as it should return a proper class
     * implementing
     */
    static RemoteObject* instance( uint32_t cid );

    /**
     * Such a function should be implemented by each class, to pass it to the
     * ClassProvider
     */
    static RemoteObject* instance( void );

    /**
     * This resolves a given callid number to a string, denoting a uniq string
     * which might be needed in case the call is done via some webservices xml
     * plugin etc.
     */
    virtual const string& call_to_string( cid_t callid ) = 0;
    
    /**
     * Ask the object if calls can be done without prior association to it. If
     * yes, calls via UDP can made to this object. If no, the call is rejected
     * saying the object is not there (so it cannot be distinguished if its
     * really not there)
     */
PH2 virtual bool need_association( void ) { return false; }

PH2 bool operator<( const RemoteObject& ro ) const { return (this < &ro); }    

    virtual void about_to_call( uint32_t funcid, uint32_t fver, const hostid_t& caller );

    virtual bool may_associate( const hostid_t& caller );
};

}
}
#endif
