/* Module:          setup.c
 *
 * Description:     This module contains the setup functions for 
 *                  adding/modifying a Data Source in the ODBC.INI portion
 *                  of the registry.
 *
 * Classes:         n/a
 *
 * API functions:   ConfigDSN
 *
 * Comments:        See "notice.txt" for copyright and license information.
 *
 *************************************************************************************/

#include  "psqlodbc.h"
#include  "connection.h"
#include  <windows.h>
#include  <windowsx.h>
#include  <odbcinst.h>
#include  <string.h>
#include  <stdlib.h>
#include  "resource.h"
#include  "dlg_specific.h"


#define INTFUNC  __stdcall

extern HINSTANCE NEAR s_hModule;               /* Saved module handle. */
extern GLOBAL_VALUES globals;

// Constants ---------------------------------------------------------------
#define MIN(x,y)      ((x) < (y) ? (x) : (y))

#ifdef WIN32
#define MAXPGPATH       (255+1)
#endif

#define MAXKEYLEN       (15+1)            // Max keyword length
#define MAXDESC         (255+1)           // Max description length
#define MAXDSNAME       (32+1)            // Max data source name length


// Globals -----------------------------------------------------------------
// NOTE:  All these are used by the dialog procedures
typedef struct tagSETUPDLG {
        HWND       hwndParent;                   // Parent window handle
        LPCSTR     lpszDrvr;                     // Driver description
		ConnInfo   ci;
        char       szDSN[MAXDSNAME];             // Original data source name
        BOOL       fNewDSN;                      // New data source flag
        BOOL       fDefault;                     // Default data source flag

} SETUPDLG, FAR *LPSETUPDLG;



// Prototypes --------------------------------------------------------------
void INTFUNC CenterDialog(HWND hdlg);
int  CALLBACK ConfigDlgProc(HWND hdlg, WORD wMsg, WPARAM wParam, LPARAM lParam);
void INTFUNC ParseAttributes (LPCSTR lpszAttributes, LPSETUPDLG lpsetupdlg);
BOOL INTFUNC SetDSNAttributes(HWND hwnd, LPSETUPDLG lpsetupdlg);


/* ConfigDSN ---------------------------------------------------------------
  Description:  ODBC Setup entry point
                This entry point is called by the ODBC Installer
                (see file header for more details)
  Input      :  hwnd ----------- Parent window handle
                fRequest ------- Request type (i.e., add, config, or remove)
                lpszDriver ----- Driver name
                lpszAttributes - data source attribute string
  Output     :  TRUE success, FALSE otherwise
--------------------------------------------------------------------------*/

BOOL CALLBACK ConfigDSN (HWND    hwnd,
                         WORD    fRequest,
                         LPCSTR  lpszDriver,
                         LPCSTR  lpszAttributes)
{
BOOL  fSuccess;                                            // Success/fail flag
GLOBALHANDLE hglbAttr;
LPSETUPDLG lpsetupdlg;
        

        // Allocate attribute array
        hglbAttr = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(SETUPDLG));
        if (!hglbAttr)
                return FALSE;
        lpsetupdlg = (LPSETUPDLG)GlobalLock(hglbAttr);

        // Parse attribute string
        if (lpszAttributes)
                ParseAttributes(lpszAttributes, lpsetupdlg);

        // Save original data source name
        if (lpsetupdlg->ci.dsn[0])
                lstrcpy(lpsetupdlg->szDSN, lpsetupdlg->ci.dsn);
        else
                lpsetupdlg->szDSN[0] = '\0';

        // Remove data source
        if (ODBC_REMOVE_DSN == fRequest) {
                // Fail if no data source name was supplied
                if (!lpsetupdlg->ci.dsn[0])
                        fSuccess = FALSE;

                // Otherwise remove data source from ODBC.INI
                else
                        fSuccess = SQLRemoveDSNFromIni(lpsetupdlg->ci.dsn);
        }

        // Add or Configure data source
        else {
                // Save passed variables for global access (e.g., dialog access)
                lpsetupdlg->hwndParent = hwnd;
                lpsetupdlg->lpszDrvr     = lpszDriver;
                lpsetupdlg->fNewDSN      = (ODBC_ADD_DSN == fRequest);
                lpsetupdlg->fDefault     = !lstrcmpi(lpsetupdlg->ci.dsn, INI_DSN);

                // Display the appropriate dialog (if parent window handle supplied)
                if (hwnd) {
                        // Display dialog(s)
                          fSuccess = (IDOK == DialogBoxParam(s_hModule,
                                                                MAKEINTRESOURCE(DLG_CONFIG),
                                                                hwnd,
                                                                ConfigDlgProc,
                                                                (LONG)(LPSTR)lpsetupdlg));
                }

                else if (lpsetupdlg->ci.dsn[0])
                        fSuccess = SetDSNAttributes(hwnd, lpsetupdlg);
                else
                        fSuccess = FALSE;
        }

        GlobalUnlock(hglbAttr);
        GlobalFree(hglbAttr);

        return fSuccess;
}


/* CenterDialog ------------------------------------------------------------
        Description:  Center the dialog over the frame window
        Input      :  hdlg -- Dialog window handle
        Output     :  None
--------------------------------------------------------------------------*/
void INTFUNC CenterDialog(HWND hdlg)
{
        HWND    hwndFrame;
        RECT    rcDlg, rcScr, rcFrame;
        int             cx, cy;

        hwndFrame = GetParent(hdlg);

        GetWindowRect(hdlg, &rcDlg);
        cx = rcDlg.right  - rcDlg.left;
        cy = rcDlg.bottom - rcDlg.top;

        GetClientRect(hwndFrame, &rcFrame);
        ClientToScreen(hwndFrame, (LPPOINT)(&rcFrame.left));
        ClientToScreen(hwndFrame, (LPPOINT)(&rcFrame.right));
        rcDlg.top    = rcFrame.top  + (((rcFrame.bottom - rcFrame.top) - cy) >> 1);
        rcDlg.left   = rcFrame.left + (((rcFrame.right - rcFrame.left) - cx) >> 1);
        rcDlg.bottom = rcDlg.top  + cy;
        rcDlg.right  = rcDlg.left + cx;

        GetWindowRect(GetDesktopWindow(), &rcScr);
        if (rcDlg.bottom > rcScr.bottom)
        {
                rcDlg.bottom = rcScr.bottom;
                rcDlg.top    = rcDlg.bottom - cy;
        }
        if (rcDlg.right  > rcScr.right)
        {
                rcDlg.right = rcScr.right;
                rcDlg.left  = rcDlg.right - cx;
        }

        if (rcDlg.left < 0) rcDlg.left = 0;
        if (rcDlg.top  < 0) rcDlg.top  = 0;

        MoveWindow(hdlg, rcDlg.left, rcDlg.top, cx, cy, TRUE);
        return;
}

/* ConfigDlgProc -----------------------------------------------------------
  Description:  Manage add data source name dialog
  Input      :  hdlg --- Dialog window handle
                wMsg --- Message
                wParam - Message parameter
                lParam - Message parameter
  Output     :  TRUE if message processed, FALSE otherwise
--------------------------------------------------------------------------*/


int CALLBACK ConfigDlgProc(HWND   hdlg,
                           WORD   wMsg,
                           WPARAM wParam,
                           LPARAM lParam)
{

	switch (wMsg) {
	// Initialize the dialog
	case WM_INITDIALOG:
	{
		LPSETUPDLG lpsetupdlg = (LPSETUPDLG) lParam;
		ConnInfo *ci = &lpsetupdlg->ci;

		/*	Hide the driver connect message */
		ShowWindow(GetDlgItem(hdlg, DRV_MSG_LABEL), SW_HIDE);

		SetWindowLong(hdlg, DWL_USER, lParam);
		CenterDialog(hdlg);                             // Center dialog

		// NOTE: Values supplied in the attribute string will always
		//       override settings in ODBC.INI

		//	Get the rest of the common attributes
		getDSNinfo(ci, CONN_DONT_OVERWRITE);

		//	Fill in any defaults
		getDSNdefaults(ci);


		// Initialize dialog fields
		SetDlgStuff(hdlg, ci);


		if (lpsetupdlg->fDefault) {
			EnableWindow(GetDlgItem(hdlg, IDC_DSNAME), FALSE);
			EnableWindow(GetDlgItem(hdlg, IDC_DSNAMETEXT), FALSE);
		}
		else
			SendDlgItemMessage(hdlg, IDC_DSNAME,
				EM_LIMITTEXT, (WPARAM)(MAXDSNAME-1), 0L);

		SendDlgItemMessage(hdlg, IDC_DESC,
			EM_LIMITTEXT, (WPARAM)(MAXDESC-1), 0L);
		return TRUE;                                            // Focus was not set
    }


	// Process buttons
	case WM_COMMAND:

		switch (GET_WM_COMMAND_ID(wParam, lParam)) {
		// Ensure the OK button is enabled only when a data source name
		// is entered
		case IDC_DSNAME:
			if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE)
			{
				char    szItem[MAXDSNAME];              // Edit control text

				// Enable/disable the OK button
				EnableWindow(GetDlgItem(hdlg, IDOK),
					GetDlgItemText(hdlg, IDC_DSNAME,
					szItem, sizeof(szItem)));

				return TRUE;
			}
			break;

		// Accept results
		case IDOK:
		{
			LPSETUPDLG lpsetupdlg;

			lpsetupdlg = (LPSETUPDLG)GetWindowLong(hdlg, DWL_USER);
			// Retrieve dialog values
			if (!lpsetupdlg->fDefault)
					GetDlgItemText(hdlg, IDC_DSNAME,
							lpsetupdlg->ci.dsn,
							sizeof(lpsetupdlg->ci.dsn));


			//	Get Dialog Values
			GetDlgStuff(hdlg, &lpsetupdlg->ci);

			// Update ODBC.INI
			SetDSNAttributes(hdlg, lpsetupdlg);
        }

		// Return to caller
		case IDCANCEL:
			EndDialog(hdlg, wParam);
			return TRUE;

		case IDC_DRIVER:

			DialogBoxParam(s_hModule, MAKEINTRESOURCE(DLG_OPTIONS_DRV),
									hdlg, driver_optionsProc, (LPARAM) NULL);

			return TRUE;

		case IDC_DATASOURCE:
			{
			LPSETUPDLG lpsetupdlg;

			lpsetupdlg = (LPSETUPDLG)GetWindowLong(hdlg, DWL_USER);

			DialogBoxParam(s_hModule, MAKEINTRESOURCE(DLG_OPTIONS_DS),
									hdlg, ds_optionsProc, (LPARAM) &lpsetupdlg->ci);

			return TRUE;
			}
		}

		break;
	}

	// Message not processed
	return FALSE;
}


/* ParseAttributes ---------------------------------------------------------
  Description:  Parse attribute string moving values into the aAttr array
  Input      :  lpszAttributes - Pointer to attribute string
  Output     :  None (global aAttr normally updated)
--------------------------------------------------------------------------*/
void INTFUNC ParseAttributes(LPCSTR lpszAttributes, LPSETUPDLG lpsetupdlg)
{
LPCSTR  lpsz;
LPCSTR  lpszStart;
char    aszKey[MAXKEYLEN];
int     cbKey;
char    value[MAXPGPATH];

		memset(&lpsetupdlg->ci, 0, sizeof(ConnInfo));

        for (lpsz=lpszAttributes; *lpsz; lpsz++)
        {  //  Extract key name (e.g., DSN), it must be terminated by an equals
                lpszStart = lpsz;
                for (;; lpsz++)
                {
                        if (!*lpsz)
                                return;         // No key was found
                        else if (*lpsz == '=')
                                break;          // Valid key found
                }
                // Determine the key's index in the key table (-1 if not found)
                cbKey    = lpsz - lpszStart;
                if (cbKey < sizeof(aszKey))
                {

                        _fmemcpy(aszKey, lpszStart, cbKey);
                        aszKey[cbKey] = '\0';
                }

                // Locate end of key value
                lpszStart = ++lpsz;
                for (; *lpsz; lpsz++);


                // lpsetupdlg->aAttr[iElement].fSupplied = TRUE;
                _fmemcpy(value, lpszStart, MIN(lpsz-lpszStart+1, MAXPGPATH));

				mylog("aszKey='%s', value='%s'\n", aszKey, value);

				//	Copy the appropriate value to the conninfo 
				copyAttributes(&lpsetupdlg->ci, aszKey, value);
        }
        return;
}


/* SetDSNAttributes --------------------------------------------------------
  Description:  Write data source attributes to ODBC.INI
  Input      :  hwnd - Parent window handle (plus globals)
  Output     :  TRUE if successful, FALSE otherwise
--------------------------------------------------------------------------*/

BOOL INTFUNC SetDSNAttributes(HWND hwndParent, LPSETUPDLG lpsetupdlg)
{
LPCSTR  lpszDSN;                                                // Pointer to data source name
    
        lpszDSN = lpsetupdlg->ci.dsn;

        // Validate arguments
        if (lpsetupdlg->fNewDSN && !*lpsetupdlg->ci.dsn)
                return FALSE;

        // Write the data source name
        if (!SQLWriteDSNToIni(lpszDSN, lpsetupdlg->lpszDrvr))
        {
                if (hwndParent)
                {
                        char  szBuf[MAXPGPATH];
                        char  szMsg[MAXPGPATH];

                        LoadString(s_hModule, IDS_BADDSN, szBuf, sizeof(szBuf));
                        wsprintf(szMsg, szBuf, lpszDSN);
                        LoadString(s_hModule, IDS_MSGTITLE, szBuf, sizeof(szBuf));
                        MessageBox(hwndParent, szMsg, szBuf, MB_ICONEXCLAMATION | MB_OK);
                }
                return FALSE;
        }


        // Update ODBC.INI
		writeDSNinfo(&lpsetupdlg->ci);


        // If the data source name has changed, remove the old name
        if (lstrcmpi(lpsetupdlg->szDSN, lpsetupdlg->ci.dsn))
        {
                SQLRemoveDSNFromIni(lpsetupdlg->szDSN);
        }
        return TRUE;
}