#include "dbwindlp.h"

UINT        flagsOutput = 0;
UINT        modeOutput = OMD_NONE;  // Output mode
BOOL        fDebugSystem = FALSE;

// Output buffer

char        achBuffer[CCHBUFFERMAX];
UINT        cchBuffer = 0;
int         cLinesBuffer = 0;
HWND        hwndBufferNotify = NULL;

// Task filter

HTASK htaskFilter = NULL;
char szTaskFilter[MAX_MODULE_NAME];


BOOL CALLBACK LibMain(HINSTANCE hinst, UINT wDS, UINT cbHeap, DWORD unused)
{
    // Nothing to do.

    return TRUE;
}

BOOL CALLBACK _export _loadds WEP(BOOL fSystemExit)
{
    // Nothing to do.

    return TRUE;
}

BOOL WINAPI _export _loadds OutputInit(void)
{
    fDebugSystem = GetSystemMetrics(SM_DEBUG);

    flagsOutput = 0;

    if (modeOutput == OMD_MONO || MonoQuery())
        flagsOutput |= DBOF_HASMONO;

    if (modeOutput == OMD_COM1 || ComQuery(1))
        flagsOutput |= DBOF_HASCOM1;

    if (modeOutput == OMD_COM2 || ComQuery(2))
        flagsOutput |= DBOF_HASCOM2;

    return TRUE;
}

BOOL WINAPI _export _loadds SetOutputMode(UINT mode)
{
    if (mode == OMD_MONO && !(flagsOutput & DBOF_HASMONO))
        mode = OMD_NONE;

    if (mode == OMD_COM1 && !(flagsOutput & DBOF_HASCOM1))
        mode = OMD_NONE;

    if (mode == OMD_COM2 && !(flagsOutput & DBOF_HASCOM2))
        mode = OMD_NONE;

    if (mode != modeOutput)
    {
        if (modeOutput != OMD_NONE)
            NotifyUnRegister(NULL);

        switch (modeOutput)
        {
        case OMD_NONE:
        case OMD_BUFFER:
            break;

        case OMD_COM1:
        case OMD_COM2:
            ComClose();
            break;

        case OMD_MONO:
            MonoClose();
            break;
        }

        modeOutput = mode;

        switch (mode)
        {
        case OMD_NONE:
        case OMD_BUFFER:
            break;

        case OMD_COM1:
            if (!ComOpen(1))
                modeOutput = OMD_NONE;
            break;

        case OMD_COM2:
            if (!ComOpen(2))
                modeOutput = OMD_NONE;
            break;

        case OMD_MONO:
            if (!MonoOpen())
                modeOutput = OMD_NONE;
            break;

        default:
            modeOutput = OMD_BUFFER;
            break;
        }

        if (modeOutput != OMD_NONE)
            NotifyRegister(NULL, NotifyCallback, NF_NORMAL | NF_RIP);
    }

    // Return FALSE if the mode wasn't changed the way the caller asked.
    //
    return modeOutput == mode;
}

UINT WINAPI _export _loadds GetOutputMode(void)
{
    return modeOutput;
}

UINT WINAPI _export _loadds GetOutputFlags(void)
{
    return flagsOutput;
}

void WINAPI _export _loadds SetBufferNotify(HWND hwndNotify)
{
    hwndBufferNotify = hwndNotify;
}

BOOL WINAPI _export _loadds GetOutputBufferInfo(OUTBUFINFO FAR* lpobi)
{
    if (modeOutput != OMD_BUFFER)
        return FALSE;

    lpobi->lpch = achBuffer;
    lpobi->cch = cchBuffer;
    lpobi->cLines = cLinesBuffer;

    return TRUE;
}

void WINAPI _export _loadds ResetBuffer()
{
    cLinesBuffer = 0;
    cchBuffer = 0;
}

BOOL WINAPI _export _loadds SetTaskFilter(LPCSTR lpszTaskName)
{
    if (lstrlen(lpszTaskName) <= MAX_MODULE_NAME - 1)
    {
        lstrcpy(szTaskFilter, lpszTaskName);
        return TRUE;
    }
    return FALSE;
}

void WINAPI _export _loadds GetTaskFilter(LPSTR lpszTaskName, int cch)
{
    lstrcpyn(lpszTaskName, szTaskFilter, cch);
}

BOOL CALLBACK _export _loadds NotifyCallback(WORD id, DWORD dwData)
{
    BOOL fHandled;
    static char szNotify[128];
    static int cReenter = 0;

    // If we're not outputting anything,
    // just return FALSE to chain to next handler.
    //
    if (modeOutput == OMD_NONE)
        return FALSE;

#ifdef DEBUG
    // For debugging, allow a few levels of recursion so we can
    // see any debug output messages generated by DBWin itself.
    //
    if (cReenter != 0)
        _asm { int 3 }
#else
    // Don't allow any reentrancy at all.
    //
    if (cReenter != 0)
        return FALSE;
#endif

    cReenter++;
    fHandled = FALSE;

    switch (id)
    {
    case NFY_STARTTASK:
        TaskFilterStartTask();
        break;

    case NFY_EXITTASK:
        TaskFilterExitTask();
        break;

    case NFY_INCHAR:
        switch (modeOutput)
        {
        case OMD_COM1:
        case OMD_COM2:
            fHandled = (BOOL)ComIn();
            break;

        default:
            fHandled = (BOOL)'i';
        }
        break;

    case NFY_OUTSTR:
        if (!CheckTaskFilter())
            break;

        switch (modeOutput)
        {
        case OMD_BUFFER:
            fHandled = BufferOut((LPCSTR)dwData);
            break;

        case OMD_MONO:
            fHandled = MonoOut((LPCSTR)dwData);
            break;

        case OMD_COM1:
        case OMD_COM2:
            fHandled = ComOut((LPCSTR)dwData);
            break;
        }
        break;

    case NFY_RIP:
        if (!fDebugSystem)
        {
            if (!CheckTaskFilter())
                break;
            FormatRip(szNotify, (NFYRIP FAR*)dwData);
            cReenter--;
            OutputDebugString(szNotify);
            cReenter++;
        }
        break;

    case NFY_LOGERROR:
        if (!fDebugSystem)
        {
            if (!CheckTaskFilter())
                break;
            FormatLogError(szNotify, (NFYLOGERROR FAR*)dwData);
            cReenter--;
            OutputDebugString(szNotify);
            cReenter++;
        }
        break;

    case NFY_LOGPARAMERROR:
        if (!fDebugSystem)
        {
            if (!CheckTaskFilter())
                break;
            FormatLogParamError(szNotify, (NFYLOGPARAMERROR FAR*)dwData);
            cReenter--;
            OutputDebugString(szNotify);
            cReenter++;
        }
        break;
    }

    cReenter--;
    return fHandled;
}

BOOL BufferOut(LPCSTR lpszOut)
{
    UINT cch;
    int cLines;

    cch = DBStrCpy(NULL, lpszOut, &cLines);

    if (cch > (sizeof(achBuffer) - cchBuffer))
    {
        MessageBeep(0);
        return FALSE;
    }

    DBStrCpy(&achBuffer[cchBuffer], lpszOut, NULL);

    cchBuffer += cch;
    cLinesBuffer += cLines;

    // If buffer was originally empty, wake up the main loop.
    //
    if (cchBuffer == cch)
        PostMessage(hwndBufferNotify, WM_BUFFERNOTEMPTY, 0, 0L);

    return TRUE;
}

// This function performs a string copy (or string length if pszDst is NULL)
// It also ensures the destination string is properly formatted for the
// edit controls: line terminators must be CR followed by LF, in that orderc.
// If pcLines != NULL, returns number of lines in the string in *pcLines
//
UINT DBStrCpy(char* pszDst, LPCSTR pszSrc, int far* pcLines)
{
    BYTE ch;
    UINT cch = 0;
    int cLines = 0;

    while (ch = *pszSrc++)
    {
        // If we find a CR or LF, then store a CR/LF in
        // the output string.
        //
        if (ch == 0x0d || ch == 0x0a)
        {
            cch += 2;
            cLines++;
            if (pszDst)
            {
                *pszDst++ = 0x0d;
                *pszDst++ = 0x0a;
            }

            // Skip any other CRs or LFs we find.
            //
            while ((ch = *pszSrc) && (ch == 0x0d || ch == 0x0a))
                ++pszSrc;
        }
        else
        {
            cch++;
            if (pszDst)
                *pszDst++ = ch;
        }
    }
    if (pszDst)
        *pszDst = 0;

    if (pcLines)
        *pcLines = cLines;

    return cch;
}

BOOL CheckTaskFilter()
{
    return (szTaskFilter[0] == 0 || htaskFilter == GetCurrentTask());
}

void TaskFilterStartTask(void)
{
    HTASK htask;
    TASKENTRY te;

    if (htaskFilter || szTaskFilter[0] == 0)
        return;

    htask = GetCurrentTask();

    te.dwSize = sizeof(te);
    if (TaskFindHandle(&te, htask))
    {
        int i;

        // Case-insensitive comparision of task name strings
        //
        for (i = 0; i < sizeof(szTaskFilter); i++)
        {
            char ch1;
            char ch2;

            ch1 = szTaskFilter[i];
            if (ch1 >= 'a' && ch1 <= 'z')
                ch1 -= 'a' - 'A';
            ch2 = te.szModule[i];
            if (ch2 >= 'a' && ch2 <= 'z')
                ch1 -= 'a' - 'A';

            if (ch1 != ch2)
                break;

            // If we got to the end of the string,
            // we have a match.  Set up the filter task
            // and continue.
            //
            if (ch1 == 0)
            {
                htaskFilter = htask;
                break;
            }
        }
    }
}

void TaskFilterExitTask(void)
{
    if (GetCurrentTask() == htaskFilter)
        htaskFilter = NULL;
}
