/*++

    Copyright (c) 1994  Microsoft Corporation

Module Name:

    COPYFLOP.C

Abstract:

    Floppy disk creation setup dialog

Author:

    Bob Watson (a-robw)

Revision History:

    17 Feb 94    Written

--*/
//
//  Windows Include Files
//

#include <windows.h>
#include <stdio.h>
#include <malloc.h>
#include <tchar.h>      // unicode macros
//
//  app include files
//
#include "otnboot.h"
#include "otnbtdlg.h"

//
//  static data for this module
//
//
//  mszDirNameList is the local list of directories that corresponds to the
//  items displayed in the Client List. This buffer is loaded by the
//  LoadClientList routine and the entry index of the directory is the
//  Item Data for the list box name entry.
//
static  TCHAR   mszDirNameList[SMALL_BUFFER_SIZE];
//
//  These string arrays are used to cross reference the drive selected
//   to the "drive name" for IOCTL functions or the Dir name for file
//  operations.
//
static  LPCTSTR  szDriveNames[2] = {TEXT("\\\\.\\A:"), TEXT("\\\\.\\B:")};
static  LPCTSTR  szDriveDirs[2] = {TEXT("A:"), TEXT("B:")};

static
MEDIA_TYPE
GetDriveTypeFromList (
    IN  HWND    hwndDlg
)
/*++

Routine Description:

    Gets the selected drive from the drive list box and looks up the drive
        type using the IOCTL function. The drive type enum value is
        returned.

    NOTE: This function may cause a system error box if there is no media
    in the drive or there is a device/drive error.

Arguments:

    IN  HWND    hwndDlg
        handle to the dialog box window

Return Value:

    value of type MEDIA_TYPE that corresponds to the drive type of the
        drive selected in the drive list box.

--*/
{
    int nSelIndex;
    HANDLE  hFloppy;
    DWORD   dwRetSize;
    DISK_GEOMETRY   dgFloppy;

    // get selected drive
    nSelIndex = SendDlgItemMessage (hwndDlg, NCDU_DRIVE_LIST,
        LB_GETCURSEL, 0, 0);

    // open device to get type
    hFloppy = CreateFile (
        szDriveNames[nSelIndex],
        0,
        (FILE_SHARE_READ | FILE_SHARE_WRITE),
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL);

    if (hFloppy != INVALID_HANDLE_VALUE) {
        // get drive information
        if (!DeviceIoControl (hFloppy,
            IOCTL_DISK_GET_DRIVE_GEOMETRY,
            NULL, 0,
            &dgFloppy,
            sizeof(DISK_GEOMETRY),
            &dwRetSize,
            NULL) ){
            // unable to get data so set to unknown
            dgFloppy.MediaType = Unknown;
        } // else return data from returned structure
        CloseHandle (hFloppy);
    } else {
        // unable to access floppy, so return unknown
        dgFloppy.MediaType = Unknown;
    }

    return dgFloppy.MediaType;
}

static
VOID
FillDriveList (
    IN  HWND    hwndDlg
)
/*++

Routine Description:

    Loads the text strings into the drive selection list box.

    NOTE: The order of these entries is significant in that it is
    used to index into the static device and drive name arrays.

Arguments:

    IN HWND hwndDlg
        handle to dialog box window

Return Value:

    NONE

--*/
{
    // clear list box
    SendDlgItemMessage (hwndDlg, NCDU_DRIVE_LIST, LB_RESETCONTENT, 0, 0);

    SendDlgItemMessage (hwndDlg, NCDU_DRIVE_LIST, LB_ADDSTRING, 0, (LPARAM)cszAColon);
    SendDlgItemMessage (hwndDlg, NCDU_DRIVE_LIST, LB_ADDSTRING, 0, (LPARAM)cszBColon);

    SendDlgItemMessage (hwndDlg, NCDU_DRIVE_LIST, LB_SETCURSEL, 0, 0);

}

static
DWORD
NumberOfDisks (
    IN  HWND    hwndDlg
)
/*++

Routine Description:

    Counts the number of "disk" directories in the currently selected client
        installation disk dir. The method used to count disks relies on the
        assumption that each disk's files are in a directory and the
        directories are mamed Disk1, Disk2, etc. The count is performed by
        looking for each disk directory in sequence and stopping when a
        dir is not found. i.e. if disk5 is not found, then 4 disks are
        assumed.

Arguments:

    Handle to dialog box window

Return Value:

    count of directorys ("disks") found

--*/
{
    LPTSTR  szFromPath;
    LPTSTR  szDirStart;
    BOOL    bCounting;
    UINT    nClientIndex;
    DWORD   nDiskNumber = 0;

    szFromPath = GlobalAlloc (GPTR, MAX_PATH_BYTES);

    if (szFromPath != NULL) {
        // get root dir of dist tree
        lstrcpy (szFromPath, pAppInfo->szDistPath);
        if (szFromPath[lstrlen(szFromPath)-1] != cBackslash) lstrcat (szFromPath, cszBackslash);

        // append client sw subdir
        nClientIndex = SendDlgItemMessage (hwndDlg, NCDU_CLIENT_SOFTWARE_LIST,
            LB_GETCURSEL, 0, 0);
        lstrcat (szFromPath, GetEntryInMultiSz (mszDirNameList, nClientIndex+1));
        if (szFromPath[lstrlen(szFromPath)-1] != cBackslash) lstrcat (szFromPath, cszBackslash);
        lstrcat (szFromPath, cszDisksSubDir);
        if (szFromPath[lstrlen(szFromPath)-1] != cBackslash) lstrcat (szFromPath, cszBackslash);
        szDirStart = &szFromPath[lstrlen(szFromPath)];

        // count directories
        bCounting = TRUE;
        nDiskNumber = 0;
        while (bCounting) {
            nDiskNumber++;
            _stprintf (szDirStart, fmtDiskNumber,
                nDiskNumber);
            bCounting = IsPathADir (szFromPath);
        }
        // account for last directory that wasn't found
        nDiskNumber -= 1;

        FREE_IF_ALLOC (szFromPath);
    }

    return nDiskNumber;   // for now
}

static
BOOL
UpdateDiskCount (
    IN  HWND    hwndDlg
)
/*++

Routine Description:

    updates the disk count text displayed in the dialog box.

Arguments:

    IN  HWND    hwndDlg
        handle to dialog box window

Return Value:

    TRUE if display updated successfully
    FALSE if not.

--*/
{
    LPTSTR  szBuffer;
    DWORD   dwNumDisks;

    szBuffer = GlobalAlloc (GPTR, MAX_PATH_BYTES);

    if (szBuffer != NULL) {
        dwNumDisks = NumberOfDisks (hwndDlg);
        if (dwNumDisks == 1) {
            _stprintf (szBuffer, GetStringResource (FMT_1_DISK_REQUIRED));
        } else {
            _stprintf (szBuffer, GetStringResource (FMT_N_DISKS_REQUIRED),
                dwNumDisks);
        }
        SetDlgItemText (hwndDlg, NCDU_NUM_DISKS_REQUIRED, szBuffer);
        FREE_IF_ALLOC (szBuffer);
        return TRUE;
    } else {
        return FALSE;
    }
}

static
BOOL
CopyFlopDlg_WM_INITDIALOG (
    IN  HWND    hwndDlg,
    IN  WPARAM  wParam,
    IN  LPARAM  lParam
)
/*++

Routine Description:

    Dialog box initialization routine. Loads list boxes in dialog

Arguments:

    IN  HWND    hwndDlg
        Handle to dialog box window

    IN  WPARAM  wParam
        Not used

    IN  LPARAM  lParam
        Not used

Return Value:

    FALSE, always.

--*/
{
    // update dialog window menu and position
    RemoveMaximizeFromSysMenu (hwndDlg);
    PositionWindow  (hwndDlg);

    // load clients to display
    LoadClientList (hwndDlg, NCDU_CLIENT_SOFTWARE_LIST,
        pAppInfo->szDistPath, CLT_FLOPPY_INSTALL, mszDirNameList);

    // set selection
    SendDlgItemMessage (hwndDlg, NCDU_CLIENT_SOFTWARE_LIST,
        LB_SETCURSEL, (WPARAM)0, (LPARAM)0);

    // load drives to select from
    FillDriveList(hwndDlg);

    // update disk count to reflect default selection
    UpdateDiskCount (hwndDlg);

    // clear old Dialog and register current
    PostMessage (GetParent(hwndDlg), NCDU_CLEAR_DLG, (WPARAM)hwndDlg, IDOK);
    PostMessage (GetParent(hwndDlg), NCDU_REGISTER_DLG,
        NCDU_CREATE_INSTALL_DISKS_DLG, (LPARAM)hwndDlg);

    // set cursor and focus
    SetCursor(LoadCursor(NULL, IDC_ARROW));
    SetFocus (GetDlgItem(hwndDlg, NCDU_MAKE_DISKS));

    return FALSE;
}

static
BOOL
CopyFlopDlg_NCDU_MAKE_DISKS (
    IN  HWND    hwndDlg
)
/*++

Routine Description:

    Function to copy the client files to the floppy disks.
        Selects the source directory using the client name and
        disk type then copies the files to the floppy disks
        sequentially assuming that the contents of each disk
        are stored in sub directory named "DiskX" where X is the
        decimal number indicating the disk sequence (e.g. 1, 2, 3, etc.)

Arguments:

    IN  HWND    hwndDlg
        Handle to dialog box window

Return Value:
    FALSE, always.

--*/
{
    MEDIA_TYPE  mtFloppy;
    LPTSTR      szFromPath;
    LPTSTR      szInfName;
    TCHAR       szSubDir[16];
    TCHAR       szDestRoot[4];
    TCHAR       szDriveRoot[8];
    LPTSTR      szClientName;
    LPTSTR      szDisplayName;
    LPTSTR      szDirStart;
    LPTSTR      szDiskName;
    LPTSTR      szClientSection;
    LPTSTR      szDisplayString;
    LPTSTR      szVolumeLabel;
    DWORD       dwFileAttr;
    BOOL        bCopyFiles;
    int         nSelIndex;
    int         nClientIndex;
    int         nCancelResult = 0;
    int         nDiskNumber;
    CF_DLG_DATA cfData;
    BOOL        bFormatDisks;

    szFromPath = (LPTSTR)GlobalAlloc (GPTR, MAX_PATH_BYTES);
    szInfName = (LPTSTR)GlobalAlloc (GPTR, MAX_PATH_BYTES);
    szClientName = (LPTSTR)GlobalAlloc (GPTR, MAX_PATH_BYTES);
    szDisplayName = (LPTSTR)GlobalAlloc (GPTR, MAX_PATH_BYTES);
    szDiskName = (LPTSTR)GlobalAlloc (GPTR, MAX_PATH_BYTES);
    szClientSection = (LPTSTR)GlobalAlloc (GPTR, MAX_PATH_BYTES);
    szVolumeLabel = (LPTSTR)GlobalAlloc (GPTR, MAX_PATH_BYTES);

    if ((szFromPath == NULL) ||
        (szClientName == NULL) ||
        (szClientSection == NULL) ||
        (szInfName == NULL) ||
        (szDiskName == NULL) ||
        (szVolumeLabel == NULL) ||
        (szDisplayName == NULL)) {
        // message not processed
        return FALSE;
    }

    // check floppy selection to see if it's supported

    //
    //      This function is really flakey, so for now, (for ever?)
    //      we'll assume they are using a HD disk drive
    //
    //  mtFloppy = GetDriveTypeFromList (hwndDlg);
    //
    mtFloppy = F3_1Pt44_512;

    if ((mtFloppy == F5_1Pt2_512) || // 5.25 HD
        (mtFloppy == F3_1Pt44_512)) { // 3.5 HD
        // get format check box state
        bFormatDisks = (BOOL)(IsDlgButtonChecked (hwndDlg, NCDU_FORMAT_DISKS) == CHECKED);

        // then this is a supported format
        //  make source path using:
        //      dist tree + client type + "disks" + drive type
        //
        // get root dir of dist tree
        lstrcpy (szFromPath, pAppInfo->szDistPath);
        if (szFromPath[lstrlen(szFromPath)-1] != cBackslash) lstrcat (szFromPath, cszBackslash);

        // append client sw subdir
        nClientIndex = SendDlgItemMessage (hwndDlg, NCDU_CLIENT_SOFTWARE_LIST,
            LB_GETCURSEL, 0, 0);
        lstrcat (szFromPath, GetEntryInMultiSz (mszDirNameList, nClientIndex+1));
        if (szFromPath[lstrlen(szFromPath)-1] != cBackslash) lstrcat (szFromPath, cszBackslash);
        lstrcat (szFromPath, cszDisksSubDir);

        // make INF file Name

        lstrcpy (szInfName, pAppInfo->szDistPath);
        if (szInfName[lstrlen(szInfName)-1] != cBackslash) lstrcat (szInfName, cszBackslash);
        lstrcat (szInfName, cszAppInfName);

        // get client name
        SendDlgItemMessage (hwndDlg, NCDU_CLIENT_SOFTWARE_LIST,
            LB_GETTEXT, (WPARAM)nClientIndex, (LPARAM)szClientName);

        if (szFromPath[lstrlen(szFromPath)-1] != cBackslash) lstrcat (szFromPath, cszBackslash);
        szDirStart = &szFromPath[lstrlen(szFromPath)];

        nSelIndex = SendDlgItemMessage (hwndDlg, NCDU_DRIVE_LIST,
            LB_GETCURSEL, 0, 0);

        // format client name key used to look up disk names in INF
        lstrcpy (szClientSection, GetEntryInMultiSz (mszDirNameList, nClientIndex+1));
        lstrcat (szClientSection, cszNames);

        // set the destination path
        lstrcpy (szDestRoot, szDriveDirs[nSelIndex]);
        lstrcpy (szDriveRoot, szDriveNames[nSelIndex]);
        lstrcat (szDriveRoot, cszBackslash);

        nDiskNumber = 1;
        _stprintf (szSubDir, fmtDiskNumber, nDiskNumber);
        lstrcpy (szDirStart, szSubDir);
        if ((dwFileAttr = QuietGetFileAttributes(szFromPath)) != 0xFFFFFFFF) {
            bCopyFiles = TRUE;
        } else {
            bCopyFiles = FALSE;
        }
        // initialize counter fields
        cfData.dwFilesCopied = 0;
        cfData.dwDirsCreated = 0;

        while (bCopyFiles) {
            if ((dwFileAttr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) {
                // see if this disk has a name, if so display it, otherwise
                // just show the disk number
                if (QuietGetPrivateProfileString (szClientSection, szSubDir,
                    cszEmptyString, szDiskName, MAX_PATH, szInfName) == 0) {
                    lstrcpy (szDiskName, szSubDir);
                } else {
                    // append "disk" to the name string
                    lstrcat (szDiskName, cszDisk);
                }
                // display "load the floppy" message
                _stprintf (szDisplayName,
                    GetStringResource (FMT_CLIENT_DISK_AND_DRIVE),
                    szClientName, szDiskName, szDriveDirs[nSelIndex]);

                nCancelResult = MessageBox (
                    hwndDlg,
                    szDisplayName,
                    GetStringResource (FMT_INSERT_FLOPPY),
                    MB_OKCANCEL_TASK_INFO);

                if (nCancelResult == IDOK) {
                    // format  the copying files message
                    _stprintf (szDisplayName,
                        GetStringResource (FMT_CLIENT_DISPLAY_NAME),
                        szClientName, szDiskName);

                    // set the disk volume label if one is in the INF
                    lstrcat (szSubDir, cszLabel);
                    if (QuietGetPrivateProfileString (szClientSection, szSubDir,
                        cszEmptyString, szVolumeLabel, MAX_PATH, szInfName) > 0) {
                    }

                    if (bFormatDisks) {
                        while (!FormatDiskInDrive (hwndDlg,
                            szDestRoot[0],
                            szVolumeLabel,
                            TRUE)) {

                            // an error occured so see if they want to try again or bail out
                            if (DisplayMessageBox (hwndDlg,
                                IDS_CORRECT_FMT_ERROR,
                                FMT_INSERT_FLOPPY,
                                MB_OKCANCEL_TASK_EXCL) == IDCANCEL) {
                                bCopyFiles = FALSE;
                                nCancelResult = IDCANCEL;
                                break; // now exit retry loop
                            }
                        }
                        if (!bCopyFiles) continue; // bail out at the top
                    }

                    cfData.szDisplayName = szDisplayName;
                    cfData.szSourceDir = szFromPath;
                    cfData.szDestDir = szDestRoot;
                    cfData.dwCopyFlags = CD_FLAGS_COPY_SUB_DIR | CD_FLAGS_COPY_ATTR | CD_FLAGS_IGNORE_ERROR;
                    cfData.dwTotalSize = 0;

                    nCancelResult = DialogBoxParam (
                        (HANDLE)GetWindowLong(GetParent(hwndDlg), GWL_HINSTANCE),
                        MAKEINTRESOURCE(NCDU_COPYING_FILES_DLG),
                        hwndDlg,
                        CopyFileDlgProc,
                        (LPARAM)&cfData);

                    if (nCancelResult == IDCANCEL) {
                        bCopyFiles = FALSE;
                    } else {
                        // set volume label (if not set already)
                        if ((!bFormatDisks) & (lstrlen(szVolumeLabel) > 0)) {
                            // set volume label here
                            LabelDiskInDrive (hwndDlg, szDestRoot[0], szVolumeLabel);
                        }
                    }

                } else {
                    bCopyFiles = FALSE;
                }
            }
            if (bCopyFiles) {
                _stprintf (szSubDir, fmtDiskNumber, ++nDiskNumber);
                lstrcpy (szDirStart, szSubDir);
                if ((dwFileAttr = QuietGetFileAttributes(szFromPath)) != 0xFFFFFFFF) {
                    bCopyFiles = TRUE;
                } else {
                    bCopyFiles = FALSE;
                }
            }
        }

        if (nCancelResult != IDCANCEL) {
            szDisplayString = GlobalAlloc (GPTR, MAX_PATH_BYTES);
            if (szDisplayString == NULL) {
                // unable to allocate string buffer so try default message
                DisplayMessageBox (
                hwndDlg,
                NCDU_COPY_COMPLETE,
                0,
                MB_OK_TASK_INFO);
            } else {
                _stprintf (szDisplayString,
                    GetStringResource (FMT_COPY_COMPLETE_STATS),
                    cfData.dwDirsCreated, cfData.dwFilesCopied);
                MessageBox (
                    hwndDlg, szDisplayString,
                    GetStringResource (SZ_APP_TITLE),
                    MB_OK_TASK_INFO);
                FREE_IF_ALLOC (szDisplayString);
            }
        } else {
            DisplayMessageBox (
                hwndDlg,
                NCDU_DISK_NOT_DONE,
                0,
                MB_OK_TASK_EXCL);
        }

        PostMessage (GetParent(hwndDlg), NCDU_SHOW_SW_CONFIG_DLG, 0, 0);
        SetCursor(LoadCursor(NULL, IDC_WAIT));

    } else {
        // unsupported format
        nCancelResult = DisplayMessageBox (
            hwndDlg,
            NCDU_UNKNOWN_FLOPPY,
            0,
            MB_OK_TASK_EXCL);
    }

    FREE_IF_ALLOC (szFromPath);
    FREE_IF_ALLOC (szClientName);
    FREE_IF_ALLOC (szDisplayName);
    FREE_IF_ALLOC (szInfName);
    FREE_IF_ALLOC (szDiskName);
    FREE_IF_ALLOC (szVolumeLabel);
    FREE_IF_ALLOC (szClientSection);

    return TRUE;
}

static
BOOL
CopyFlopDlg_WM_COMMAND (
    IN  HWND    hwndDlg,
    IN  WPARAM  wParam,
    IN  LPARAM  lParam
)
/*++

Routine Description:

    processes the WM_COMMAND windows message. The only buttons processed
        are:
            IDCANCEL    which will cause the dlg box to exit.
            Make Disks  start the copy file process

Arguments:

    IN  HWND    hwndDlg
        Handle to dialog box window

    IN  WPARAM  wParam
        LOWORD  has ID of control selected

    IN  LPARAM  lParam
        Not Used.

Return Value:

    TRUE if message not processed,
        otherwise value returned by selected function

--*/
{
    switch (LOWORD(wParam)) {
        case IDCANCEL:
            switch (HIWORD(wParam)) {
                case BN_CLICKED:
                    PostMessage (GetParent(hwndDlg), NCDU_SHOW_SHARE_NET_SW_DLG, 0, 0);
                    SetCursor(LoadCursor(NULL, IDC_WAIT));
                    return TRUE;

                default:
                    return FALSE;
            }

        case NCDU_CLIENT_SOFTWARE_LIST:
            switch (HIWORD(wParam)) {
                case LBN_SELCHANGE:
                    // update the disk count for the currently selected client
                    UpdateDiskCount (hwndDlg);
                    return TRUE;

                case LBN_DBLCLK:
                    // pretend that the OK buton was pressed
                    PostMessage (hwndDlg, WM_COMMAND,
                        MAKEWPARAM (NCDU_MAKE_DISKS, BN_CLICKED),
                        (LPARAM)GetDlgItem(hwndDlg, NCDU_MAKE_DISKS));
                    return TRUE;

                default:
                    return FALSE;
            }

        case NCDU_MAKE_DISKS:   return CopyFlopDlg_NCDU_MAKE_DISKS (hwndDlg);
        case NCDU_CREATE_INSTALL_DISKS_HELP:
            switch (HIWORD(wParam)) {
                case BN_CLICKED:
//                    return ShowAppHelp (hwndDlg, LOWORD(wParam));
                    return PostMessage (GetParent(hwndDlg), WM_HOTKEY,
                        (WPARAM)NCDU_HELP_HOT_KEY, 0);

                default:
                    return FALSE;
            }

        default:                return TRUE;
    }
}

BOOL CALLBACK
CopyFlopDlgProc (
    IN  HWND    hwndDlg,
    IN  UINT    message,
    IN  WPARAM  wParam,
    IN  LPARAM  lParam
)
/*++

Routine Description:

    Main window processing routine. Processes the following messages by
        dispatching to the local processing routine. All other messages
        are handled by the default dialog procedure.

            WM_INITDIALOG:  dialog box initialization
            WM_COMMAND:     user input message
            WM_PAINT:       for painting icon when minimized
            WM_MOVE:        for saving the new location of the window
            WM_SYSCOMMAND:  for processing menu messages


Arguments:

    standard WNDPROC arguments

Return Value:

    FALSE if message not processed here, otherwise
        value returned by dispatched routine.

--*/
{
    switch (message) {
        case WM_INITDIALOG: return (CopyFlopDlg_WM_INITDIALOG (hwndDlg, wParam, lParam));
        case WM_COMMAND:    return (CopyFlopDlg_WM_COMMAND (hwndDlg, wParam, lParam));
        case WM_PAINT:      return (Dlg_WM_PAINT (hwndDlg, wParam, lParam));
        case WM_MOVE:       return (Dlg_WM_MOVE (hwndDlg, wParam, lParam));
        case WM_SYSCOMMAND: return (Dlg_WM_SYSCOMMAND (hwndDlg, wParam, lParam));
        default:            return FALSE;
    }
}

