class ManagedSession
{
public:
        ManagedSession( CSession* pSession )
        : m_used( false )
        , m_pSession( pSession )
        {
        }

        ~ManagedSession()
        {
                delete m_pSession;
        }

        bool IsUsed() const
        {
                return m_used;
        }

        bool CompareSession(CSession* pSession) const
        {
                return m_pSession == pSession;
        }

        CSession* GetSession(const CDataSource& ds)
        {
                if ( !m_used )
                {
                        m_used = true;
                        m_pSession->Open( ds );
                        return m_pSession;
                }

                return NULL;
        }

        void Release()
        {
                if ( m_used )
                {
                        m_pSession->Close();
                        m_used = false;
                }
        }

private:
        bool m_used;
        CSession* m_pSession;
};

class DBConn
{
public:
        DBConn()
        : m_pDBPropSet( NULL )
        , m_init(false)
        {
                Initialize();
        }

        ~DBConn()
        {
                m_init = false;

                POSITION pos = m_managedSessionPtrList.GetHeadPosition();
                while ( pos )
                {
                        ManagedSession* pManagedSession = m_managedSessionPtrList.GetNext( pos );
                        pManagedSession->Release();
                        delete pManagedSession;
                }

                m_ds.Close();

                delete m_pDBPropSet;

                CoUninitialize();
        }

        CSession* GetSession()
        {
                if ( !m_init )
                        return NULL;

                m_cs.Lock();

                POSITION pos = m_managedSessionPtrList.GetHeadPosition();
                while ( true )
                {
                        while ( pos )
                        {
                                ManagedSession* pManagedSession = m_managedSessionPtrList.GetNext( pos );
                                if ( !pManagedSession->IsUsed() )
                                        return pManagedSession->GetSession( m_ds );
                        }

                        m_cs.UnLock();

                        Sleep(100);

                        m_cs.Lock();
                }

                m_cs.UnLock();
        }

        void Release(CSession* pSession)
        {
                if ( !m_init )
                        return;

                m_cs.Lock();

                POSITION pos = m_managedSessionPtrList.GetHeadPosition();
                while ( pos )
                {
                        ManagedSession* pManagedSession = m_managedSessionPtrList.GetNext(pos);
                        if (pManagedSession->CompareSession(pSession) && pManagedSession->IsUsed())
                        {
                                pManagedSession->Release();
                                return;
                        }
                }

                m_cs.UnLock();
        }

protected:
        HRESULT Initialize(int sessionCnt = 10)
        {
                try 
                {
                        HRESULT hr = CoInitialize( NULL );
                        if ( FAILED( hr ) )
                                throw hr;

                        CLSID clsid = CLSID_NULL;
                        hr = CLSIDFromProgID( OLESTR(\"MSDASQL.1\"), &clsid );
                        if ( FAILED( hr ) )
                                throw hr;

                        CreateDBPropSet(\"lecture\", \"127.0.0.1\", \"3306\", \"lecture\", \"lecture\", \"lecture\");

                        hr = m_ds.Open( clsid, m_pDBPropSet );
                        if ( FAILED( hr ) )
                                throw hr;

                        for (int i = 0; i < sessionCnt; ++i)
                        {
                                CSession* pSession = new CSession;
                                ManagedSession* pManagedSession = new ManagedSession(pSession);
                                m_managedSessionPtrList.AddTail(pManagedSession);
                        }
                }
                catch (const HRESULT& hr)
                {
                        return hr;
                }

                m_init = true;
                return S_OK;
        }

        void CreateDBPropSet(const CString& dsn, const CString& server, const CString& port,
                                                const CString& dbname, const CString& id, const CString& pw)
        {
                CString DBPropString;
                
                DBPropString.Format(\"Dsn=%s;server=%s;uid=%s;database=%s;port=%s;pwd=%s\",
                        dsn, server, id, dbname, port, pw);

                m_pDBPropSet = new CDBPropSet( DBPROPSET_DBINIT );
                m_pDBPropSet->AddProperty( DBPROP_INIT_PROMPT, static_cast<short>(4) );
                m_pDBPropSet->AddProperty( DBPROP_INIT_PROVIDERSTRING, DBPropString );
        }

private:
        CDataSource m_ds;
        CList<ManagedSession*> m_managedSessionPtrList;
        CDBPropSet* m_pDBPropSet;

        bool m_init;
        CriticalSection m_cs;
};