/*!
 * \file db/dbmi_driver/driver.c
 *
 * \brief DBMI Library (driver) - drivers
 *
 * (C) 1999-2008 by the GRASS Development Team
 *
 * This program is free software under the GNU General Public
 * License (>=v2). Read the file COPYING that comes with GRASS
 * for details.
 *
 * \author Joel Jones (CERL/UIUC), Radim Blazek
 * \author Modified by Glynn Clements <glynn gclements.plus.com>,
 * Markus Neteler <neteler itc.it>,
 * Huidae Cho <grass4u gmail.com>
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <grass/gis.h>
#include <grass/dbmi.h>
#include "procs.h"
#define DB_DRIVER_C
#include "dbstubs.h"

/*!
   \brief Get driver (?)

   \param argc, argv arguments

   \return 0 on success
   \return 1 on failure
 */
int db_driver(int argc, char *argv[])
{
    int stat;
    int procnum;
    int i;
    int rfd, wfd;
    FILE *send, *recv;
    char *modestr;

    /* Read and set environment variables, see dbmi_client/start.c */
    if ((modestr = getenv("GRASS_DB_DRIVER_GISRC_MODE"))) {
        int mode;

        mode = atoi(modestr);

        if (mode == G_GISRC_MODE_MEMORY) {
            G_set_gisrc_mode(G_GISRC_MODE_MEMORY);
            G_setenv_nogisrc("DEBUG", getenv("DEBUG"));
            G_setenv_nogisrc("GISDBASE", getenv("GISDBASE"));
            G_setenv_nogisrc("LOCATION_NAME", getenv("LOCATION_NAME"));
            G_setenv_nogisrc("MAPSET", getenv("MAPSET"));
            G_debug(3, "Driver GISDBASE set to '%s'", G_getenv("GISDBASE"));
        }
    }

#ifdef __MINGW32__
    /* TODO: */
    /* We should close everything except stdin, stdout but _fcloseall()
     * closes open streams not file descriptors. _getmaxstdio too big number.
     *
     * Because the pipes were created just before this driver was started
     * the file descriptors should not be above a closed descriptor
     * until it was run from a multithread application and some descriptors
     * were closed in the mean time.
     * Also Windows documentation does not say that new file descriptor is
     * the lowest available.
     */

    {
        int err_count = 0;
        int cfd = 3;

        while (1) {
            if (close(cfd) == -1)
                err_count++;

            /* no good reason for 10 */
            if (err_count > 10)
                break;

            cfd++;
        }
    }

    _setmode(_fileno(stdin), _O_BINARY);
    _setmode(_fileno(stdout), _O_BINARY);
#endif

    send = stdout;
    recv = stdin;

    /* THIS CODE IS FOR DEBUGGING WITH CODECENTER */

    /**********************************************/
    if (argc == 3) {
        rfd = wfd = -1;
        sscanf(argv[1], "%d", &rfd);
        sscanf(argv[2], "%d", &wfd);
        send = fdopen(wfd, "w");
        if (send == NULL) {
            db_syserror(argv[1]);
            exit(1);
        }
        recv = fdopen(rfd, "r");
        if (recv == NULL) {
            db_syserror(argv[2]);
            exit(1);
        }
    }

    /**********************************************/

    db_clear_error();
    db_auto_print_errors(1);
    db_auto_print_protocol_errors(1);
    db__init_driver_state();

#ifndef USE_BUFFERED_IO
    setbuf(recv, NULL);
    setbuf(send, NULL);
#endif
    db__set_protocol_fds(send, recv);

    if (db_driver_init(argc, argv) == DB_OK)
        db__send_success();
    else {
        db__send_failure();
        exit(1);
    }

    stat = DB_OK;
    /* get the procedure number */
    while (db__recv_procnum(&procnum) == DB_OK) {
        if (procnum == DB_PROC_SHUTDOWN_DRIVER) {
            db__send_procedure_ok(procnum);
            break;
        }
        db_clear_error();

        /* find this procedure */
        for (i = 0; procedure[i].routine; i++)
            if (procedure[i].procnum == procnum)
                break;

        /* if found, call it */
        if (procedure[i].routine) {
            if ((stat = db__send_procedure_ok(procnum)) != DB_OK)
                break; /* while loop */
            if ((stat = (*procedure[i].routine)()) != DB_OK)
                break;
        }
        else if ((stat = db__send_procedure_not_implemented(procnum)) != DB_OK)
            break;
    }

    db_driver_finish();

    exit(stat == DB_OK ? 0 : 1);
}
