////////////////////////////////////////////////////////////////////////////////
//                                                                             /
// 2012-2019 (c) Baical                                                        /
//                                                                             /
// This library is free software; you can redistribute it and/or               /
// modify it under the terms of the GNU Lesser General Public                  /
// License as published by the Free Software Foundation; either                /
// version 3.0 of the License, or (at your option) any later version.          /
//                                                                             /
// This library is distributed in the hope that it will be useful,             /
// but WITHOUT ANY WARRANTY; without even the implied warranty of              /
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU           /
// Lesser General Public License for more details.                             /
//                                                                             /
// You should have received a copy of the GNU Lesser General Public            /
// License along with this library.                                            /
//                                                                             /
////////////////////////////////////////////////////////////////////////////////
#ifndef P7_THREAD_H
#define P7_THREAD_H

//TO FIX

#define THREAD_EXIT_TIMEOUT                   (30000) //30 seconds

class CThread
{
private:
    IJournal  *m_pLog;
protected:
    HANDLE     m_hExit_Event;
    HANDLE     m_hThread;
public:
    CThread():
         m_hThread(NULL)
        ,m_hExit_Event(NULL)
        ,m_pLog(NULL)
    {
    }

    ~CThread()
    {
        if (m_hThread)
        {
            JOURNAL_WARNING(m_pLog, L"Thread is not stopped ?!");
        }
        //Destroy();                             

        if (m_pLog)
        {
            m_pLog->Release();
            m_pLog = NULL;
        }

        if (m_hExit_Event)
        {
            CloseHandle(m_hExit_Event);
            m_hExit_Event = NULL;
        }
    }

    void Set_Log(IJournal *i_pLog)
    {
        //if thread is already started we do not allow to set logger
        //it can cause problems, because usage of this variable is not
        //thread safe.
        if (m_hThread)
        {
            return;
        }

        if (m_pLog)
        {
            m_pLog->Release();
            m_pLog = NULL;
        }

        m_pLog = i_pLog;
        if (m_pLog)
        {
            m_pLog->Add_Ref();
        }
    }


    tBOOL Create()
    {
        tBOOL l_bReturn = TRUE;

        if (m_hThread)
        {
            return l_bReturn;
        }

        if (l_bReturn)
        {
            m_hExit_Event = CreateEvent(NULL, FALSE, FALSE, NULL);
            if (NULL == m_hExit_Event)
            {
                l_bReturn = FALSE;
                JOURNAL_ERROR(m_pLog, L"Exit event wasn't created !");
            }
        }


        if (l_bReturn)
        {
            m_hThread = (void*)_beginthread(&Static_Routine, 0, this);
            if (NULL == m_hThread)
            {
                l_bReturn = FALSE;
                JOURNAL_ERROR(m_pLog, L"Thread wasn't created !");
            }
        }

        if (FALSE == l_bReturn)
        {
            Destroy();
        }

        return l_bReturn;
    }

    BOOL Destroy()
    {
        BOOL l_bReturn = TRUE;
        if (NULL == m_hThread)
        {
            return l_bReturn;
        }

        DWORD l_dwTimeStamp = GetTickCount();
        DWORD l_dwDifference = 0;

        if (m_hExit_Event)
        {
            SetEvent(m_hExit_Event);
            while (m_hThread)
            {
                Sleep(10);

                //if we wait X minute and thread didn't close
                if (CTicks::Difference(GetTickCount(), l_dwTimeStamp) >= THREAD_EXIT_TIMEOUT)
                {
                    JOURNAL_ERROR(m_pLog, L"Thread wasn't closed !");
                    l_bReturn = FALSE;
                    break;
                }
            }
        }

        return l_bReturn;
    }

protected:
    virtual void Routine()
    { 
        JOURNAL_WARNING(m_pLog, L"Thread main function wasn't redefined !");
        m_hThread = NULL; 
    }

private:
    static void __cdecl Static_Routine(void *i_pContext)
    {
        CThread * l_pRoutine = (CThread *)i_pContext;
        if (l_pRoutine)
        {
            l_pRoutine->Routine();
        }
        _endthread();
    } 

};

#endif //P7_THREAD_H
