class CZonePing 
{
// CZonePing
public:
	CZonePing();
	~CZonePing();

    void * operator new(size_t size);
    void * operator new[](size_t size);
    void operator delete( void* ptr );
    void operator delete[] ( void* ptr );


// IZonePing
public:
    typedef void PingCallbackFunc( DWORD inet, DWORD latency, void * context );


    int __stdcall StartupServer();
    virtual HRESULT __stdcall StartupClient( DWORD ping_interval_sec );
    virtual HRESULT __stdcall Shutdown();
    virtual HRESULT __stdcall Add( DWORD inet );
    virtual HRESULT __stdcall Ping( DWORD inet );
    virtual HRESULT __stdcall Remove( DWORD inet );
    virtual HRESULT __stdcall RemoveAll();
    virtual HRESULT __stdcall Lookup( DWORD inet, DWORD *pLatency );
    virtual HRESULT __stdcall RegisterCallback( DWORD inet, PingCallbackFunc * pfn, void * context);


private:
    class ZonePing
    {
    public: 
        ZonePing(DWORD inet = 0);

	    enum PINGSTATE { UNKNOWN, PINGER, PINGEE };

        void * operator new(size_t size);
        void * operator new[](size_t size);
        void operator delete( void* ptr );
        void operator delete[] ( void* ptr );

        DWORD     m_inet;
        DWORD     m_latency;
        DWORD     m_samples;
        DWORD     m_tick;

        // mdm - for use with callbacks
        PingCallbackFunc * m_pCallback;
        void *    m_context;

        PINGSTATE m_state;
        ZonePing* m_pNext;

    };


protected:
    ZonePing * FindNextItem( ZonePing * pPing, bool * bWrapped );
    inline DWORD GetListIndex(DWORD inet)
	{
		return ( inet & 0x000000FF ) % m_PingIntervalSec;
	}

    void ScanForMissed( void );

    inline DWORD GetTickDelta( DWORD now, DWORD then )
    {
        if ( now >= then )
        {
            return now - then;
        }
        else
        {
            return INFINITE - then + now;
        }
    }

    inline DWORD Get13BitTickDelta( DWORD now, DWORD then )
    {
        if ( now >= then )
        {
            return now - then;
        }
        else
        {
            return 0x1FFF - then + now;
        }
    }

    BOOL    CreateSocket();

    ZonePing* m_PingArray;
    DWORD     m_PingEntries;
    ZonePing * m_pCurrentItem;

    DWORD*    m_inetArray;
    DWORD     m_inetAlloc;

    DWORD  m_PingIntervalSec;
    DWORD  m_CurInterval;

    SOCKET m_Socket;
    BOOL   m_bWellKnownPort;
    HANDLE m_hWellKnownPortEvent;

    HANDLE m_hStopEvent;
    CRITICAL_SECTION m_pCS[1];

    HANDLE m_hPingerThread;
    HANDLE m_hPingeeThread;

    static DWORD WINAPI PingerThreadProc( LPVOID p );
    static DWORD WINAPI PingeeThreadProc( LPVOID p );

    void PingerThread();
    void PingeeThread();

    HANDLE m_hStartupMutex;
    long   m_refCountStartup;

    DWORD  m_inetLocal[4];  // allow up to 4 ip address
};
