/***************************************************************************
                          socket.h  -  description
                             -------------------
    begin                : Sun Mar 10 2002
    copyright            : (C) 2002 by Vladimir Shutoff
    email                : vovan@shutoff.ru
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 _SOCKET_H
#define _SOCKET_H

#include <qobject.h>
#include "buffer.h"
#include "contacts.h"
#include "event.h"
#include "simapi.h"

class QTimer;

#ifdef USE_OPENSSL
typedef struct ssl_ctx_st SSL_CTX;
typedef struct ssl_st SSL;
typedef struct bio_st BIO;
#endif

namespace SIM
{

class EXPORT SocketNotify
{
public:
    SocketNotify() {}
    virtual ~SocketNotify() {}
    virtual void connect_ready() = 0;
    virtual void read_ready() = 0;
    virtual void write_ready() = 0;
    virtual void error_state(const QString &err_text, unsigned code = 0) = 0;
    virtual void resolve_ready(unsigned long) {}
};

class EXPORT Socket
{
protected:
    QString m_host;
    unsigned short m_port;
    SocketNotify *notify;
public:
    Socket() : m_port(0), notify(NULL) {}
    virtual ~Socket() {}
    virtual int read(char *buf, unsigned int size) = 0;
    virtual void write(const char *buf, unsigned int size) = 0;
    virtual void connect(const QString &host, unsigned short port)
    {
        m_host = host;
        m_port = port;
    }
    virtual void close() = 0;
    virtual unsigned long localHost() = 0;
    virtual void pause(unsigned) = 0;
    void error(const QString &err_text, unsigned code=0);
    void setNotify(SocketNotify *n) { notify = n; }
    enum Mode
    {
        Direct,
        Indirect,
        Web
    };
    virtual Mode mode() const { return Direct; }
    const QString &getHost() const { return m_host; }
    unsigned short getPort() const { return m_port; }
    const SocketNotify *getNotify() const { return notify; }
};

class ServerSocket;
class TCPClient;

class EXPORT ServerSocketNotify
{
public:
    ServerSocketNotify();
    virtual ~ServerSocketNotify();
    virtual bool accept(Socket*, unsigned long ip) = 0;
    virtual void bind_ready(unsigned short port) = 0;
    virtual bool error(const QString &err) = 0;
    virtual void bind(unsigned short mixPort, unsigned short maxPort, TCPClient *client);
#ifndef WIN32
    virtual void bind(const char *path);
#endif
    void setListener(ServerSocket *listener);
    ServerSocket *m_listener;
};

class EXPORT ServerSocket
{
public:
    ServerSocket();
    virtual ~ServerSocket() {}
    void setNotify(ServerSocketNotify *n) { notify = n; }
    virtual void bind(unsigned short mixPort, unsigned short maxPort, TCPClient *client) = 0;
#ifndef WIN32
    virtual void bind(const char *path) = 0;
#endif
    virtual void close() = 0;
protected:
    ServerSocketNotify *notify;
};

class ClientSocket;

class EXPORT SocketFactory : public QObject
{
    Q_OBJECT
public:
    SocketFactory(QObject *parent);
    virtual ~SocketFactory();
    virtual Socket *createSocket() = 0;
    virtual ServerSocket *createServerSocket() = 0;
    void remove(Socket*);
    void remove(ServerSocket*);
    bool add(ClientSocket *s);
    bool erase(ClientSocket *s);
    void setActive(bool);
    bool isActive() const;
    virtual void checkState() {}
protected slots:
    void idle();
private:
    struct SocketFactoryPrivate *d;

    COPY_RESTRICTED(SocketFactory)
};

SocketFactory EXPORT *getSocketFactory();

class EXPORT ClientSocketNotify
{
public:
    ClientSocketNotify() {}
    virtual ~ClientSocketNotify() {}
    virtual bool error_state(const QString &err, unsigned code = 0) = 0;
    virtual void connect_ready() = 0;
    virtual void packet_ready() = 0;
    virtual void write_ready() {}
    virtual void resolve_ready(unsigned long) {}
};

class TCPClient;

class EXPORT ClientSocket : public SocketNotify
{
public:
    ClientSocket(ClientSocketNotify*, Socket *sock=NULL);
    ~ClientSocket();

    virtual void error_state(const QString &err, unsigned code = 0);
    void connect(const QString &host, unsigned short port, TCPClient *client);
    void write();
    void pause(unsigned);
    unsigned long localHost();
    bool created();
    virtual void read_ready();
    void close();
    void setRaw(bool mode);
    Socket *socket() const { return m_sock; }
    void setSocket(Socket *s, bool bClearError = true);
    void setNotify(ClientSocketNotify *n) { m_notify = n; }
    const QString &errorString() const;
    virtual Buffer &readBuffer() { return m_readBuffer; }
    virtual Buffer &writeBuffer() { return m_writeBuffer; }
protected:
    virtual void connect_ready();
    virtual void write_ready();
    virtual void resolve_ready(unsigned long ip);

    Socket *m_sock;
    ClientSocketNotify *m_notify;
    bool bRawMode;
    bool bClosed;

    Buffer m_readBuffer;
    Buffer m_writeBuffer;

    unsigned	errCode;
    QString     errString;
    friend class SocketFactory;
};

const unsigned NO_RECONNECT = (unsigned)(-1);

class EXPORT TCPClient : public QObject, public Client, public ClientSocketNotify, public EventReceiver
{
    Q_OBJECT
public:
    TCPClient(Protocol *protocol, Buffer *cfg, unsigned priority = DefaultPriority);

    virtual void	        setStatus(unsigned status, bool bCommon);

    virtual QString         getServer() const = 0;
    virtual unsigned short  getPort() const = 0;
    unsigned long	        ip() const { return m_ip; }
    virtual ClientSocket   *socket() { return m_clientSocket; }

    unsigned m_reconnect;
protected slots:
    void reconnect();
    void loginTimeout();
protected:
    virtual void	setStatus(unsigned status) = 0;
    virtual void	disconnected() = 0;

    virtual void	resolve_ready(unsigned long);
    virtual void	connect_ready();
    virtual bool	error_state(const QString &err, unsigned code = 0);
    virtual void	socketConnect();
    virtual bool    processEvent(Event *e);
    virtual Socket  *createSocket();
    virtual ClientSocket *createClientSocket();
    void			setClientStatus(unsigned status);
    unsigned		m_logonStatus;
    unsigned		m_ip;
    QTimer			*m_timer;
    QTimer			*m_loginTimer;
    bool			m_bWaitReconnect;
private:
    ClientSocket	*m_clientSocket;
};

#ifdef USE_OPENSSL

EXPORT QByteArray md5(const char*, int size = -1);
EXPORT QByteArray sha1(const char*, int size = -1);

class EXPORT SSLClient : public SocketNotify, public Socket
{
public:
    SSLClient(Socket*);
    ~SSLClient();
    virtual int read(char *buf, unsigned int size);
    virtual void write(const char *buf, unsigned int size);
    virtual void connect(const QString &host, unsigned short port);
    virtual void close();
    virtual unsigned long localHost();
    virtual void pause(unsigned);
    bool connected() const { return m_bSecure; }
    Socket *socket() const { return sock; }
    void setSocket(Socket *s);
    bool init();
    void accept();
    void connect();
    void shutdown();
    void process(bool bInRead=false, bool bWantRead=false);
    void write();
    void clear();
protected:
    virtual bool initSSL() = 0;
    bool initTLS1(bool bDH);
    bool initBIO();
    Buffer wBuffer;
    virtual void connect_ready();
    virtual void read_ready();
    virtual void write_ready();
    virtual void error_state(const QString &err, unsigned code = 0);
    Socket *sock;
    enum State
    {
        SSLAccept,
        SSLConnect,
        SSLShutdown,
        SSLWrite,
        SSLConnected
    };
    State state;
    bool m_bSecure;
    SSL_CTX *mpCTX;
    SSL     *mpSSL;
    BIO     *mrBIO;
    BIO     *mwBIO;
};

#endif

}

#endif
