#if !defined(_DAO_RUNNABLE_H_)
#define _DAO_RUNNABLE_H_
#pragma once
#include <functional>
#include <string>
#include <afxdao.h>
#include "Utility.h"
#include "PromptExceptionMessage.h"

typedef std::function<bool ()>  DaoRunner;

#define DAO_RUNNER_START(a)   DaoRunner a = [&]() -> bool

class DaoRunnable
{
public:
    DaoRunner runner;
    std::string filename;
    int line;
    bool bPromptError;
    CString csPromptMessage;
    DaoRunnable(const std::string &filename, int line)
        : filename(filename), line(line), bPromptError(true)
    {
    }

    bool Run()
    {
        try
        {
            return runner();
        }
        catch (CDaoException* DaoEx)
        {
            DebugLog("%s(%d) CDaoException [%s] Message [%s]\n",  filename.c_str(), line, DaoEx->m_pErrorInfo->m_strDescription, csPromptMessage);

            if (bPromptError)
            {
                CPromptExceptionMessage::promptDaoException(DaoEx, csPromptMessage);
            }

            DaoEx->Delete(); // must delete to avoid memory leak!
        }
        catch (CMemoryException* MemEx)
        {
            TCHAR szCause[255];
            MemEx->GetErrorMessage(szCause, 255);
            DebugLog("%s(%d) CMemoryException [%s] Message [%s]\n",  filename.c_str(), line, szCause, csPromptMessage);

            if (bPromptError)
            {
                CPromptExceptionMessage::promptMemoryException(MemEx, csPromptMessage);
            }

            MemEx->Delete(); // must delete to avoid memory leak!
        }
        catch (CException *e)
        {
            TCHAR szCause[255];
            e->GetErrorMessage(szCause, 255);

            DebugLog("%s(%d) CException [%s] Message [%s]\n",  filename.c_str(), line, szCause, csPromptMessage);

            if (bPromptError)
            {
                e->ReportError();
            }

            e->Delete(); // must delete to avoid memory leak!
        }
        return false;
    }
};

class DaoRunnableBuilder
{
private:
    DaoRunnableBuilder();
    DaoRunnable runnable;
public:
    DaoRunnableBuilder(DaoRunner runner, const std::string &filename, int line)
        : runnable(filename, line)
    {
        runnable.runner       = runner;
        runnable.filename     = filename;
        runnable.line         = line;
        runnable.bPromptError = true;
    }

    DaoRunnableBuilder& SetPromptError(bool bPromptError)
    {
        runnable.bPromptError = bPromptError;
        return *this;
    }

    DaoRunnableBuilder& SetErrorMessage(CString &msg)
    {
        runnable.csPromptMessage = msg;
        return *this;
    }

    DaoRunnableBuilder& SetErrorMessage(const char *fmt, ...)
    {
        va_list argList;
        va_start(argList, fmt);
        runnable.csPromptMessage.FormatV(fmt, argList);
        va_end(argList);
        return *this;
    }

    DaoRunnable &Build()
    {
        return runnable;
    }
};

#define DAO_BUILD_RUNNABLE_F(f) DaoRunnableBuilder(f, __FILE__, __LINE__)

#endif
