#ifndef _SQUID_SRC_COMM_OPENERSTATEDATA_H
#define _SQUID_SRC_COMM_OPENERSTATEDATA_H

#include "base/AsyncCall.h"
#include "base/AsyncJob.h"
#include "cbdata.h"
#include "CommCalls.h"
#include "comm_err_t.h"
#include "comm/forward.h"

namespace Comm
{

/**
 * Async-opener of a Comm connection.
 */
class ConnOpener : public AsyncJob
{
protected:
    virtual void start();
    virtual void swanSong();

public:
    void noteAbort() { mustStop("externally aborted"); }

    typedef CbcPointer<ConnOpener> Pointer;

    virtual bool doneAll() const;

    ConnOpener(Comm::ConnectionPointer &, AsyncCall::Pointer &handler, time_t connect_timeout);
    ~ConnOpener();

    void setHost(const char *);    ///< set the hostname note for this connection
    const char * getHost() const;  ///< get the hostname noted for this connection

private:
    // Undefined because two openers cannot share a connection
    ConnOpener(const ConnOpener &);
    ConnOpener & operator =(const ConnOpener &c);

    void earlyAbort(const CommCloseCbParams &);
    void timeout(const CommTimeoutCbParams &);
    void sendAnswer(comm_err_t errFlag, int xerrno, const char *why);
    static void InProgressConnectRetry(int fd, void *data);
    static void DelayedConnectRetry(void *data);
    void connect();
    void connected();
    void lookupLocalAddress();

    void sleep();
    void restart();

    bool createFd();
    void closeFd();
    void keepFd();
    void cleanFd();

    void cancelSleep();

private:
    char *host_;                         ///< domain name we are trying to connect to.
    int temporaryFd_;                    ///< the FD being opened. Do NOT set conn_->fd until it is fully open.
    Comm::ConnectionPointer conn_;       ///< single connection currently to be opened.
    AsyncCall::Pointer callback_;        ///< handler to be called on connection completion.

    int totalTries_;   ///< total number of connection attempts over all destinations so far.
    int failRetries_;  ///< number of retries current destination has been tried.

    /// if we are not done by then, we will call back with COMM_TIMEOUT
    time_t deadline_;

    /// handles to calls which we may need to cancel.
    struct Calls {
        AsyncCall::Pointer earlyAbort_;
        AsyncCall::Pointer timeout_;
        /// Whether we are idling before retrying to connect; not yet a call
        /// [that we can cancel], but it will probably become one eventually.
        bool sleep_;
    } calls_;

    CBDATA_CLASS2(ConnOpener);
};

}; // namespace Comm

#endif /* _SQUID_SRC_COMM_CONNOPENER_H */
