/*
 * TWAIN32 Source Manager
 *
 * Copyright 2000 Corel Corporation
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "config.h"

#include <stdlib.h>
#include <stdarg.h>

#define NONAMELESSUNION
#define NONAMELESSSTRUCT
#include "windef.h"
#include "winbase.h"
#include "twain.h"
#include "twain_i.h"
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(twain);

/* DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS */
TW_UINT16 TWAIN_CloseDS (pTW_IDENTITY pOrigin, TW_MEMREF pData)
{
#ifndef HAVE_SANE
    DSM_twCC = TWCC_NODS;
    return TWRC_FAILURE;
#else
    TW_UINT16 twRC = TWRC_SUCCESS;
    pTW_IDENTITY pIdentity = (pTW_IDENTITY) pData;
    activeDS *currentDS = NULL, *prevDS = NULL;

    TRACE ("DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS\n");

    for (currentDS = activeSources; currentDS; currentDS = currentDS->next)
    {
        if (currentDS->identity.Id == pIdentity->Id)
            break;
        prevDS = currentDS;
    }
    if (currentDS)
    {
        /* Only valid to close a data source if it is in state 4 */
        if (currentDS->currentState == 4)
        {
            sane_close (currentDS->deviceHandle);
            /* remove the data source from active data source list */
            if (prevDS)
                prevDS->next = currentDS->next;
            else
                activeSources = currentDS->next;
            HeapFree (GetProcessHeap(), 0, currentDS);
            twRC = TWRC_SUCCESS;
            DSM_twCC = TWCC_SUCCESS;
        }
        else
        {
            twRC = TWRC_FAILURE;
            DSM_twCC = TWCC_SEQERROR;
        }
    }
    else
    {
        twRC = TWRC_FAILURE;
        DSM_twCC = TWCC_NODS;
    }

    return twRC;
#endif
}

/* DG_CONTROL/DAT_IDENTITY/MSG_GETDEFAULT */
TW_UINT16 TWAIN_IdentityGetDefault (pTW_IDENTITY pOrigin, TW_MEMREF pData)
{
#ifndef HAVE_SANE
    DSM_twCC = TWCC_NODS;
    return TWRC_FAILURE;
#else
    TW_UINT16 twRC = TWRC_SUCCESS;
    pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) pData;

    TRACE("DG_CONTROL/DAT_IDENTITY/MSG_GETDEFAULT\n");

    if (!device_list)
    {
        if ((sane_get_devices (&device_list, SANE_FALSE) != SANE_STATUS_GOOD))
        {
            DSM_twCC = TWCC_NODS;
            return TWRC_FAILURE;
        }
    }

    /* FIXME: the default device is not necessarily the first device.  *
     * Users should be able to choose the default device               */
    if (device_list && device_list[0])
    {
        pSourceIdentity->Id = DSM_sourceId ++;
        strcpy (pSourceIdentity->ProductName, device_list[0]->name);
        strcpy (pSourceIdentity->Manufacturer, device_list[0]->vendor);
        strcpy (pSourceIdentity->ProductFamily, device_list[0]->model);
        pSourceIdentity->ProtocolMajor = TWON_PROTOCOLMAJOR;
        pSourceIdentity->ProtocolMinor = TWON_PROTOCOLMINOR;

        twRC = TWRC_SUCCESS;
        DSM_twCC = TWCC_SUCCESS;
    }
    else
    {
        twRC = TWRC_FAILURE;
        DSM_twCC = TWCC_NODS;
    }

    return twRC;
#endif
}

/* DG_CONTROL/DAT_IDENTITY/MSG_GETFIRST */
TW_UINT16 TWAIN_IdentityGetFirst (pTW_IDENTITY pOrigin, TW_MEMREF pData)
{
#ifndef HAVE_SANE
    DSM_twCC = TWCC_NODS;
    return TWRC_FAILURE;
#else
    TW_UINT16 twRC = TWRC_SUCCESS;
    pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) pData;
    SANE_Status status;

    TRACE ("DG_CONTROL/DAT_IDENTITY/MSG_GETFIRST\n");

    status = sane_get_devices (&device_list, SANE_FALSE);
    if (status == SANE_STATUS_GOOD)
    {
        if (device_list[0])
        {
            TRACE("got: %s, %s, %s\n", device_list[0]->name, device_list[0]->vendor, device_list[0]->model);
            pSourceIdentity->Id = DSM_sourceId ++;
            strcpy (pSourceIdentity->ProductName, device_list[0]->name);
            strcpy (pSourceIdentity->Manufacturer, device_list[0]->vendor);
            strcpy (pSourceIdentity->ProductFamily, device_list[0]->model);
            pSourceIdentity->ProtocolMajor = TWON_PROTOCOLMAJOR;
            pSourceIdentity->ProtocolMinor = TWON_PROTOCOLMINOR;

            DSM_currentDevice = 1;
            twRC = TWRC_SUCCESS;
            DSM_twCC = TWCC_SUCCESS;
        }
        else
        {
            TRACE("got empty device list\n");
            twRC = TWRC_FAILURE;
            DSM_twCC = TWCC_NODS;
        }
    }
    else if (status == SANE_STATUS_NO_MEM)
    {
        twRC = TWRC_FAILURE;
        DSM_twCC = TWCC_LOWMEMORY;
    }
    else
    {
        WARN("sane_get_devices() failed: %s\n", sane_strstatus (status));
        twRC = TWRC_FAILURE;
        DSM_twCC = TWCC_NODS;
    }

    return twRC;
#endif
}

/* DG_CONTROL/DAT_IDENTITY/MSG_GETNEXT */
TW_UINT16 TWAIN_IdentityGetNext (pTW_IDENTITY pOrigin, TW_MEMREF pData)
{
#ifndef HAVE_SANE
    DSM_twCC = TWCC_SUCCESS;
    return TWRC_ENDOFLIST;
#else
    TW_UINT16 twRC = TWRC_SUCCESS;
    pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) pData;

    TRACE("DG_CONTROL/DAT_IDENTITY/MSG_GETNEXT\n");

    if (device_list && device_list[DSM_currentDevice] &&
        device_list[DSM_currentDevice]->name &&
        device_list[DSM_currentDevice]->vendor &&
        device_list[DSM_currentDevice]->model)
    {
        pSourceIdentity->Id = DSM_sourceId ++;
        strcpy (pSourceIdentity->ProductName, device_list[DSM_currentDevice]->name);
        strcpy (pSourceIdentity->Manufacturer, device_list[DSM_currentDevice]->vendor);
        strcpy (pSourceIdentity->ProductFamily, device_list[DSM_currentDevice]->model);
        pSourceIdentity->ProtocolMajor = TWON_PROTOCOLMAJOR;
        pSourceIdentity->ProtocolMinor = TWON_PROTOCOLMINOR;
        DSM_currentDevice ++;

        twRC = TWRC_SUCCESS;
        DSM_twCC = TWCC_SUCCESS;
    }
    else
    {
        DSM_twCC = TWCC_SUCCESS;
        twRC = TWRC_ENDOFLIST;
    }

    return twRC;
#endif
}

/* DG_CONTROL/DAT_IDENTITY/MSG_OPENDS */
TW_UINT16 TWAIN_OpenDS (pTW_IDENTITY pOrigin, TW_MEMREF pData)
{
#ifndef HAVE_SANE
    DSM_twCC = TWCC_NODS;
    return TWRC_FAILURE;
#else
    TW_UINT16 twRC = TWRC_SUCCESS, i = 0;
    pTW_IDENTITY pIdentity = (pTW_IDENTITY) pData;
    activeDS *newSource;
    SANE_Status status;

    TRACE("DG_CONTROL/DAT_IDENTITY/MSG_OPENDS\n");

    if (DSM_currentState != 3)
    {
        DSM_twCC = TWCC_SEQERROR;
        return TWRC_FAILURE;
    }

    if (!device_list &&
       (sane_get_devices (&device_list, SANE_FALSE) != SANE_STATUS_GOOD))
    {
        DSM_twCC = TWCC_NODS;
        return TWRC_FAILURE;
    }

    if (pIdentity->ProductName[0] != '\0')
    {
        /* Make sure the source to be opened exists in the device list */
        for (i = 0; device_list[i]; i ++)
        {
            if (strcmp (device_list[i]->name, pIdentity->ProductName) == 0)
                break;
        }
    }

    if (device_list[i])
    {
        /* the source is found in the device list */
        newSource = HeapAlloc (GetProcessHeap(), 0, sizeof (activeDS));
        if (newSource)
        {
            status = sane_open(device_list[i]->name,&newSource->deviceHandle);
            if (status == SANE_STATUS_GOOD)
            {
                /* Assign name and id for the opened data source */
                strcpy (pIdentity->ProductName, device_list[i]->name);
                pIdentity->Id = DSM_sourceId ++;
                /* add the data source to an internal active source list */
                newSource->next = activeSources;
                newSource->identity.Id = pIdentity->Id;
                strcpy (newSource->identity.ProductName, pIdentity->ProductName);
                newSource->currentState = 4; /*transition into state 4*/
                newSource->twCC = TWCC_SUCCESS;
                activeSources = newSource;
                twRC = TWRC_SUCCESS;
                DSM_twCC = TWCC_SUCCESS;
            }
            else
            {
                twRC = TWRC_FAILURE;
                DSM_twCC = TWCC_OPERATIONERROR;
            }
        }
        else
        {
            twRC = TWRC_FAILURE;
            DSM_twCC = TWCC_LOWMEMORY;
        }
    }
    else
    {
        twRC = TWRC_FAILURE;
        DSM_twCC = TWCC_NODS;
    }

    return twRC;
#endif
}

/* DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT */
TW_UINT16 TWAIN_UserSelect (pTW_IDENTITY pOrigin, TW_MEMREF pData)
{
#ifndef HAVE_SANE
    return TWRC_SUCCESS;
#else
    TW_UINT16 twRC = TWRC_SUCCESS;

    TRACE("DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT\n");

    /* FIXME: we should replace xscanimage with our own  User Select UI */
    system("xscanimage");

    DSM_twCC = TWCC_SUCCESS;
    return twRC;
#endif
}

/* DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM */
TW_UINT16 TWAIN_CloseDSM (pTW_IDENTITY pOrigin, TW_MEMREF pData)
{
#ifndef HAVE_SANE
    return TWRC_FAILURE;
#else
    TW_UINT16 twRC = TWRC_SUCCESS;
    activeDS *currentDS = activeSources, *nextDS;

    TRACE("DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM\n");

    if (DSM_currentState == 3)
    {
        sane_exit ();
        DSM_initialized = FALSE;
        DSM_parentHWND = 0;
        DSM_currentState = 2;

        /* If there are data sources still open, close them now. */
        while (currentDS != NULL)
        {
            nextDS = currentDS->next;
            sane_close (currentDS->deviceHandle);
            HeapFree (GetProcessHeap(), 0, currentDS);
            currentDS = nextDS;
        }
        activeSources = NULL;
        DSM_twCC = TWCC_SUCCESS;
        twRC = TWRC_SUCCESS;
    }
    else
    {
        DSM_twCC = TWCC_SEQERROR;
        twRC = TWRC_FAILURE;
    }

    return twRC;
#endif
}

/* DG_CONTROL/DAT_PARENT/MSG_OPENDSM */
TW_UINT16 TWAIN_OpenDSM (pTW_IDENTITY pOrigin, TW_MEMREF pData)
{
#ifndef HAVE_SANE
    return TWRC_FAILURE;
#else
    TW_UINT16 twRC = TWRC_SUCCESS;
    SANE_Status status;
    SANE_Int version_code;

    TRACE("DG_CONTROL/DAT_PARENT/MSG_OPENDSM\n");

    if (DSM_currentState == 2)
    {
        if (!DSM_initialized)
        {
            DSM_initialized = TRUE;
            status = sane_init (&version_code, NULL);
            device_list = NULL;
            DSM_currentDevice = 0;
            DSM_sourceId = 0;
        }
        DSM_parentHWND = *(TW_HANDLE*)pData;
        DSM_currentState = 3; /* transition to state 3 */
        DSM_twCC = TWCC_SUCCESS;
        twRC = TWRC_SUCCESS;
    }
    else
    {
        /* operation invoked in invalid state */
        DSM_twCC = TWCC_SEQERROR;
        twRC = TWRC_FAILURE;
    }

    return twRC;
#endif
}

/* DG_CONTROL/DAT_STATUS/MSG_GET */
TW_UINT16 TWAIN_GetDSMStatus (pTW_IDENTITY pOrigin, TW_MEMREF pData)
{
    pTW_STATUS pSourceStatus = (pTW_STATUS) pData;

    TRACE ("DG_CONTROL/DAT_STATUS/MSG_GET\n");

    pSourceStatus->ConditionCode = DSM_twCC;
    DSM_twCC = TWCC_SUCCESS;  /* clear the condition code */

    return TWRC_SUCCESS;
}
