/***************************************************************************
 *   Copyright (C) 2003 by Julian Rockey (linux@jrockey.com)               *
 *   Original code by Torben Weis                                          *
 *                                                                         *
 *   This program 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; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 ***************************************************************************/

#ifndef __pcop_h__
#define __pcop_h__

#include <Python.h>

#include <qcstring.h>
#include <qlist.h>
#include <qasciidict.h>

#include <dcopclient.h>
#include <dcopobject.h>

class QDataStream;

namespace PythonDCOP {
  class Client;
  class PCOPMethod;
  class ImportedModules;

  // Python interface
  PyObject *dcop_call( PyObject* self, PyObject* args );
  PyObject *application_list( PyObject *self, PyObject *args );
  PyObject *object_list(PyObject *self, PyObject *args );
  PyObject *method_list(PyObject *self, PyObject *args );
  PyObject *register_as( PyObject *self, PyObject *args);
  PyObject *create_dcop_object( PyObject *self, PyObject *args);
  PyObject *set_method_list( PyObject *self, PyObject *args);
  PyObject *connect_DCOP_Signal( PyObject *self, PyObject *args);
  PyObject *disconnect_DCOP_Signal( PyObject *self, PyObject *args);


  // helpers...
  void delete_dcop_object(void *vp);
  PyObject *make_py_list(const QCStringList &qt_list);

  /**
   * Used by the Python interface to talk to DCOP
   */
  class Client {
  public:
    Client();
    ~Client();
    void processEvents();
    DCOPClient *dcop();
//     ImportedModules *module() const { return m_module; }
    static Client *instance();
  protected:
    DCOPClient *m_dcop;
//     ImportedModules *m_module;
    static Client *s_instance;
    QApplication *m_qapp;
  };

  /**
   * Class representing a DCOPObject.
   * This class represents a DCOP object in a "server" capacity.
   */
  class PCOPObject : public DCOPObject
  {
  public:
    /**
     * Construct from a pointer to the Python object holding this CObject.
     */
    PCOPObject(PyObject *py_obj);

    /**
     * Construct from a pointer to the Python object holding this CObject and
     * a DCOP object ID.
     */
    PCOPObject(PyObject *py_obj, const char *objid);

    virtual ~PCOPObject();

    /**
     * Process method fun, whose arguments are marshalled in data.
     * Set replyType to be the reply type and marshall the reply data into replyData.
     */
    virtual bool process(const QCString &fun, const QByteArray &data, QCString& replyType, QByteArray &replyData);

    /**
     * Return list of supported functions (methods).
     */
    virtual QCStringList functions();

    /**
     * Set the list of methods that this object handles.
     * The key of the QT dictionary is the method signature; the data in
     * the dictionary is a pointer to the python method to which it corresponds.
     */
    virtual bool setMethodList(QAsciiDict<PyObject> meth_list);

    /**
     * Returns the current list of methods, as set by setMethodList.
     */
    virtual PyObject *methodList();

    /**
     * Matches an 'incoming' method signature (fun) and returns a PCOPMethod pointer,
     * or NULL if none match.
     */
    PCOPMethod *matchMethod(const QCString &fun);

  private:
    virtual bool py_process(const QCString &fun, const QByteArray &data, QCString& replyType, QByteArray &replyData);

    /**
     * The Python object holding this CObject.
     */
    PyObject *m_py_obj;

    /**
     * The list of methods this object supports.
     */
    QAsciiDict<PCOPMethod> m_methods;

  };

  /**
   * Class representing a data type, with methods for DCOP marshalling and unmarshalling.
   */
  class PCOPType
  {
  public:
    PCOPType( const QCString& dcop_representation);
    ~PCOPType();

    QCString signature() const;

    PyObject* demarshal( QDataStream& str ) const;
    bool marshal( PyObject* obj, QDataStream& str ) const;

    // checks if the given PyObject can be marshalled as this PCOPType
    bool isMarshallable( PyObject *obj ) const;

    const QCString &type() const { return m_type; }
    const PCOPType *leftType() const { return m_leftType; }
    const PCOPType *rightType() const { return m_rightType; }

    // TODO: make these private
    QCString m_type;
    PCOPType* m_leftType;
    PCOPType* m_rightType;

  };

  /**
   * Class representing a DCOP method
   */
  class PCOPMethod
  {
  public:
    PCOPMethod( const QCString& dcop_signature );
    ~PCOPMethod();

    int paramCount() const;
//     QCString signature() const;
//     QCString name() const;
    PCOPType* param( int );
    const PCOPType* param( int ) const;

    bool setPythonMethod(PyObject *py_method);
    PyObject *pythonMethod() const { return m_py_method; }
    const QCString &signature() const { return m_signature; }
    const QCString &name() const { return m_name; }
    const PCOPType *type() const { return m_type; }

    QCString m_signature;
    QCString m_name;
    PCOPType* m_type;
    QList<PCOPType> m_params;
  private:
    PyObject *m_py_method;
  };

  /**
   * Class representing a DCOP class.
   */
  class PCOPClass
  {
  public:
    PCOPClass( const QCStringList& dcop_style_methods);
    ~PCOPClass();

    const PCOPMethod* method( const QCString &name, PyObject *argTuple = 0 );

    QCStringList m_ifaces;
    QAsciiDict<PCOPMethod> m_methods;
  };

}

#endif
