/*
 * Graphics driver management functions
 *
 * Copyright 1996 Alexandre Julliard
 */

#include <string.h>
#include "gdi.h"
#include "heap.h"
#include "debugtools.h"

DEFAULT_DEBUG_CHANNEL(driver)

typedef struct tagGRAPHICS_DRIVER
{
    struct tagGRAPHICS_DRIVER *next;
    LPSTR                      name;
    const DC_FUNCTIONS        *funcs;
} GRAPHICS_DRIVER;

static GRAPHICS_DRIVER *firstDriver = NULL;
static GRAPHICS_DRIVER *genericDriver = NULL;

/**********************************************************************
 *	     DRIVER_RegisterDriver
 */
BOOL DRIVER_RegisterDriver( LPCSTR name, const DC_FUNCTIONS *funcs )
{
    GRAPHICS_DRIVER *driver = HeapAlloc( SystemHeap, 0, sizeof(*driver) );
    if (!driver) return FALSE;
    driver->funcs = funcs;
    if (name)
    {
        driver->name  = HEAP_strdupA( SystemHeap, 0, name );
        driver->next  = firstDriver;
        firstDriver = driver;
        return TRUE;
    }
    /* No name -> it's the generic driver */
    if (genericDriver)
    {
        WARN(" already a generic driver\n" );
        HeapFree( SystemHeap, 0, driver );
        return FALSE;
    }
    driver->name = NULL;
    genericDriver = driver;
    return TRUE;
}


/**********************************************************************
 *	     DRIVER_FindDriver
 */
const DC_FUNCTIONS *DRIVER_FindDriver( LPCSTR name )
{
    GRAPHICS_DRIVER *driver = firstDriver;

    TRACE(": %s\n", name);
    while (driver && name)
    {
        if (!strcasecmp( driver->name, name )) return driver->funcs;
        driver = driver->next;
    }
    return genericDriver ? genericDriver->funcs : NULL;
}


/**********************************************************************
 *	     DRIVER_UnregisterDriver
 */
BOOL DRIVER_UnregisterDriver( LPCSTR name )
{
    if (name)
    {
        GRAPHICS_DRIVER **ppDriver = &firstDriver;
        while (*ppDriver)
        {
            if (!strcasecmp( (*ppDriver)->name, name ))
            {
                GRAPHICS_DRIVER *driver = *ppDriver;
                (*ppDriver) = driver->next;
                HeapFree( SystemHeap, 0, driver->name );
                HeapFree( SystemHeap, 0, driver );
                return TRUE;
            }
            ppDriver = &(*ppDriver)->next;
        }
        return FALSE;
    }
    else
    {
        if (!genericDriver) return FALSE;
        HeapFree( SystemHeap, 0, genericDriver );
        genericDriver = NULL;
        return TRUE;
    }
}

/*****************************************************************************
 *      DRIVER_GetDriverName
 *
 */
BOOL DRIVER_GetDriverName( LPCSTR device, LPSTR driver, DWORD size )
{
    char *p;
    size = GetProfileStringA("devices", device, "", driver, size);
    if(!size) {
        WARN("Unable to find '%s' in [devices] section of win.ini\n", device);
	return FALSE;
    }
    p = strchr(driver, ',');
    if(!p) {
        WARN("'%s' entry in [devices] section of win.ini is malformed.\n",
	     device);
	return FALSE;
    }
    *p = '\0';
    TRACE("Found '%s' for '%s'\n", driver, device);
    return TRUE;
}
    
/*****************************************************************************
 *      GDI_CallDevInstall16   [GDI32.100]
 *
 * This should thunk to 16-bit and simply call the proc with the given args.
 */
INT WINAPI GDI_CallDevInstall16( FARPROC16 lpfnDevInstallProc, HWND hWnd, 
                                 LPSTR lpModelName, LPSTR OldPort, LPSTR NewPort )
{
    FIXME("(%p, %04x, %s, %s, %s)\n", 
	  lpfnDevInstallProc, hWnd, lpModelName, OldPort, NewPort );
    return -1;
}

/*****************************************************************************
 *      GDI_CallExtDeviceModePropSheet16   [GDI32.101]
 *
 * This should load the correct driver for lpszDevice and calls this driver's
 * ExtDeviceModePropSheet proc. 
 *
 * Note: The driver calls a callback routine for each property sheet page; these 
 * pages are supposed to be filled into the structure pointed to by lpPropSheet.
 * The layout of this structure is:
 * 
 * struct
 * {
 *   DWORD  nPages;
 *   DWORD  unknown;
 *   HPROPSHEETPAGE  pages[10];
 * };
 */
INT WINAPI GDI_CallExtDeviceModePropSheet16( HWND hWnd, LPCSTR lpszDevice, 
                                             LPCSTR lpszPort, LPVOID lpPropSheet )
{
    FIXME("(%04x, %s, %s, %p)\n", 
      hWnd, lpszDevice, lpszPort, lpPropSheet );
    return -1;
}

/*****************************************************************************
 *      GDI_CallExtDeviceMode16   [GDI32.102]
 *
 * This should load the correct driver for lpszDevice and calls this driver's
 * ExtDeviceMode proc.
 */
INT WINAPI GDI_CallExtDeviceMode16( HWND hwnd, 
                                    LPDEVMODEA lpdmOutput, LPSTR lpszDevice, 
                                    LPSTR lpszPort, LPDEVMODEA lpdmInput, 
                                    LPSTR lpszProfile, DWORD fwMode )
{
    char buf[300];
    const DC_FUNCTIONS *funcs;

    TRACE("(%04x, %p, %s, %s, %p, %s, %ld)\n", 
	  hwnd, lpdmOutput, lpszDevice, lpszPort, 
	  lpdmInput, lpszProfile, fwMode );

    if(!DRIVER_GetDriverName( lpszDevice, buf, sizeof(buf) )) return -1;
    funcs = DRIVER_FindDriver( buf );
    if(!funcs || !funcs->pExtDeviceMode) return -1;
    return funcs->pExtDeviceMode(hwnd, lpdmOutput, lpszDevice, lpszPort,
				 lpdmInput, lpszProfile, fwMode);
}

/****************************************************************************
 *      GDI_CallAdvancedSetupDialog16     [GDI32.103]
 *
 * This should load the correct driver for lpszDevice and calls this driver's
 * AdvancedSetupDialog proc.
 */
INT WINAPI GDI_CallAdvancedSetupDialog16( HWND hwnd, LPSTR lpszDevice,
                                          LPDEVMODEA devin, LPDEVMODEA devout )
{
    TRACE("(%04x, %s, %p, %p)\n", 
	  hwnd, lpszDevice, devin, devout );
    return -1;
}

/*****************************************************************************
 *      GDI_CallDeviceCapabilities16      [GDI32.104]
 *
 * This should load the correct driver for lpszDevice and calls this driver's
 * DeviceCapabilities proc.
 */
DWORD WINAPI GDI_CallDeviceCapabilities16( LPCSTR lpszDevice, LPCSTR lpszPort,
                                           WORD fwCapability, LPSTR lpszOutput,
                                           LPDEVMODEA lpdm )
{
    char buf[300];
    const DC_FUNCTIONS *funcs;

    TRACE("(%s, %s, %d, %p, %p)\n", 
	  lpszDevice, lpszPort, fwCapability, lpszOutput, lpdm );


    if(!DRIVER_GetDriverName( lpszDevice, buf, sizeof(buf) )) return -1;
    funcs = DRIVER_FindDriver( buf );
    if(!funcs || !funcs->pDeviceCapabilities) return -1;
    return funcs->pDeviceCapabilities( lpszDevice, lpszPort, fwCapability,
				       lpszOutput, lpdm);
}


