/*--------------------------------------------------------------------
|
| LangPlay.c - Sample Win app to play AVI movies using MCIWnd. Handles
|       multiple language track movies and lets the
|       user select the track to listen to at playback.
|
|
+--------------------------------------------------------------------*/
/**************************************************************************
 *
 *  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
 *  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
 *  PURPOSE.
 *  Copyright (c) 1992 - 1994 Microsoft Corporation.  All Rights Reserved.
 *
 **************************************************************************/
#define INC_OLE2
#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <commdlg.h>
#include <string.h>
#include <stdlib.h>
#include <direct.h>
#include <digitalv.h>
#include <vfw.h>
#include "langplay.h"


/**************************************************************
************************ GLOBALS ******************************
**************************************************************/
/* AVI stuff to keep around */
HWND hwndMovie;                 /* window handle of the movie */
BOOL fMovieOpen = FALSE;        /* Open flag: TRUE == movie open, FALSE = none */
HMENU hMenuBar = NULL;          /* menu bar handle */
char szAppName [] = "LangPlay";

// struct for handling multi-language support
typedef struct langs_tag {
        WORD            wLangTag;       // language type tag
        char            achName[64];    // stream name  (limited to 64 chars by AVIStreamInfo)
} LANGS, FAR *LPLANGS;

#define         NOAUDIO         0       // no audio stream
int             iCurLang;               // current language selected (0 == NONE)


/* function declarations */
long FAR PASCAL WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
void fileOpenMovie(HWND hWnd);
void menubarUpdate(HWND hWnd);
void titlebarUpdate(HWND hWnd, LPSTR lpstrMovie);
BOOL CALLBACK AboutDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

/* language specific functions */
BOOL enumLangs(HWND hwnd, LPSTR lpstrMovie);
void buildLangMenu(HWND hwnd, LPLANGS lpLangs, DWORD dwLangs);
void switchLang(HWND hWnd, int iLangStream);


/********************************************************************
************************** FUNCTIONS ********************************
********************************************************************/


/*--------------------------------------------------------------+
| initApp - initialize the app overall.                         |
|                                                               |
| Returns the Window handle for the app on success, NULL if     |
| there is a failure.                                           |
|                                                               |
+--------------------------------------------------------------*/
HWND initApp(HINSTANCE hInstance, HINSTANCE hPrevInstance, int nCmdShow)
{
        HWND            hWnd;   /* window handle to return */
        int             iWinHeight;
        WORD    wVer;

        /* first let's make sure we are running on 1.1 */
        wVer = HIWORD(VideoForWindowsVersion());
        if (wVer < 0x010a){
                /* oops, we are too old, blow out of here */
                MessageBeep(MB_ICONHAND);
                MessageBox(NULL, "Video for Windows version is too old",
                          "LangPlay Error", MB_OK|MB_ICONSTOP);
                return FALSE;
        }

        if (!hPrevInstance){
                WNDCLASS    wndclass;

                wndclass.style         = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
                wndclass.lpfnWndProc   = WndProc;
                wndclass.cbClsExtra    = 0;
                wndclass.cbWndExtra    = 0;
                wndclass.hInstance     = hInstance;
                wndclass.hIcon         = LoadIcon (hInstance, "AppIcon");
                wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW);
                wndclass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
                wndclass.lpszMenuName  = szAppName;
                wndclass.lpszClassName = szAppName;

                if (!RegisterClass(&wndclass)){
                        MessageBox(NULL, "RegisterClass failure", szAppName, MB_OK);
                        return NULL;
                }
        }

        iWinHeight = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYMENU) +
                        (GetSystemMetrics(SM_CYFRAME) * 2);

        /* create the main window for the app */
        hWnd = CreateWindow(szAppName, szAppName, WS_OVERLAPPEDWINDOW |
                WS_CLIPCHILDREN, CW_USEDEFAULT, CW_USEDEFAULT, 180, iWinHeight,
                NULL, NULL, hInstance, NULL);

        if (hWnd == NULL){
                MessageBox(NULL, "CreateWindow failure", szAppName, MB_OK);
                return NULL;
        }

        hMenuBar = GetMenu(hWnd);       /* get the menu bar handle */
        menubarUpdate(hWnd);            /* update menu bar to disable Movie menu */

        /* Show the main window */
        ShowWindow(hWnd, nCmdShow);
        UpdateWindow(hWnd);

        /* create the movie window using MCIWnd that has no file open initially */
        hwndMovie = MCIWndCreate(hWnd, hInstance, WS_CHILD |WS_VISIBLE | MCIWNDF_NOOPEN |
                                MCIWNDF_NOERRORDLG | MCIWNDF_NOTIFYSIZE, NULL);

        if (!hwndMovie){
                /* we didn't get the movie window, destroy the app's window and bail out */
                DestroyWindow(hWnd);
                return NULL;
        }
        return hWnd;
}




/*--------------------------------------------------------------+
| WinMain - main routine.                                       |
|                                                               |
+--------------------------------------------------------------*/
int PASCAL WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                                LPSTR lpszCmdParam, int nCmdShow)
{
        HWND        hWnd;
        MSG         msg;

        if ((hWnd = initApp(hInstance, hPrevInstance,nCmdShow)) == NULL)
                return 0;       /* died initializing, bail out */

        while (GetMessage(&msg, NULL, 0, 0)){
                TranslateMessage(&msg);
                DispatchMessage(&msg);
        }
        return msg.wParam;
}


/*--------------------------------------------------------------+
| WndProc - window proc for the app                             |
|                                                               |
+--------------------------------------------------------------*/
long FAR PASCAL WndProc (HWND hWnd, UINT message, WPARAM wParam,
                                                LPARAM lParam)
{
        PAINTSTRUCT ps;
        WORD w;
        WORD    wMenu;
        RECT    rc;

        switch (message){
                case WM_CREATE:
                        return 0;

                case WM_INITMENUPOPUP:
                        /* be sure this isn't the system menu */
                        if (HIWORD(lParam))
                                return DefWindowProc(hWnd, WM_INITMENUPOPUP,
                                                wParam, lParam);

                        wMenu = LOWORD(lParam);
                        switch (wMenu){
                                case 0:   /* file menu */
                                        /* turn on/off CLOSE & PLAY */
                                        if (fMovieOpen) w = MF_ENABLED|MF_BYCOMMAND;
                                        else            w = MF_GRAYED|MF_BYCOMMAND;
                                        EnableMenuItem((HMENU)wParam, IDM_CLOSE, w);
                                        break;
                        } /* switch */
                        break;

                case WM_COMMAND:
                        if (wParam >= IDM_STREAM){
                                // the command is to switch the audio stream
                                switchLang(hWnd, wParam - IDM_STREAM + 1);
                                return 0;
                        }
                        /* handle the menu commands */
                        switch (wParam) {
                                /* File Menu */
                                case IDM_OPEN:
                                        fileOpenMovie(hWnd);
                                        break;
                                case IDM_CLOSE:
                                        fMovieOpen = FALSE;
                                        MCIWndClose(hwndMovie);         // close the movie
                                        ShowWindow(hwndMovie, SW_HIDE); //hide the window
                                        menubarUpdate(hWnd);
                                        titlebarUpdate(hWnd, NULL);     // title bar back to plain
                                        break;
                                case IDM_EXIT:
                                        PostMessage(hWnd, WM_CLOSE, 0, 0L);
                                        break;

                                /* audio menu */
                                case IDM_NONE:
                                        switchLang(hWnd, NOAUDIO);
                                        break;

                                case IDM_ABOUT:
                                        DialogBox((HANDLE)GetWindowInstance(hWnd),
                                                  MAKEINTRESOURCE(IDD_ABOUT),
                                                  hWnd,
                                                  AboutDlgProc);
                                        break;

                        }
                        return 0;

                case WM_PAINT:
                        BeginPaint(hWnd, &ps);
                        EndPaint(hWnd, &ps);
                        return 0;

                case WM_SIZE:
                        if (hwndMovie && fMovieOpen)
                                MoveWindow(hwndMovie,0,0,LOWORD(lParam),HIWORD(lParam),TRUE);
                        break;

                case WM_DESTROY:
                        if (fMovieOpen)
                                MCIWndClose(hwndMovie);  // close an open movie
                        MCIWndDestroy(hwndMovie);    // now destroy the MCIWnd window
                        PostQuitMessage(0);
                        return 0;

                case MCIWNDM_NOTIFYSIZE:
                        if (fMovieOpen){
                                /* adjust to size of the movie window */
                                GetWindowRect(hwndMovie, &rc);
                                AdjustWindowRect(&rc, GetWindowLong(hWnd, GWL_STYLE), TRUE);
                                SetWindowPos(hWnd, NULL, 0, 0, rc.right - rc.left,
                                        rc.bottom - rc.top,
                                        SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE);
                        } else {
                                /* movie closed, adjust to the default size */
                                int iWinHeight;

                                iWinHeight = GetSystemMetrics(SM_CYCAPTION) +
                                                GetSystemMetrics(SM_CYMENU) +
                                                (GetSystemMetrics(SM_CYFRAME) * 2);
                                SetWindowPos(hWnd, NULL, 0, 0, 180, iWinHeight,
                                        SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE);
                        }
                        break;

                case WM_ACTIVATE:
                case WM_QUERYNEWPALETTE:
                case WM_PALETTECHANGED:
                        //
                        // Forward palette-related messages through to the MCIWnd,
                        // so it can do the right thing.
                        //
                        if (hwndMovie)
                                return SendMessage(hwndMovie, message, wParam, lParam);
                        break;
        } /* switch */
        return DefWindowProc(hWnd, message, wParam, lParam);
}

/*--------------------------------------------------------------+
| menubarUpdate - update the menu bar based on the <fMovieOpen> |
|                 flag value.  This will turn on/off the        |
|                 Movie menu.                                   |
|                                                               |
+--------------------------------------------------------------*/
void menubarUpdate(HWND hWnd)
{
        WORD w;

        if (fMovieOpen){
                w = MF_ENABLED|MF_BYPOSITION;
        } else {
                w = MF_GRAYED|MF_BYPOSITION;
        }
        EnableMenuItem(hMenuBar, 1, w); /* change the Movie menu (#1) */
        DrawMenuBar(hWnd);      /* re-draw the menu bar */
}

/*--------------------------------------------------------------+
| titlebarUpdate - update the title bar to include the name     |
|                  of the movie playing.                        |
|                                                               |
+--------------------------------------------------------------*/
void titlebarUpdate(HWND hWnd, LPSTR lpstrMovie)
{
        char achNewTitle[BUFFER_LENGTH];        // space for the title

        if (lpstrMovie != NULL)
                wsprintf((LPSTR)achNewTitle,"%s - %s", (LPSTR)szAppName,lpstrMovie);
        else
                lstrcpy((LPSTR)achNewTitle, (LPSTR)szAppName);
        SetWindowText(hWnd, (LPSTR)achNewTitle);
}

/*--------------------------------------------------------------+
| fileOpenMovie - open an AVI movie. Use CommDlg open box to    |
|               open and then handle the initialization to      |
|               show the movie and position it properly.  Keep  |
|               the movie paused when opened.                   |
|                                                               |
|               Sets <fMovieOpened> on success.                 |
+--------------------------------------------------------------*/
void fileOpenMovie(HWND hWnd)
{
        OPENFILENAME ofn;

        static char szFile [BUFFER_LENGTH];
        static char szFileTitle [BUFFER_LENGTH];

        /* use the OpenFile dialog to get the filename */
        memset(&ofn, 0, sizeof(ofn));
        ofn.lStructSize = sizeof(ofn);
        ofn.hwndOwner = hWnd;
        ofn.lpstrFilter = "Video for Windows\0*.avi\0\0";
        ofn.lpstrFile = szFile;
        ofn.nMaxFile = sizeof(szFile);
        ofn.lpstrFileTitle = szFileTitle;
        ofn.nMaxFileTitle = sizeof(szFileTitle);
        ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

        /* use CommDlg to get our filename */
        if (GetOpenFileName(&ofn)){

                /* we got a filename, now close any old movie and open */
                /* the new one.                                 */
                if (fMovieOpen)
                        MCIWndClose(hwndMovie);

                enumLangs(hWnd, ofn.lpstrFile); // find out the languages used in the file.

                /* try to open the file */
                fMovieOpen = TRUE;              // assume the best
                if (MCIWndOpen(hwndMovie, ofn.lpstrFile, 0) == 0){
                        /* we opened the file o.k., now set up to */
                        /* play it.                                */
                        ShowWindow(hwndMovie, SW_SHOW);
                } else {
                        /* generic error for open */
                        MessageBox(hWnd, "Unable to open Movie", NULL,
                              MB_ICONEXCLAMATION|MB_OK);
                        fMovieOpen = FALSE;
                }
        }
        /* update menu bar */
        menubarUpdate(hWnd);
        if (fMovieOpen)
                titlebarUpdate(hWnd, (LPSTR)ofn.lpstrFileTitle);
        else
                titlebarUpdate(hWnd, NULL);

        /* cause an update to occur */
        InvalidateRect(hWnd, NULL, FALSE);
        UpdateWindow(hWnd);
}



/*--------------------------------------------------------------+
| enumLangs - enumerate the audio streams in a file and set up  |
|             the global ghLangs as a handle to the array of    |
|             language streams.                                 |                                                               |
|                                                               |
| To do this:                                                   |
|       1. Open the file using AVIFileOpen().                   |
|       2. Open all Audio Streams, getting the pavi for it      |
|       3. Get the stream info on all audio streams to get names|
|       4. If names don't exist use "Audio Strean n"            |
|       5. Close the file                                       |
|       6. Build the audio stream menu                          |
|                                                               |
| Return TRUE if successfully set up menu, FALSE on any error   |
|                                                               |
+--------------------------------------------------------------*/
BOOL enumLangs(HWND hWnd, LPSTR lpstrMovie)
{
        PAVIFILE        pFile;
        PAVISTREAM      pStream;
        AVIFILEINFO aviInfo;
        AVISTREAMINFO aviStream;
        DWORD           dwNumStreams;
        DWORD           dwNumAudioStreams = 0L;
        LPLANGS         lpLangs;
        LPLANGS         lpLang;
        HANDLE          hLangs;

        DWORD           dw;


        AVIFileInit();

        // go open the file
        if (AVIFileOpen((PAVIFILE far *)&pFile, lpstrMovie, OF_READ, NULL) != 0)
                return FALSE;

        // get the file information
        AVIFileInfo(pFile, (LPAVIFILEINFO)&aviInfo, sizeof(aviInfo));
        dwNumStreams = aviInfo.dwStreams;               // grab the number of streams

        // loop through the streams and find the # of audio streams
        for (dw = 0L; dw < dwNumStreams; dw++){
                AVIFileGetStream(pFile, (PAVISTREAM far *)&pStream, 0, dw);
                AVIStreamInfo(pStream, (LPAVISTREAMINFO)&aviStream, sizeof(aviStream));
                if (aviStream.fccType == streamtypeAUDIO)
                        dwNumAudioStreams++;
                AVIStreamClose(pStream);

        }

        // we now know how many audio streams we are dealing with, we need to allocate
        // enough memory for the Langs array and then get all the audio stream information
        // again.
        hLangs = GlobalAlloc(GHND, (sizeof(LANGS) *  dwNumAudioStreams));
        if (hLangs == NULL)
                return FALSE;

        lpLangs = (LPLANGS)GlobalLock(hLangs);  // get the memory

        // loop through the audio streams and fill out the array
        for (dw = 0L, lpLang = lpLangs; dw < dwNumAudioStreams; dw++, lpLang++) {
                AVIFileGetStream(pFile, (PAVISTREAM far *)&pStream, streamtypeAUDIO, dw);
                AVIStreamInfo(pStream, (LPAVISTREAMINFO)&aviStream, sizeof(aviStream));
                if (aviStream.szName && *aviStream.szName != '\0')
                        lstrcpy((LPSTR)lpLang->achName, (LPSTR)aviStream.szName);
                else
                        wsprintf((LPSTR)lpLang->achName, "Audio Stream %lu",dw);
                AVIStreamClose(pStream);

        }

        // now build the menu for this
        buildLangMenu(hWnd, lpLangs, dwNumAudioStreams);

        // close up and deallocate resources
        AVIFileClose(pFile);
        AVIFileExit();

        GlobalUnlock(hLangs);
        GlobalFree(hLangs);

        return TRUE;
}


/*--------------------------------------------------------------+
| buildLangMenu - build up the language menu for the audio      |
|                                 streams available.            |
|                                                               |
| hwnd is the main application window handle                    |
| lang points to an array of LANGSTRUCT entries already filled  |
|      in by the caller.                                        |
+--------------------------------------------------------------*/
void buildLangMenu(HWND hwnd, LPLANGS lpLangs, DWORD dwLangs)
{
        UINT    i;
        HMENU   hMenu;
        LPLANGS lplang;
        UINT    uNumMenus;

        // go through menu chain and get the Audio Stream pop-up menu
        hMenu = GetMenu(hwnd);                  // get the menu bar
        hMenu = GetSubMenu(hMenu, 1);   // get the Audio Stream menu

        uNumMenus = GetMenuItemCount(hMenu);    // how many items are on this menu?
        if (uNumMenus > 1){
                // we've got a menu with items already, time to delete all of them
                // except for the first one (NONE).  NOTE: Item 0 == first item
                // be sure to delete in reverse order so you get them all.
                for ( --uNumMenus; uNumMenus; uNumMenus--) {
                        DeleteMenu(hMenu, uNumMenus, MF_BYPOSITION);
                }
        }

        // loop through the languages and add menus to the existing menu
        for (i=0, lplang = lpLangs; i<dwLangs; i++, lplang++){
                AppendMenu(hMenu, MF_ENABLED | MF_STRING, IDM_STREAM+i, lplang->achName);
        }

        // get default set up
        if (dwLangs)
                iCurLang = 1;           // use first audio stream
        else
                iCurLang = NOAUDIO;     // else none

        /* set up the checkmark initially */
        CheckMenuItem(hMenu, (iCurLang), MF_BYPOSITION | MF_CHECKED);
}


/*------------------------------------------------------------------+
| switchLang - switch audio stream playback                                                     |
|                                                                                                                                       |
| iLangStream == the audio stream to switch to (-1 == NONE)                     |
| Be sure to update iCurrLang global to be the current audio stream     |
| selected.                                                                                                                     |
|                                                                                                                                       |
+------------------------------------------------------------------*/
void switchLang(HWND hWnd, int iLangStream)
{
        HMENU                                   hMenu;
        char                                    achStrBuff[256];


        // if user just picked the same stream then just get out of here
        if (iCurLang == iLangStream)
                return;

        // go through menu chain and get the Audio Stream pop-up menu
        hMenu = GetMenu(hWnd);          // get the menu bar
        hMenu = GetSubMenu(hMenu, 1);   // get the Audio Stream menu

        // turn off the checkmark from the old item
        CheckMenuItem(hMenu, (iCurLang), MF_BYPOSITION | MF_UNCHECKED);

        // turn on the checkmark on the new item
        CheckMenuItem(hMenu, (iLangStream), MF_BYPOSITION | MF_CHECKED);

        if (iLangStream == NOAUDIO){
                // turn off all audio
                MCIWndSendString(hwndMovie, "setaudio off");
        } else {
                // turn on audio & the specific stream
                wsprintf(achStrBuff, "setaudio stream to %d", iLangStream);

                // send the command
                MCIWndSendString(hwndMovie, achStrBuff);


                if (iCurLang == NOAUDIO){
                        // audio was off, turn it on
                        MCIWndSendString(hwndMovie, "setaudio on");
                }
        }

        iCurLang = iLangStream;         // set the current stream
}

/* AboutDlgProc()
 *
 * Dialog Procedure for the "about" dialog box.
 *
 */

BOOL CALLBACK AboutDlgProc(
        HWND    hwnd,
        UINT    msg,
        WPARAM  wParam,
        LPARAM  lParam)
{
        switch (msg) {
        case WM_COMMAND:
                EndDialog(hwnd, TRUE);
                return TRUE;
        case WM_INITDIALOG:
                return TRUE;
        }
        return FALSE;
}

