/*
 *  SCLIENT.C
 *
 *  This module contains functions associated with the client dialog box
 *  in SAMPLE.EXE.
 *
 *  Copyright (C) 1991 by Daytris.  All rights reserved.
 */

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#ifndef ZORTECH
#include <memory.h>
#endif
#include "dbmgr.h"
#include "sampledb.h"
#include "sample.h"


/************************************************
 * Local data
 ************************************************/

static CLIENT client;
static enum 
    {
    MODE_ADD,
    MODE_UPDATE,
    MODE_DELETE
    } eMode;
static BOOL bAddressChange;
static HANDLE hAddresses;


/************************************************
 * Function Declarations
 ************************************************/

BOOL AddClientDlg( HWND hWnd);
BOOL UpdateClientDlg( HWND hWnd);
BOOL DeleteClientDlg( HWND hWnd);
static BOOL GetSelectedClient( HWND hWnd, CLIENT *pClient, short *pIndexSel);
static BOOL AddClientAddresses( HWND hWnd);
static BOOL DeleteClientAddresses( HWND hWnd);
static BOOL FreeClientAddresses( HWND hWnd);
BOOL FAR PASCAL ClientProc( HWND hDlg, unsigned iMessage, WORD wParam,
    LONG lParam);
static void SetClientFields( HWND hDlg);
static BOOL GetClientFields( HWND hDlg);
static BOOL StoreAddressHandles( HWND hDlg);


/***************************************************************************
 * Function : AddClientDlg
 *
 * Purpose  : This function drives the add client dialog box.
 *
 * Returns  : TRUE  - client added
 *            FALSE - add aborted or error in add
 ***************************************************************************/
BOOL AddClientDlg( HANDLE hWnd)
    {
    short nStatus;
    DWORD dwStatus;
    FARPROC lpfnClientProc;

    /* Set static variables */
    eMode = MODE_ADD;
    bAddressChange = FALSE;

    /* Initialize the client structure */
    memset( &client, 0, sizeof( CLIENT));
    client.lClientNbr = setup.lNextClientNbr;

    /* Create an instance and open the Client window */
    lpfnClientProc = MakeProcInstance( ClientProc, hInst);
    nStatus = DialogBox( hInst, "client", hWnd, lpfnClientProc);
    FreeProcInstance( lpfnClientProc);

    /* User selected OK */
    if( nStatus == IDOK)
        {
        /* Add the client */
        if( dwStatus = XDbRecordAdd( hDb, "client", &client,
            sizeof( CLIENT)) )
            {
            DbError( hWnd, dwStatus, __FILE__, __LINE__);
            return FALSE;
            }

        /* Add the member addresses */
        if( bAddressChange)
            {
            if( ! AddClientAddresses( hWnd))
                return FALSE;
            }

        /* Increment the client number counter and update the setup
           record */
        setup.lNextClientNbr++;
        if( dwStatus = XDbRecordUpdate( hDb, "setup", &setup,
            sizeof( SETUP)))
            {
            DbError( hWnd, dwStatus, __FILE__, __LINE__);
            return FALSE;
            }

        /* Flush the database */
        DbFlush( hDb);

        /* Add the client to the list box */
        AddToClientListBox( hWnd, &client);
        }
    else    /* IDCANCEL */
        {
        /* Free all address handles for this client */
        if( ! FreeClientAddresses( hWnd))
            return FALSE;
        }

    return TRUE;
    }


/***************************************************************************
 * Function : UpdateClientDlg
 *
 * Purpose  : This function drives the add client dialog box.
 *
 * Returns  : TRUE  - client added
 *            FALSE - update aborted or error in update
 ***************************************************************************/
BOOL UpdateClientDlg( HANDLE hWnd)
    {
    short nStatus, nIndex;
    DWORD dwStatus;
    FARPROC lpfnClientProc;

    /* Set the mode */
    eMode = MODE_UPDATE;

    /* Initialize the client structure */
    if( ! GetSelectedClient( hWnd, &client, &nIndex))
        {
        MessageBeep( 0);
        return FALSE;
        }

    /* Create an instance and open the Client window */
    lpfnClientProc = MakeProcInstance( ClientProc, hInst);
    nStatus = DialogBox( hInst, "client", hWnd, lpfnClientProc);
    FreeProcInstance( lpfnClientProc);

    /* User selected OK */
    if( nStatus == IDOK)
        {
        /* Update the client */
        if( dwStatus = XDbRecordUpdate( hDb, "client", &client,
            sizeof( CLIENT)) )
            {
            DbError( hWnd, dwStatus, __FILE__, __LINE__);
            return FALSE;
            }

        /* If any address has changed, delete all member addresses and
           add addresses in handle table. */
        if( bAddressChange)
            {
            if( ! DeleteClientAddresses( hWnd))
                return FALSE;
            if( ! AddClientAddresses( hWnd))
                return FALSE;
            }

        /* Flush the database */
        DbFlush( hDb);

        /* Update the listbox */
        DeleteFromClientListBox( hWnd, nIndex);
        AddToClientListBox( hWnd, &client);
        }
    else    /* IDCANCEL */
        {
        /* Free all address handles for this client */
        if( ! FreeClientAddresses( hWnd))
            return FALSE;
        }

    return TRUE;
    }


/***************************************************************************
 * Function : DeleteClientDlg
 *
 * Purpose  : This function drives the client record deletion process.
 *
 * Returns  : TRUE  - client deleted
 *            FALSE - delete aborted or error in delete
 ***************************************************************************/
BOOL DeleteClientDlg( HWND hWnd)
    {
    short nStatus, nIndex;
    DWORD dwStatus;

    /* Initialize the client structure */
    if( ! GetSelectedClient( hWnd, &client, &nIndex))
        {
        MessageBeep( 0);
        return FALSE;
        }

    /* Ask user if they're sure */
    nStatus = MessageBox( hWnd, "Are you sure?", "Delete Client",
        MB_ICONQUESTION | MB_YESNO);

    /* User selected YES */
    if( nStatus == IDYES)
        {
        /* Delete all member addresses */
        if( ! DeleteClientAddresses( hWnd))
            return FALSE;

        /* Delete the client */
        if( dwStatus = DbRecordDelete( hDb, "client"))
            {
            DbError( hWnd, dwStatus, __FILE__, __LINE__);
            return FALSE;
            }

        /* Flush the database */
        DbFlush( hDb);

        /* Remove client from listbox */
        if( ! DeleteFromClientListBox( hWnd, nIndex))
            return FALSE;
        }

    return TRUE;
    }


/***************************************************************************
 * Function : GetSelectedClient
 *
 * Purpose  : This function retrieves the selected client record from
 *            the database.  It uses the client number in the string
 *            of the listbox as a key value to retrieve upon.  The
 *            listbox index is also returned.
 *
 * Returns  : TRUE  - client retrieved
 *            FALSE - error
 ***************************************************************************/
static BOOL GetSelectedClient( HWND hWnd, CLIENT *pClient, short *pIndex)
    {
    LONG lKey;
    DWORD dwStatus;
    char szBuffer[64], *pTemp;

    /* Get the listbox selection */
    if( (*pIndex = (short)SendMessage( hWndClientLB, LB_GETCURSEL, 0, 0L))
        == LB_ERR)
        {
        return FALSE;
        }
    if( SendMessage( hWndClientLB, LB_GETTEXT, *pIndex,
        (LONG)(LPSTR)szBuffer) == LB_ERR)
        {
        MessageBox( hWnd, "SendMessage / LB_GETTEXT", "Fatal Error",
            MB_ICONEXCLAMATION | MB_OK);
        return FALSE;
        }

    /* Find the client number in the selected string */
    if( bSortByNumber)
        pTemp = strtok( szBuffer, " ");
    else
        {
        pTemp = strchr( szBuffer, 0);
        for( pTemp-- ; *pTemp != ' ' ; pTemp--)
            ;
        pTemp++;
        }
    lKey = atol( pTemp);

    /* Retrieve the record */
    if( dwStatus = XDbRecordGetByKey( hDb, "client", "lClientNbr", pClient,
        sizeof( CLIENT), &lKey, sizeof( LONG)))
        {
        DbError( hWnd, dwStatus, __FILE__, __LINE__);
        return FALSE;
        }

    return TRUE;
    }


/***************************************************************************
 * Function : AddClientAddresses
 *
 * Purpose  : This function adds the addresses associated with the client
 *            record.  A base handle, 'hAddresses', contains a null
 *            terminated array of address handles (records) to be added.
 *
 * Returns  : TRUE  - add ok
 *            FALSE - error in add
 ***************************************************************************/
static BOOL AddClientAddresses( HWND hWnd)
    {
    HANDLE FAR *lpAddressHandle;
    DWORD dwStatus;

    /* If no addresses, return */
    if( ! hAddresses)
        return TRUE;

    /* Get a pointer to the array of address handles */
    if( ! (lpAddressHandle = (HANDLE FAR *)GlobalLock( hAddresses)))
        {
        MessageBox( hWnd, "Memory: GlobalLock", "Fatal Error",
            MB_ICONEXCLAMATION | MB_OK);
        return FALSE;
        }

    for( ; *lpAddressHandle ; lpAddressHandle++)
        {
        /* Add the address record */
        if( dwStatus = DbRecordAdd( hDb, "address", *lpAddressHandle))
            {
            DbError( hWnd, dwStatus, __FILE__, __LINE__);
            return FALSE;
            }
        /* Make a set connection between the client and address.  The
           address record will be a member of the client. */
        if( dwStatus = DbSetAdd( hDb, "client", "address"))
            {
            DbError( hWnd, dwStatus, __FILE__, __LINE__);
            return FALSE;
            }

        /* Free the address handle */
        if( GlobalFree( *lpAddressHandle))
            {
            MessageBox( hWnd, "Memory: GlobalFree", "Fatal Error",
                MB_ICONEXCLAMATION | MB_OK);
            return FALSE;
            }
        }

    /* Unlock and free the array of address handles */
    GlobalUnlock( hAddresses);
    if( GlobalFree( hAddresses))
        {
        MessageBox( hWnd, "Memory: GlobalFree", "Fatal Error",
            MB_ICONEXCLAMATION | MB_OK);
        return FALSE;
        }

    return TRUE;
    }


/***************************************************************************
 * Function : DeleteClientAddresses
 *
 * Purpose  : This function deletes all address records that are members
 *            of the selected client.
 *
 * Returns  : TRUE  - delete ok
 *            FALSE - error in delete
 ***************************************************************************/
static BOOL DeleteClientAddresses( HWND hWnd)
    {
    DWORD dwStatus;

    while( 1)
        {
        /* Set currency to first member record in the set */
        dwStatus = DbSetFindFirst( hDb, "client", "address");

        /* Check for errors */
        if( dwStatus == E_NOTFOUND)
            break;
        if( dwStatus)
            {
            DbError( hWnd, dwStatus, __FILE__, __LINE__);
            return FALSE;
            }

        /* IMPORTANT: All set connections must be deleted before the member
           record of a set is deleted */

        /* Delete the set connection between client (owner) and address
           (member) */
        if( dwStatus = DbSetDelete( hDb, "client", "address"))
            {
            DbError( hWnd, dwStatus, __FILE__, __LINE__);
            return FALSE;
            }

        /* Delete the address record */
        if( dwStatus = DbRecordDelete( hDb, "address"))
            {
            DbError( hWnd, dwStatus, __FILE__, __LINE__);
            return FALSE;
            }
        }

    return TRUE;
    }


/***************************************************************************
 * Function : FreeClientAddresses
 *
 * Purpose  : This function frees all addresses associated with the
 *            selected client.  'hAddresses' contains a handle to
 *            a null terminated array of address handles in the listbox.
 *            
 * Returns  : TRUE  - free ok
 *            FALSE - error in free
 ***************************************************************************/
static BOOL FreeClientAddresses( HWND hWnd)
    {
    HANDLE FAR *lpAddressHandle;

    /* If no addresses, return */
    if( ! hAddresses)
        return TRUE;

    /* Get a pointer to the array of address handles */
    if( ! (lpAddressHandle = (HANDLE FAR *)GlobalLock( hAddresses)))
        {
        MessageBox( hWnd, "Memory: GlobalLock", "Fatal Error",
            MB_ICONEXCLAMATION | MB_OK);
        return FALSE;
        }

    for( ; *lpAddressHandle ; lpAddressHandle++)
        {
        /* Free the address handle */
        if( GlobalFree( *lpAddressHandle))
            {
            MessageBox( hWnd, "Memory: GlobalFree", "Fatal Error",
                MB_ICONEXCLAMATION | MB_OK);
            return FALSE;
            }
        }

    /* Unlock and free the array of address handles */
    GlobalUnlock( hAddresses);
    if( GlobalFree( hAddresses))
        {
        MessageBox( hWnd, "Memory: GlobalFree", "Fatal Error",
            MB_ICONEXCLAMATION | MB_OK);
        return FALSE;
        }

    return TRUE;
    }


/***************************************************************************
 * Function : ClientProc
 *
 * Purpose  : This function is the window procedure for 'add' and 'update'
 *            client.
 *
 * Returns  : TRUE  - message processed
 *            FALSE - message not processed
 ***************************************************************************/
BOOL FAR PASCAL ClientProc( HWND hDlg, unsigned iMessage, WORD wParam,
    LONG lParam)
    {
    switch( iMessage)
        {
        case WM_INITDIALOG:
            {
            short nTab;

            if( eMode == MODE_ADD)
                SetWindowText( hDlg, "Add Client");
            else
                SetWindowText( hDlg, "Update Client");

            /* Set a tab stop in the listbox out of sight.  The handle
               to the ADDRESS structure will be stored here (in ASCII) */
            nTab = (LOWORD( GetDialogBaseUnits()) * 100) / 4;
            SendDlgItemMessage( hDlg, IDC_ADDR_LISTBOX, LB_SETTABSTOPS, 1,
                (LONG)(LPSTR)&nTab);

            SetClientFields( hDlg);

            /* If update mode, load the address listbox */
            if( eMode == MODE_UPDATE)
                LoadAddressListBox( hDlg);

            /* Set focus to Name field */
            SetFocus( GetDlgItem( hDlg, IDC_EDIT_NAME));

            return TRUE;
            }

        case WM_COMMAND:
            switch( wParam)
                {
                case IDOK:
                    if( ! GetClientFields( hDlg))
                        break;
                    StoreAddressHandles( hDlg);
                    EndDialog( hDlg, IDOK);
                    break;

                case IDCANCEL:
                    StoreAddressHandles( hDlg);
                    EndDialog( hDlg, IDCANCEL);
                    break;

                case IDC_ADD_ADDR:
                    if( AddAddressDlg( hDlg))
                        bAddressChange = TRUE;
                    break;

                case IDC_UPDATE_ADDR:
                    if( UpdateAddressDlg( hDlg))
                        bAddressChange = TRUE;
                    break;

                case IDC_DELETE_ADDR:
                    if( DeleteAddressDlg( hDlg))
                        bAddressChange = TRUE;
                    break;
                }
            return TRUE;
        }

    return FALSE;
    }


/***************************************************************************
 * Function : SetClientFields
 *
 * Purpose  : This function initializes the client dialog fields.  It
 *            uses the static 'client' structure.
 *
 * Returns  : n/a
 ***************************************************************************/
static void SetClientFields( HWND hDlg)
    {
    char szBuffer[36];

    /* Set client number */
    ltoa( client.lClientNbr, szBuffer, 10);
    SetDlgItemText( hDlg, IDC_EDIT_NUMBER , szBuffer);

    /* Set client name */
    SendDlgItemMessage( hDlg, IDC_EDIT_NUMBER, EM_LIMITTEXT,
        sizeof( client.szName) - 1, 0L);
    SetDlgItemText( hDlg, IDC_EDIT_NAME, client.szName);

    /* Set client description */
    SendDlgItemMessage( hDlg, IDC_EDIT_DESC, EM_LIMITTEXT,
        sizeof( client.szDescription) - 1, 0L);
    SetDlgItemText( hDlg, IDC_EDIT_DESC, client.szDescription);

    /* Set client balance */
    sprintf( szBuffer, "%.2f", client.dBalance);
    SendDlgItemMessage( hDlg, IDC_EDIT_BALANCE, EM_LIMITTEXT, 10, 0L);
    SetDlgItemText( hDlg, IDC_EDIT_BALANCE, szBuffer);
    }


/***************************************************************************
 * Function : GetClientFields
 *
 * Purpose  : This function retrieves the client dialog fields.  It
 *            also performs minor field error checking.  Client fields
 *            are stored in the static 'client' structure.
 *
 * Returns  : TRUE  - fields ok
 *            FALSE - error in field (focus is set)
 ***************************************************************************/
static BOOL GetClientFields( HWND hDlg)
    {
    char szBuffer[36];

    GetDlgItemText( hDlg, IDC_EDIT_NAME, client.szName,
        sizeof( client.szName));
    GetDlgItemText( hDlg, IDC_EDIT_DESC, client.szDescription,
        sizeof( client.szDescription));
    GetDlgItemText( hDlg, IDC_EDIT_BALANCE, szBuffer, sizeof( szBuffer));
    client.dBalance = atof( szBuffer);

    /* Name field required */
    if( ! *client.szName)
        {
        MessageBox( hDlg, "Name required", "Invalid Input",
            MB_APPLMODAL | MB_ICONEXCLAMATION | MB_OK);
        /* Set focus to Name field */
        SetFocus( GetDlgItem( hDlg, IDC_EDIT_NAME));

        return FALSE;
        }

    return TRUE;
    }


/***************************************************************************
 * Function : StoreAddressFields
 *
 * Purpose  : This function stores all the address handles associated
 *            with a client in a null-terminated array.  'hAddresses'
 *            contains a handle to the array.
 *
 * Returns  : TRUE  - address handles stored
 *            FALSE - error
 ***************************************************************************/
static BOOL StoreAddressHandles( HWND hDlg)
    {
    short nCount, i;
    HANDLE FAR *lpAddressHandle;

    /* Set handle to address handle array to NULL */
    hAddresses = NULL;

    /* Get the number of addresses in listbox */
    nCount = (short)SendMessage( GetDlgItem( hDlg, IDC_ADDR_LISTBOX), LB_GETCOUNT,
        0, 0L);
    if( ! nCount)
        return TRUE;

    /* Allocate space to store address handles.  Leave last entry NULL
       to trigger end of addresses. */
    if( ! (hAddresses = GlobalAlloc( GMEM_MOVEABLE | GMEM_ZEROINIT,
        (DWORD)(sizeof( HANDLE) * (nCount + 1)))) )
        {
        MessageBox( hDlg, "Out of memory", "Fatal Error",
            MB_ICONEXCLAMATION | MB_OK);
        return FALSE;
        }

    /* Lock handle */
    if( ! (lpAddressHandle = (HANDLE FAR *)GlobalLock( hAddresses)) )
        {
        MessageBox( hDlg, "Memory: GlobalLock", "Fatal Error",
            MB_ICONEXCLAMATION | MB_OK);
        return FALSE;
        }

    /* Get each address handle and store it */
    for( i=0 ; i<nCount ; i++)
        {
        *lpAddressHandle = GetAddressHandle( hDlg, i);
        if( ! *lpAddressHandle)
            return FALSE;
        lpAddressHandle++;
        }

    /* Unlock handle */
    GlobalUnlock( hAddresses);

    return TRUE;
    }
