/*
**    Name: ODBCLib.c
**
**    Date: Thu Jan 17 12:13:43 2002
**
*/

#include "ODBCLib.h"
#include "isql.h"
#include "kernel.h"
#include "hourglass.h"
#include "wimp.h"
#include <string.h>

ODBCLibWindow odbcwindows[256];

int odbclibhourglass = 0;

int odbclibquit = 0;

int odbclibversion = 0;

int odbclibtask = 0;

/* Set when Escape is pressed while waiting for the ODBC manager */

static int odbclibescape = 0;

#ifndef TaskManager_TaskNameFromHandle
#define TaskManager_TaskNameFromHandle 0x42680
#endif

/* Centiseconds between checks that the manager task still exists */

#define ODBCLIB_ALIVE_INTERVAL 100

/* Centiseconds to wait for !ODBC to start, and for a query to finish after Message_Quit */

#define ODBCLIB_START_TIMEOUT 1000
#define ODBCLIB_QUIT_TIMEOUT  1000

/* Centiseconds a completed query may wait for its client before it is treated as abandoned */

#define ODBCLIB_ABANDON_TIMEOUT 1000

/* Report an ODBC failure once until a call next succeeds */

static int odbclibreported = 0;

static void odbclib_report(const char *text)
    {
    _kernel_swi_regs regs;

    os_error error;

    if (odbclibreported)
        {
        return;
        }

    odbclibreported = 1;

    error.errnum = 0;
    strncpy(error.errmess,text,sizeof(error.errmess) - 1);
    error.errmess[sizeof(error.errmess) - 1] = 0;

    regs.r[0] = (int)&error;
    regs.r[1] = wimp_ERROR_BOX_OK_ICON;
    regs.r[2] = (int)"ODBC";
    _kernel_swi(Wimp_ReportError,&regs,&regs);
    }

static int odbclib_time(void)
    {
    _kernel_swi_regs regs;

    if (_kernel_swi(OS_ReadMonotonicTime,&regs,&regs) != NULL)
        {
        return 0;
        }

    return regs.r[0];
    }

static int odbclib_status(int *state)
    {
    _kernel_swi_regs regs;

    _kernel_oserror *error;

    regs.r[0] = SQL_QueryStatusGet;

    error = _kernel_swi(SQL_QueryStatus,&regs,&regs);

    if (error != NULL)
        {
        odbclib_report(error->errmess);
        return 1;
        }

    *state = regs.r[1];

    return 0;
    }

static void odbclib_set_status(int state)
    {
    _kernel_swi_regs regs;

    regs.r[0] = SQL_QueryStatusSet;
    regs.r[1] = state;

    _kernel_swi(SQL_QueryStatus,&regs,&regs);
    }

/* Read the manager task handle and check its version */

static int odbclib_dmtask(void)
    {
    _kernel_swi_regs regs;

    _kernel_oserror *error;

    regs.r[0] = SQL_DMTaskGet;

    error = _kernel_swi(SQL_DMTask,&regs,&regs);

    if (error != NULL)
        {
        odbclib_report(error->errmess);
        return 1;
        }

    if (regs.r[2] < 100)
        {
        odbclibversion = 0;
        odbclibtask = 0;
        odbclib_report("Incorrect ODBC version.");
        return 1;
        }

    odbclibversion = regs.r[2];
    odbclibtask = regs.r[1];

    return 0;
    }

/* Check the manager task still exists: if it died without resetting the
   query status, a pending query would never complete */

static int odbclib_manager_alive(void)
    {
    _kernel_swi_regs regs;

    regs.r[0] = SQL_DMTaskGet;

    if (_kernel_swi(SQL_DMTask,&regs,&regs) != NULL || regs.r[1] == 0)
        {
        return 0;
        }

    regs.r[0] = regs.r[1];

    return _kernel_swi(TaskManager_TaskNameFromHandle,&regs,&regs) == NULL;
    }

static int odbclib_notify(void)
    {
    _kernel_swi_regs regs;

    wimp_message message;

    message.size     = 20;
    message.your_ref = 0;
    message.action   = message_SQL_PENDING;

    regs.r[0] = wimp_USER_MESSAGE;
    regs.r[1] = (int)&message;
    regs.r[2] = odbclibtask;

    return _kernel_swi(Wimp_SendMessage,&regs,&regs) != NULL;
    }

/* Wait for free query slot */

int ODBCLibWait(ODBCLibState state)
    {
    int current;

    int started;

    int deadline;

    int waitstart;

    int completesince;

    int alivecheck;

    if (odbclib_status(&current))
        {
        return 1;
        }

    if (state == ODBC_STATE_COMPLETE)
        {
        if (odbclibversion == 0 && odbclib_dmtask())
            {
            odbclib_set_status(ODBC_STATE_READY);
            return 1;
            }

        odbclib_set_status(ODBC_STATE_PENDING);

/* Send pending message, rereading the task handle if the manager has restarted */

        if (odbclib_notify())
            {
            if (odbclib_dmtask() || odbclib_notify())
                {
                odbclibversion = 0;
                odbclib_set_status(ODBC_STATE_READY);
                odbclib_report("The ODBC manager is not responding.");
                return 1;
                }
            }
        }

    started = 0;
    deadline = 0;
    completesince = 0;
    waitstart = odbclib_time();
    alivecheck = waitstart;
    current = -1;
    odbclibescape = 0;

    while (current != state)
        {
        if (odbclib_status(&current))
            {
            return 1;
            }

        if (current == state)
            {
            break;
            }

/* The manager quit while running this query */

        if (state == ODBC_STATE_COMPLETE && current == ODBC_STATE_NOSERVER)
            {
            odbclib_report("The ODBC manager quit before the query finished.");
            return 1;
            }

/* A pending query whose manager task has gone will never complete */

        if (current == ODBC_STATE_PENDING &&
            odbclib_time() - alivecheck > ODBCLIB_ALIVE_INTERVAL)
            {
            alivecheck = odbclib_time();

            if (!odbclib_manager_alive())
                {
                odbclibversion = 0;
                odbclib_set_status(ODBC_STATE_NOSERVER);

                if (state == ODBC_STATE_COMPLETE)
                    {
                    odbclib_report("The ODBC manager stopped before the query finished.");
                    return 1;
                    }

                continue;
                }
            }

/* Escape abandons the wait. A query this client sent that the manager hasn't
   started is withdrawn; the manager ignores the message once it isn't pending */

        if (odbclibescape)
            {
            odbclibescape = 0;

            if (state == ODBC_STATE_COMPLETE && current == ODBC_STATE_PENDING)
                {
                odbclib_set_status(ODBC_STATE_READY);
                }

            return 1;
            }

        if (current == ODBC_STATE_NOSERVER)
            {
            if (!started)
                {
                _kernel_swi_regs regs;

                _kernel_oserror *error;

                started = 1;
                regs.r[0] = (int)"<ODBC$Dir>.!Run";

                error = _kernel_swi(XWimp_StartTask,&regs,&regs);

                if (error != NULL)
                    {
                    odbclib_report(error->errmess);
                    return 1;
                    }

                waitstart = odbclib_time();
                }
            else if (odbclib_time() - waitstart > ODBCLIB_START_TIMEOUT)
                {
                odbclib_report("The ODBC manager could not be started.");
                return 1;
                }
            }

        if (state == ODBC_STATE_READY && current == ODBC_STATE_COMPLETE)
            {
            if (completesince == 0)
                {
                completesince = odbclib_time() | 1;
                }
            else if (odbclib_time() - completesince > ODBCLIB_ABANDON_TIMEOUT)
                {
                odbclib_set_status(ODBC_STATE_READY);
                completesince = 0;
                continue;
                }
            }
        else
            {
            completesince = 0;
            }

        if (odbclibquit && deadline == 0)
            {
            deadline = odbclib_time() + ODBCLIB_QUIT_TIMEOUT;

            if (deadline == 0)
                {
                deadline = 1;
                }
            }

        if (deadline != 0 && odbclib_time() - deadline > 0)
            {
            return 1;
            }

        if (ODBCLibPoll(state))
            {
            return 1;
            }
        }

    if (state == ODBC_STATE_COMPLETE)
        {
        odbclibreported = 0;
        }

    return 0;
    }

/* Wimp poll handler */

int ODBCLibPoll(ODBCLibState state)
    {
    wimp_block block;

    _kernel_swi_regs regs;

    regs.r[0] = wimp_MASK_LOSE |
                wimp_MASK_GAIN |
                wimp_MASK_RECORDED |
                wimp_MASK_ACKNOWLEDGE;

    if (state == ODBC_STATE_COMPLETE)
        {
//        regs.r[0] |= wimp_MASK_NULL;
        }

    regs.r[1] = (int)&block;

    if (_kernel_swi(Wimp_Poll,&regs,&regs) != NULL)
        {
        return 1;
        }

    switch (regs.r[0])
        {
        case wimp_REDRAW_WINDOW_REQUEST:
            regs.r[1] = (int)&block;

            if (_kernel_swi(Wimp_RedrawWindow,&regs,&regs) != NULL)
                {
                regs.r[0] = 0;
                }

            while (regs.r[0] != 0)
                {
                regs.r[1] = (int)&block;

                if (_kernel_swi(Wimp_GetRectangle,&regs,&regs) != NULL)
                    {
                    break;
                    }
                }

            ODBCLibAddWindow((int)block.redraw.w,1);
            break;

        case wimp_USER_MESSAGE:
            if (block.message.action == message_QUIT)
                {
                odbclibquit = 1;
                }
            break;

        case wimp_POINTER_LEAVING_WINDOW:
            if (odbclibhourglass > 0)
                {
                _kernel_swi(XHourglass_Off,&regs,&regs);
                odbclibhourglass--;
                }
            break;

        case wimp_POINTER_ENTERING_WINDOW:
            if (_kernel_swi(XHourglass_On,&regs,&regs) == NULL)
                {
                odbclibhourglass++;
                }
            break;

        case wimp_KEY_PRESSED:
            if (block.key.c == 0x1b)
                {
                odbclibescape = 1;
                }
            else
                {
                regs.r[0] = block.key.c;
                _kernel_swi(Wimp_ProcessKey,&regs,&regs);
                }
            break;

        case wimp_CLOSE_WINDOW_REQUEST:
        case wimp_MOUSE_CLICK:
            break;
        }

    return 0;
    }

/* Set finished state */

int ODBCLibFinished(void)
    {
    _kernel_swi_regs regs;

    regs.r[0] = SQL_QueryStatusReplyGet;

    if (_kernel_swi(SQL_QueryStatus,&regs,&regs) != NULL)
        {
        regs.r[1] = SQL_ERROR;
        }

    odbclib_set_status(ODBC_STATE_READY);

    return regs.r[1];
    }


/* Initialise */

int ODBCLibInitialise(void)
    {
    memset(odbcwindows,0,sizeof(odbcwindows));

    odbclibhourglass = 0;
    odbclibquit = 0;
    odbclibversion = 0;
    odbclibtask = 0;
    odbclibreported = 0;

    return 0;
    }


/* Add window */

int ODBCLibAddWindow(int w,int type)
    {
    int index;

    if (w == 0)
        {
        return 1;
        }

    for (index = 0;index < 256;index++)
        {
        if (odbcwindows[index].w == w)
            {
            return 0;
            }
        else if (odbcwindows[index].w == 0)
            {
            odbcwindows[index].w = w;
            odbcwindows[index].type = type;
            return 0;
            }
        }

    return 1;
    }


/* Refresh all windows that need it */

int ODBCLibRefresh(void)
    {
    int index;

    wimp_window_info info;

    _kernel_swi_regs regs;

    while (odbclibhourglass > 0)
        {
        _kernel_swi(XHourglass_Off,&regs,&regs);
        odbclibhourglass--;
        }

    if (odbcwindows[0].w == 0)
        {
        return 0;
        }

    for (index = 0;index < 256;index++)
        {
        if (odbcwindows[index].w != 0)
            {
            info.w = (wimp_w)odbcwindows[index].w;
            regs.r[1] = ((int)&info) | 1;

            if (_kernel_swi(Wimp_GetWindowInfo,&regs,&regs) != NULL)
                {
                odbcwindows[index].w = 0;
                continue;
                }

            if (odbcwindows[index].type == 1)
                {

/* Redraw */

                regs.r[0] = odbcwindows[index].w;
                regs.r[1] = info.extent.x0;
                regs.r[2] = info.extent.y0;
                regs.r[3] = info.extent.x1;
                regs.r[4] = info.extent.y1;
                _kernel_swi(Wimp_ForceRedraw,&regs,&regs);
                }
            else if (odbcwindows[index].type == 2)
                {

/* Open */
                regs.r[1] = (int)&info;
                _kernel_swi(Wimp_CloseWindow,&regs,&regs);

                regs.r[1] = (int)&info;
                _kernel_swi(Wimp_OpenWindow,&regs,&regs);

                }

            odbcwindows[index].w = 0;
            }
        }

    return 0;
    }

/* Application poll loop */

int ODBCLibLoop(odbclib_loop *loopfunc)
    {
    if (loopfunc == NULL)
        {
        return 1;
        }

    ODBCLibInitialise();

    while (loopfunc() == 0 && odbclibquit == 0)
        {
        ODBCLibRefresh();
        }

    return 0;
    }
