/******************************Module*Header*******************************\
* Module Name: scan.c
*
* Code for scanning the available CD Rom devices.
*
*
* Created: 02-11-93
* Author:  Stephen Estrop [StephenE]
*
* Copyright (c) 1993 Microsoft Corporation
\**************************************************************************/
#pragma warning( once : 4201 4214 )

#define NOOLE

#include <windows.h>    /* required for all Windows applications */
#include <windowsx.h>

#include <string.h>
#include <tchar.h>              /* contains portable ascii/unicode macros */

#include "playres.h"
#include "cdplayer.h"
#include "cdapi.h"
#include "scan.h"
#include "trklst.h"
#include "database.h"



/*****************************Private*Routine******************************\
* ScanForCdromDevices
*
* Returns the number of CD-ROM devices installed in the system.
*
* History:
* 18-11-93 - StephenE - Created
*
\**************************************************************************/
int
ScanForCdromDevices(
    void
    )
{
    DWORD   dwDrives;
    TCHAR   chDrive[] = TEXT("A:\\");
    int     iNumDrives;

    iNumDrives  = 0;

    for (dwDrives = GetLogicalDrives(); dwDrives != 0; dwDrives >>= 1 ) {

        /*
        ** Is there a logical drive ??
        */
        if (dwDrives & 1) {

            if ( GetDriveType(chDrive) == DRIVE_CDROM ) {

                g_Devices[iNumDrives] = (CDROM*)AllocMemory( sizeof(CDROM) );

                g_Devices[iNumDrives]->drive = chDrive[0];
                g_Devices[iNumDrives]->State = CD_BEING_SCANNED;

                iNumDrives++;
            }
        }

        /*
        ** Go look at the next drive
        */
        chDrive[0] = chDrive[0] + 1;
    }

    return iNumDrives;
}


/******************************Public*Routine******************************\
* RescanDevice
*
*
* This routine is called to scan the disc in a given cdrom by
* reading its table of contents.  If the cdrom is playing the user is
* notified that the music will stop.
*
* History:
* 18-11-93 - StephenE - Created
*
\**************************************************************************/
void RescanDevice(
    HWND hwndNotify,
    int cdrom
    )
{
    TOC_THREAD_PARMS    *ptoc;
    HWND                hwndButton;
    int                 iMsgBoxRtn;

    if ( g_Devices[cdrom]->State & CD_PLAYING ) {

        TCHAR   s1[256];
        TCHAR   s2[256];

        _tcscpy( s1, IdStr( STR_CANCEL_PLAY ) );
        _tcscpy( s2, IdStr( STR_RESCAN ) );

        iMsgBoxRtn = MessageBox( g_hwndApp, s1, s2,
                                 MB_APPLMODAL | MB_DEFBUTTON1 |
                                 MB_ICONQUESTION | MB_YESNO);

        if ( iMsgBoxRtn == IDYES ) {

            hwndButton = g_hwndControls[INDEX(IDM_PLAYBAR_STOP)];

            SendMessage( hwndButton, WM_LBUTTONDOWN, 0, 0L );
            SendMessage( hwndButton, WM_LBUTTONUP, 0, 0L );
        }
        else {

            return;
        }
    }


    /*
    ** Attempt to read table of contents of disc in this drive.  We
    ** now spawn off a separate thread to do this.  Note that the child
    ** thread frees the storage allocated below.
    */
    ptoc = (TOC_THREAD_PARMS*)AllocMemory( sizeof(TOC_THREAD_PARMS) );
    ptoc->hwndNotify = hwndNotify;
    ptoc->cdrom = cdrom;
    ReadTableOfContents( ptoc );

}


/*****************************Private*Routine******************************\
* ReadTableofContents
*
* This function reads in the table of contents (TOC) for the specified cdrom.
* All TOC's are read on a worker thread.  The hi-word of thread_info variable
* is a boolean that states if the display should been updated after the TOC
* has been reads.  The lo-word of thread_info is the id of the cdrom device
* to be read.
*
* History:
* 18-11-93 - StephenE - Created
*
\**************************************************************************/
void
ReadTableOfContents(
    TOC_THREAD_PARMS *ptoc
    )
{
    DWORD   dwThreadId;
    int     cdrom;

    cdrom = ptoc->cdrom;
    g_Devices[ cdrom ]->fIsTocValid = FALSE;
    g_Devices[cdrom]->fShowLeadIn = FALSE;
    g_Devices[cdrom]->fProcessingLeadIn = FALSE;

    if (g_Devices[ cdrom ]->hThreadToc != NULL) {

        /*
        ** We have a thread TOC handle see if the thread is
        ** still running.  If so just return, otherwise
        */
        switch ( WaitForSingleObject(g_Devices[ cdrom ]->hThreadToc, 0L) ) {

        /*
        ** Thread has finished to continue
        */
        case WAIT_OBJECT_0:
            break;

        /*
        ** The thread is still running so just return
        */
        case WAIT_TIMEOUT:
        default:
            return;
        }

        CloseHandle( g_Devices[ cdrom ]->hThreadToc );
    }

    g_Devices[ cdrom ]->hThreadToc = CreateThread(
        NULL, 0L, (LPTHREAD_START_ROUTINE)TableOfContentsThread,
        (LPVOID)ptoc, 0L, &dwThreadId );

    /*
    ** For now I will kill the app if I cannot create the
    ** ReadTableOfContents thread.  This is probably a bit
    ** harsh.
    */

    if (g_Devices[ cdrom ]->hThreadToc == NULL) {
        FatalApplicationError( STR_NO_RES, GetLastError() );
    }

}

/*****************************Private*Routine******************************\
* TableOfContentsThread
*
* This is the worker thread that reads the table of contents for the
* specified cdrom.
*
* Before the thread exits we post a message to the UI threads main window to
* notify it that the TOC for this cdrom has been updated.  It then  examines the
* database to determine if this cdrom is known and updates the screen ccordingly.
*
*
* History:
* 18-11-93 - StephenE - Created
*
\**************************************************************************/
void
TableOfContentsThread(
    TOC_THREAD_PARMS *ptoc
    )
{
    DWORD   status;
    UCHAR   num, numaudio;
    int     cdrom;
    HWND    hwndNotify;

    //  This serializes access to this function 
    //  between multiple threads and the CDPlayer_OnTocRead
    //  function on the main thread. 
    //  This prevents resource contention on CDROM Multi-changers
    EnterCriticalSection (&g_csTOCSerialize);

    cdrom = ptoc->cdrom;
    hwndNotify = ptoc->hwndNotify;

    LocalFree( ptoc );

    /*
    ** Try to read the TOC from the drive.
    */

#ifdef USE_IOCTLS

    status = GetCdromTOC( g_Devices[cdrom]->hCd, &(g_Devices[cdrom]->toc) );
    num = g_Devices[cdrom]->toc.LastTrack - g_Devices[cdrom]->toc.FirstTrack+1;
    {
        int     i;

        numaudio = 0;

        /*
        ** Look for audio tracks...
        */
        for( i = 0; i < num; i++ ) {

            if ( (g_Devices[cdrom]->toc.TrackData[i].Control &
                  TRACK_TYPE_MASK ) == AUDIO_TRACK ) {

                numaudio++;
            }

        }
    }

    /*
    ** Need to check if we got data tracks or audio
    ** tracks back...if there is a mix, strip out
    ** the data tracks...
    */
    if (status == ERROR_SUCCESS) {


        /*
        ** If there aren't any audio tracks, then we (most likely)
        ** have a data CD loaded.
        */

        if (numaudio == 0) {

            status == ERROR_UNRECOGNIZED_MEDIA;
            g_Devices[cdrom]->State = CD_DATA_CD_LOADED | CD_STOPPED;

        }
        else {

            g_Devices[cdrom]->State = CD_LOADED | CD_STOPPED;
        }
    }
    else {

        g_Devices[cdrom]->State = CD_NO_CD | CD_STOPPED;
    }

#else
    {
        MCIDEVICEID wDeviceID;
        DWORD       dwCDPlayerMode = 0L;

    OSVERSIONINFO os;
    os.dwOSVersionInfoSize = sizeof(os);
    GetVersionEx(&os);
    if (os.dwPlatformId != VER_PLATFORM_WIN32_NT)
    {
        if (g_Devices[cdrom]->hCd == 0) {
            g_Devices[cdrom]->hCd = OpenCdRom( g_Devices[cdrom]->drive,
                                               &status );
        }
        wDeviceID = g_Devices[cdrom]->hCd;
    }
    else
    {
        wDeviceID = OpenCdRom( g_Devices[cdrom]->drive, &status );
    }

        if ( wDeviceID != 0 ) {

            int     i;

            numaudio = 0;
            status = GetCdromTOC( wDeviceID, &(g_Devices[cdrom]->toc) );

            /*
            ** Need to check if we got data tracks or audio
            ** tracks back...if there is a mix, strip out
            ** the data tracks...
            */
            if ( status == ERROR_SUCCESS) {
                num = g_Devices[cdrom]->toc.LastTrack -
                      g_Devices[cdrom]->toc.FirstTrack + 1;

                for( i = 0; i < num; i++ ) {

                    if ( IsCdromTrackAudio(wDeviceID, i) ) {

                        numaudio++;
                    }
                }
            }

            dwCDPlayerMode = GetCdromMode( wDeviceID );

            OSVERSIONINFO os;
            os.dwOSVersionInfoSize = sizeof(os);
            GetVersionEx(&os);
            if (os.dwPlatformId == VER_PLATFORM_WIN32_NT)
            {
                CloseCdRom( wDeviceID );
            }
        }

        /*
        ** Need to check if we got data tracks or audio
        ** tracks back...if there is a mix, strip out
        ** the data tracks...
        */
        if (status == ERROR_SUCCESS) {

            /*
            ** If there aren't any audio tracks, then we (most likely)
            ** have a data CD loaded.
            */

            if (numaudio == 0) {

                g_Devices[cdrom]->State = CD_DATA_CD_LOADED | CD_STOPPED;
            }
            else {

                g_Devices[cdrom]->State = CD_LOADED;

                switch (dwCDPlayerMode) {

                case MCI_MODE_PAUSE:
                    g_Devices[cdrom]->State |= CD_PAUSED;
                    break;

                case MCI_MODE_PLAY:
                    g_Devices[cdrom]->State |= CD_PLAYING;
                    break;

                default:
                    g_Devices[cdrom]->State |= CD_STOPPED;
                    break;
                }
            }

        }
        else {

            if (status == (DWORD)MCIERR_MUST_USE_SHAREABLE) {
                g_Devices[cdrom]->State = CD_IN_USE;
            }

            if (g_Devices[cdrom]->State != CD_IN_USE) {
                g_Devices[cdrom]->State = CD_NO_CD | CD_STOPPED;
            }
        }
    }
#endif

    /*
    ** Notify the UI thread that a TOC has been read and then terminate the
    ** thread.
    */

    PostMessage( hwndNotify, WM_NOTIFY_TOC_READ,
                 (WPARAM)cdrom, (LPARAM)numaudio );

    LeaveCriticalSection (&g_csTOCSerialize);
    
    ExitThread( 1L );
}
