//------------------------------------------------------------------------------
// File: DVApp.cpp
//
// Desc: DirectShow sample code - DV control/capture example.
//
// Copyright (c) 2000-2001 Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------------------------

#include <windows.h>
#include <commdlg.h>
#include <commctrl.h>
#include <tchar.h>
#include <dbt.h>
#include <streams.h>
#include <xprtdefs.h> 
#include "CDVGraph.h"
#include "resource.h"

// ------------ constants-----------------
#define DV_CAPLIMIT_NONE         10L
#define DV_CAPLIMIT_TIME         11L
#define DV_CAPLIMIT_SIZE         12L

#define DV_BYTESPERMSEC          352  //DV captures at 3600K per second (3600 / 1024 == ~3.52)
#define DV_TIMERFREQ             55   //milliseconds between timer ticks
#define DV_BYTES_IN_MEG          1048576L  //(1024 * 1024)

// ----------macros-----------------------
#define MBOX(s)                 MessageBox(g_hwndApp, s, APPNAME, MB_OK);

// ----------function prototypes----------
BOOL            FInitMain(int nCmdShow);
int             NMsgLoop(void);
BOOL            DV_InitControls(HWND hwnd, HINSTANCE hInst);

HRESULT         DV_AppSetup(void);
HRESULT         SetPreviewWindow(void);

// UI help functions
void Mark_ToolBar_Button(BOOL bEnableRecord, BOOL bEnableOthers);
void Mark_GraphMode_Menu(HWND hwnd, int idmVal);
void GetSelectedGraphMode( WPARAM wParam, GRAPH_TYPE* pGraphType);

// Message Processing Functions
LRESULT CALLBACK    MainWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
void CALLBACK       DV_TransportCommand_WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);
void CALLBACK       DV_GraphModeCommand_WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK       DV_CapSizeDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void CALLBACK       DV_DroppedFrameProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime);
void CALLBACK       DV_TimecodeTimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime);
void CALLBACK       DV_StopRecProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime);
BOOL CALLBACK       DV_AboutDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void                DV_DisplayTimecode();
BOOL                DV_SeekATN(void);
BOOL                DV_UpdateTapeInfo(void);
BOOL                DV_RefreshMode(void);

// ------------global data----------------

// Handles to the windows
HINSTANCE       g_hinst;
HWND            g_hwndApp               = NULL;
HWND            g_hwndTBar              = NULL; 
HWND            g_hwndStatus            = NULL;
HMENU           g_hmenu;

// metrics of the windows
int             g_iAppHeight            = DEFAULT_VIDEO_HEIGHT + HEIGHT_EDGE;
int             g_iAppWidth             = DEFAULT_VIDEO_WIDTH  + WIDTH_EDGE;        
int             g_iVWHeight             = DEFAULT_VIDEO_HEIGHT;
int             g_iVWWidth              = DEFAULT_VIDEO_WIDTH;        
DWORD           g_statusHeight          = 0;

// device notification globals
PVOID           g_hDevNotify   = NULL;
BOOL            g_bDeviceFound = FALSE;

// capture variables
DWORD                   g_dwCaptureLimit        = DV_CAPLIMIT_NONE; //track whether we are using time, disk, or no based capture limits
DWORD                   g_dwDiskSpace           = 120;              //roughly the same
DWORD                   g_dwTimeLimit           = 30;               //default to 30 seconds of capture

// graph related variables
CDVGraph*       g_pGraph        = NULL; 
GRAPH_TYPE      g_iGraphType    = GRAPH_PREVIEW;    //need to track the current graph

// file names
TCHAR           g_InputFileName[_MAX_PATH]          = {DEFAULT_CAP_FILE_NAME};
TCHAR           g_OutputFileName[_MAX_PATH]         = {DEFAULT_CAP_FILE_NAME};
TCHAR           g_FilterGraphFileName[_MAX_PATH]    = {DEFAULT_FG_FILE_NAME};

DWORD           g_CapStartTime          = 0;
BOOL            g_bUseAtnTimer          = FALSE;  //track if we want to constantly update the timer
BOOL            g_bHalfFrameRate        = FALSE;


// ------------inline functions-------------------
// put the VCR Mode
inline HRESULT DV_PutVcrMode(long Mode)
{
    if(!g_pGraph->m_pIAMExtTransport)
        return S_FALSE;
    return g_pGraph->m_pIAMExtTransport->put_Mode(Mode);
} 

// update the status windows with appropriate text
inline LRESULT DV_StatusText(LPCTSTR statusText, UINT nPart)
{
    return SendMessage(g_hwndStatus, SB_SETTEXT, (WPARAM) 0 | nPart, (LPARAM)statusText);
} 

//divide the status bar into thirds, and give the middle frame an extra 100 pixels.
inline LRESULT DV_StatusParts(UINT width)
{
    int rg[3];
    rg[0] = (width / 3) - 50;
    rg[1] = ((rg[0]+50) * 2) + 50;
    rg[2] = -1;
    return SendMessage(g_hwndStatus, SB_SETPARTS, 3, (LPARAM)(LPINT) rg);
}    


/*-------------------------------------------------------------------------
Routine:        WinMain
Purpose:        Program entry point      
Arguments:      Usual
Returns:        Usual
Notes:          Sets up window capabilities, initializes & 
                    creates the required DirectShow interface & filters.
------------------------------------------------------------------------*/
int WINAPI WinMain( HINSTANCE hinst,        // instance handle
                    HINSTANCE hinstPrev,    // always NULL
                    LPSTR pszCmd,           // pointer to ANSI command line arguments
                    int nCmdShow)           // initial app window state
{
    CoInitialize(NULL);

    ASSERT(!hinstPrev);
    g_hinst = hinst;
    if (!FInitMain(nCmdShow))
    {
        return -1;
    }

    // Register for device add/remove notifications.
    DEV_BROADCAST_DEVICEINTERFACE filterData;
    ZeroMemory(&filterData, sizeof(DEV_BROADCAST_DEVICEINTERFACE));   
    filterData.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
    filterData.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
    filterData.dbcc_classguid = AM_KSCATEGORY_CAPTURE;
    g_hDevNotify = RegisterDeviceNotification(g_hwndApp, &filterData, DEVICE_NOTIFY_WINDOW_HANDLE);        
    ASSERT(g_hDevNotify != NULL);

    // dvapp setup 
    DV_AppSetup();

    return NMsgLoop();
}

/*--------------------------------------------------------------------------
Routine:  FInitMain
Purpose:  Initialize the main application window.
Arguments:int initial app window state
Returns:  Return TRUE if successful.
------------------------------------------------------------------------*/
BOOL FInitMain( int nCmdShow)
{
    WNDCLASSEX      wc;

    INITCOMMONCONTROLSEX icc;
    // register common control classes
    icc.dwSize = sizeof(icc);
    icc.dwICC = ICC_BAR_CLASSES;
    InitCommonControlsEx(&icc);

    // register the window class
    ZeroMemory(&wc, sizeof(wc));
    wc.cbSize           = sizeof(wc);
    wc.style            = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc      = MainWndProc;
    wc.cbClsExtra       = 0 ;
    wc.cbWndExtra       = 0 ;
    wc.hInstance        = g_hinst;
    wc.hIcon            = LoadIcon (g_hinst, TEXT("DVICON")) ;
    wc.hCursor          = LoadCursor (NULL, IDC_ARROW) ;
    wc.hbrBackground    = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
    wc.lpszMenuName     = NULL ;
    wc.lpszClassName    = APPNAME ;
    wc.hIconSm          = LoadIcon(g_hinst, TEXT("DVICON"));   
    if (!RegisterClassEx(&wc))
    {
        MessageBox(NULL, TEXT("cannot register the window class"), TEXT("Error"), 0);
        return FALSE;
    }

    g_hmenu = LoadMenu(g_hinst, MAKEINTRESOURCE(IDR_MENU));
    // create the main application window
    g_hwndApp = CreateWindowEx(
        0,
        APPNAME,    // window class name
        DV_APPTITLE,    // window caption
        WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX, // window style
        CW_USEDEFAULT,  // initial x position
        CW_USEDEFAULT,  // initial y position
        g_iAppWidth,    // initial x size
        g_iAppHeight,   // initial y size
        NULL,           // parent window handle
        g_hmenu,        // window menu handle
        g_hinst,        // program instance handle
        0) ;            // creation parameters
    
    if (!g_hwndApp)
    {
        MBOX(TEXT("Create main window failed"));
        return FALSE;
    }

    // show the window
    ShowWindow(g_hwndApp, nCmdShow);
    UpdateWindow(g_hwndApp);
    SetWindowText(g_hwndApp, TEXT("Initializing..."));

    return TRUE;
}

/*--------------------------------------------------------------------------
Routine:  NMsgLoop
Purpose:  Execute the program message loop.
Arguments:None
Returns:  Return the wParam field of the final WM_QUIT message.
------------------------------------------------------------------------*/ 
int NMsgLoop(void)
{
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {   
        TranslateMessage(&msg);
        DispatchMessage(&msg);              
    }

    return ((int) msg.wParam);
}

/*--------------------------------------------------------------------------
Routine:  MainWndProc
Purpose:  Window procedure for the main application window.
Arguments:Usual
Returns:  Usual
------------------------------------------------------------------------*/ 
LRESULT CALLBACK MainWndProc(
    HWND hwnd,
    UINT msg,
    WPARAM wparam,
    LPARAM lparam)
{
    switch (msg)
    {
        case WM_CREATE:
            if (!DV_InitControls(hwnd, (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE)))
            {
                MBOX(TEXT("DV_InitControls failed to create one of the control windows"));
            }
            break;

        case WM_SIZE:
        {
            RECT  rcAppWin, rcClient, rcTB;
            //re-size the App window
            int cxBorder = GetSystemMetrics(SM_CXBORDER);
            int cyBorder = GetSystemMetrics(SM_CYBORDER);
            GetWindowRect(g_hwndApp, &rcAppWin);
            GetWindowRect(g_hwndTBar, &rcTB);
            MoveWindow(g_hwndApp, rcAppWin.left, rcAppWin.top, g_iAppWidth, g_iAppHeight, TRUE);

            // Tell the toolbar to resize itself to fill the top of the window.
            SendMessage(g_hwndTBar, TB_AUTOSIZE, 0L, 0L);

            //handle the status bar height
            GetClientRect(g_hwndApp, &rcClient);
            cxBorder = GetSystemMetrics(SM_CXBORDER);
            cyBorder = GetSystemMetrics(SM_CYBORDER);
            MoveWindow(g_hwndStatus, -cxBorder, rcClient.bottom - (g_statusHeight + cyBorder), 
                       rcClient.right + (2 * cxBorder), (g_statusHeight + (2 * cyBorder)), TRUE);  

            DV_StatusParts(rcClient.right);
        
            //re-size the video window
            GetWindowRect(g_hwndTBar, &rcTB);
            if (g_pGraph )
            {
                if(g_pGraph->m_pVideoWindow)
                {
                    g_pGraph->m_pVideoWindow->SetWindowPosition(0, rcTB.bottom - rcTB.top, g_iVWWidth, g_iVWHeight);
                }
            }
            break;          
        }           

        case WM_CLOSE:
            return SendMessage(hwnd, WM_DESTROY, 0,0);

        case WM_DESTROY:
            // Unregister device notifications
            if (g_hDevNotify != NULL)
            {
                UnregisterDeviceNotification(g_hDevNotify);
                g_hDevNotify = NULL;
            }       
            //  Kill the graph
            if(g_pGraph != NULL)
                delete g_pGraph;

            CoUninitialize();

            PostQuitMessage(0);
            break;

        case WM_DEVICECHANGE:
        {
            // We are interested in only device arrival events
            if (DBT_DEVICEARRIVAL != wparam)
                break;
            PDEV_BROADCAST_HDR pdbh = (PDEV_BROADCAST_HDR) lparam;
            if (pdbh->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE)
                break;

            // Check for capture devices.
            PDEV_BROADCAST_DEVICEINTERFACE pdbi = (PDEV_BROADCAST_DEVICEINTERFACE) lparam;
            if (pdbi->dbcc_classguid != AM_KSCATEGORY_CAPTURE)
                break;

            // Check for device arrival.
            if (g_bDeviceFound == FALSE)
            {
                MessageBox(g_hwndApp, TEXT("There is a new capture device on this system..."), APPNAME, MB_OK);
                DV_AppSetup();
            }
            break;
        }

       case WM_FGNOTIFY :            //Filter Graph Events Notify Message handler
       {
           if(g_pGraph->m_pMediaEvent == NULL)
               break;

            long event;
            LONG_PTR l1, l2;
            // Get the corresponding filtergraph directshow media event to handle
            while (SUCCEEDED(g_pGraph->m_pMediaEvent->GetEvent(&event, &l1, &l2, 0L)))
            {
                switch (event)
                {
                case EC_USERABORT :
                case EC_COMPLETE :
                    // filtergraph is done, time to stop the filtergraph or transport state
                    SendMessage(g_hwndApp, WM_COMMAND, IDM_STOP, 0);  
                    break;
            
                case EC_ERRORABORT :
                    //something bad happened during capture
                    MBOX(TEXT("Error during preview, capture or transmit"));
                    break;

                case EC_DEVICE_LOST :   // Check if we have lost a capture filter being used.
                    if (l2 == 0)        //0 indicates device removed;1 indicates removed device added again
                    {
                        IBaseFilter *pf;
                        IUnknown *punk = (IUnknown *) l1;
                        HRESULT hr = punk->QueryInterface(IID_IBaseFilter, (void **) &pf);
                        if(FAILED(hr))
                            break;
                    
                        if (::IsEqualObject(g_pGraph->m_pDeviceFilter, pf))
                        {
                            MBOX(TEXT("DV Camcorder Device in use was removed"));

                            // handle the timers accordingly
                            if (g_bUseAtnTimer)
                                KillTimer(hwnd, DV_TIMER_ATN);
                            KillTimer(hwnd, DV_TIMER_CAPLIMIT);
                            KillTimer(hwnd, DV_TIMER_FRAMES);
                            g_bDeviceFound = FALSE;

                            // should call nuke filters....????
                            if(g_pGraph != NULL)
                                delete g_pGraph; //  pf is released
                            DV_AppSetup();
                        }
                        else
                            pf->Release();               
                    }
                    break;
                default:
                    break;

                }// end of switch
                if(g_pGraph->m_pMediaEvent == NULL)
               break;
            } // end of while
            break;
        }

        case WM_COMMAND:
        switch (LOWORD(wparam))
        {
            // The File Menu 
            /*  
                The globals for input file and output file are TCHAR.  When compiled ANSI,
                GetOpenFileName (obviously) return ANSI buffers.  DShow functions use Unicode
                names exclusively, so these are converted to Unicode within the functions 
                that need to use these variables (DV_Make...To... functions).
            */
            case IDM_SETINPUT :    //fall through
            case IDM_SETOUTPUT :
            case IDM_OPTIONS_SAVEGRAPH :
            {
                OPENFILENAME ofn = {0};
                OSVERSIONINFO osi = {0};
                //need to adjust the ofn struct size if we are running on win98 vs. nt5
                osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
                GetVersionEx(&osi);
                if (osi.dwMajorVersion >=5 && osi.dwPlatformId == VER_PLATFORM_WIN32_NT)
                {
                    ofn.lStructSize = sizeof (OPENFILENAME);
                } 
                else
                {
                    ofn.lStructSize = sizeof(OPENFILENAME);
                }   
            
                ofn.hwndOwner = g_hwndApp;
                ofn.nMaxFile = _MAX_PATH;
                if (IDM_OPTIONS_SAVEGRAPH == LOWORD (wparam))
                {
                    ofn.lpstrFilter = TEXT("Filter Graph\0*.grf\0\0");
                    ofn.lpstrTitle = TEXT("Set FilterGraph File Name");
                    ofn.lpstrFile = g_FilterGraphFileName;
                } 
                else if (IDM_SETOUTPUT == LOWORD (wparam))
                {
                    ofn.lpstrFilter = TEXT("Microsoft AVI\0*.avi\0\0");
                    ofn.lpstrTitle = TEXT("Set output File Name");
                    ofn.lpstrFile = g_OutputFileName;
                } 
                else
                {
                    ofn.lpstrFilter = TEXT("Microsoft AVI\0*.avi\0\0");
                    ofn.lpstrTitle = TEXT("Set input File Name");
                    ofn.lpstrFile = g_InputFileName;
                } 

                if (GetOpenFileName(&ofn))
                {
                    if (IDM_OPTIONS_SAVEGRAPH == LOWORD (wparam))
                    {
                        lstrcpy(g_FilterGraphFileName, ofn.lpstrFile);
                        // Save the current built filter graph to a *.grf file
                        if(g_pGraph != NULL)
                            g_pGraph->SaveGraphToFile(g_FilterGraphFileName);
                        
                    } 
                    else if (IDM_SETOUTPUT == LOWORD (wparam))
                    {
                        lstrcpy(g_OutputFileName, ofn.lpstrFile);
                    } 
                    else
                    {
                        lstrcpy(g_InputFileName, ofn.lpstrFile);
                    } 
                }
                break;    
            } 
            case IDM_ABOUT:
                DialogBox((HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE), MAKEINTRESOURCE(IDD_ABOUT), hwnd, (DLGPROC)DV_AboutDlgProc);
                break;

            // Toolbar Button Commands
            // Transport State & Graph State Control
            case IDM_PLAY :
            case IDM_RECORD :
            case IDM_STOP : 
            case IDM_PAUSE :
            case IDM_FF :
            case IDM_REW :
            case IDM_PLAY_FAST_FF :
            case IDM_PLAY_FAST_REV :        
            case IDM_STEP_FWD :
            case IDM_STEP_REV :
            case IDM_SEEKTIMECODE :
                DV_TransportCommand_WndProc(hwnd, msg, wparam, lparam);
                break;

            // The Options Menu
            case IDM_DECODESIZE :
                g_pGraph->StopGraph();
                g_pGraph->GetVideoWindowDimensions(&g_iVWWidth, &g_iVWHeight, TRUE, g_hwndApp);
                g_iAppWidth = g_iVWWidth + WIDTH_EDGE;
                g_iAppHeight = g_iVWHeight + HEIGHT_EDGE;
                //resize the window
                PostMessage(g_hwndApp, WM_SIZE, SIZE_RESTORED, MAKELONG(g_iAppWidth, g_iAppHeight));
                break;

            case IDM_CHECKTAPE :
                DV_UpdateTapeInfo();
                break;

            case IDM_REFRESHMODE :
                DV_RefreshMode();
                break;

            case IDM_FRAMERATE :
                if( g_bHalfFrameRate )
                {
                    g_bHalfFrameRate = FALSE;
                    CheckMenuItem(g_hmenu, IDM_FRAMERATE, MF_UNCHECKED);
                }
                else
                {
                    g_bHalfFrameRate = TRUE;
                    CheckMenuItem(g_hmenu, IDM_FRAMERATE, MF_CHECKED);                   
                }
                g_pGraph->ChangeFrameRate(g_bHalfFrameRate);
                break;

            case IDM_CAPSIZE:
              DialogBox((HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE), MAKEINTRESOURCE(IDD_DIALOG_CAPSIZE), hwnd, (DLGPROC)DV_CapSizeDlgProc);
              break;

            case IDM_EXIT:
                return SendMessage(hwnd, WM_CLOSE, 0, 0);
                
            // Graph Mode Menu
            // Change the Current Graph Mode
            case IDM_PREVIEW :
            case IDM_FILETODV :
            case IDM_FILETODV_NOPRE :
            case IDM_DVTOFILE :
            case IDM_DVTOFILE_NOPRE :
            case IDM_FILETODV_TYPE2 :
            case IDM_FILETODV_NOPRE_TYPE2 :
            case IDM_DVTOFILE_TYPE2 :
            case IDM_DVTOFILE_NOPRE_TYPE2 :            
                DV_GraphModeCommand_WndProc (hwnd, msg, wparam, lparam);
                
                break;
        }
        break;

        default:
            return DefWindowProc(hwnd, msg, wparam, lparam);
    }

    return 0;
}

/*-------------------------------------------------------------------------
Routine:        DV_InitControls
Purpose:        Initializer for app window controls (toolbars, edit controls, etc.)
Arguments:    window handle and hinstance
Returns:        FALSE if creation of any of the controls fails.
Notes:          
------------------------------------------------------------------------*/
BOOL DV_InitControls(HWND hwnd, HINSTANCE hInst)
{
    HWND hwndEdit1 = NULL;
    HWND hwndEdit2 = NULL;
    HWND hwndEdit3 = NULL;
    HWND hwndEdit4 = NULL;
    HWND hwndTCCheck = NULL;

    RECT rect = {0};

    // create status bar windows
    g_hwndStatus = CreateWindowEx( 0,
                                   STATUSCLASSNAME,
                                   TEXT(""),
                                   WS_CHILD | WS_BORDER | WS_VISIBLE | WS_CLIPSIBLINGS,
                                   -100, -100,
                                   10, 10,
                                   hwnd,
                                   HMENU(IDB_STATUS),
                                   g_hinst,
                                   NULL);

    DV_StatusParts(g_iAppWidth);
    g_statusHeight = rect.bottom;

    // create toolbar window
    g_hwndTBar = CreateToolbarEx(hwnd, 
                                   WS_CHILD | WS_VISIBLE | WS_BORDER | TBSTYLE_TOOLTIPS, 
                                   ID_TOOLBAR, 
                                   10, 
                                   g_hinst, 
                                   IDB_TOOLBAR, 
                                   g_rgTbButtons, 
                                   sizeof(g_rgTbButtons) / sizeof(TBBUTTON), 
                                   16,16,16,16, 
                                   sizeof(TBBUTTON)); 

    // create timecode text boxes on the toolbar        
    rect.right = 350;
    hwndEdit1 = CreateWindow(TEXT("edit"), TEXT("00"), WS_CHILD | WS_BORDER | WS_VISIBLE | WS_TABSTOP | ES_NUMBER, 
                    rect.right + 6, 4, 22, 18, g_hwndTBar, (HMENU)IDC_EDIT_HOUR, (HINSTANCE) hInst, NULL);
    
    hwndEdit2 = CreateWindow(TEXT("edit"), TEXT("00"), WS_CHILD | WS_BORDER | WS_VISIBLE | WS_TABSTOP | ES_NUMBER,  
                    rect.right + 30, 4, 22, 18, g_hwndTBar, (HMENU)IDC_EDIT_MINUTE, (HINSTANCE) hInst, NULL);
    
    hwndEdit3 = CreateWindow(TEXT("edit"), TEXT("00"), WS_CHILD | WS_BORDER | WS_VISIBLE | WS_TABSTOP | ES_NUMBER, 
                    rect.right + 54, 4, 22, 18, g_hwndTBar, (HMENU)IDC_EDIT_SECOND, (HINSTANCE) hInst, NULL);

    hwndEdit4 = CreateWindow(TEXT("edit"), TEXT("00"), WS_CHILD | WS_BORDER | WS_VISIBLE | WS_TABSTOP | ES_NUMBER, 
                    rect.right + 78, 4, 22, 18, g_hwndTBar, (HMENU)IDC_EDIT_FRAME, (HINSTANCE) hInst, NULL);

    hwndTCCheck = CreateWindow(TEXT("button"), TEXT("Display Timecodes"), WS_CHILD | BS_AUTOCHECKBOX | WS_VISIBLE | WS_TABSTOP, 
                    rect.right + 106, 5, 190, 18, g_hwndTBar, (HMENU)IDC_TCCHECKBOX, (HINSTANCE) hInst, NULL); 
    Button_SetCheck (hwndTCCheck, BST_CHECKED) ;                    

    return (!( !hwndEdit1) || (!hwndEdit2) || (!hwndEdit3) || (!hwndEdit4) || (!hwndTCCheck) || (!g_hwndStatus));
} 

/*-------------------------------------------------------------------------
Routine:        DV_AppSetup
Purpose:        look for a DV device, initialize it, get its subunit mode, 
                    Create the filtergraph and instantiate the filters
Arguments:    None
Returns:        None
Notes:          
------------------------------------------------------------------------*/
HRESULT DV_AppSetup(void)
{   
    HRESULT hr =S_OK;
    g_pGraph = new CDVGraph();
    if ( !g_pGraph ){
       hr = E_OUTOFMEMORY;
       return hr;
    }
    
    //look for a DV device and initialize it, or show an error if one does not exist 
    hr = g_pGraph->BuildBasicGraph();
    if (S_OK != hr) 
    {
        g_bDeviceFound = FALSE;
        int iOption = MessageBox(g_hwndApp, TEXT("There are no DV camcorder devices on this system.\n\n")
                                 TEXT("Do you want to exit the app?"), APPNAME, MB_YESNO);
        if(iOption == IDYES) 
            SendMessage(g_hwndApp, WM_DESTROY, 0,0);
        else
        {
            ShowWindow(g_hwndTBar, SW_HIDE);

            EnableMenuItem(g_hmenu, IDM_REFRESHMODE, MF_GRAYED);
            EnableMenuItem(g_hmenu, IDM_CHECKTAPE, MF_GRAYED);
            EnableMenuItem(g_hmenu, IDM_DECODESIZE, MF_GRAYED);

            EnableMenuItem(g_hmenu, IDM_PREVIEW, MF_GRAYED);
            EnableMenuItem(g_hmenu, IDM_DVTOFILE, MF_GRAYED);
            EnableMenuItem(g_hmenu, IDM_DVTOFILE_NOPRE, MF_GRAYED);
            EnableMenuItem(g_hmenu, IDM_FILETODV, MF_GRAYED);
            EnableMenuItem(g_hmenu, IDM_FILETODV_NOPRE, MF_GRAYED);
            EnableMenuItem(g_hmenu, IDM_DVTOFILE_TYPE2, MF_GRAYED);
            EnableMenuItem(g_hmenu, IDM_DVTOFILE_NOPRE_TYPE2, MF_GRAYED);
            EnableMenuItem(g_hmenu, IDM_FILETODV_TYPE2, MF_GRAYED);
            EnableMenuItem(g_hmenu, IDM_FILETODV_NOPRE_TYPE2, MF_GRAYED);
        }
    }
    else
    {
        g_bDeviceFound = TRUE;
        ShowWindow(g_hwndTBar, SW_SHOWNORMAL);

        EnableMenuItem(g_hmenu, IDM_REFRESHMODE, MF_ENABLED);
        EnableMenuItem(g_hmenu, IDM_CHECKTAPE, MF_ENABLED);
        EnableMenuItem(g_hmenu, IDM_DECODESIZE, MF_ENABLED);

        EnableMenuItem(g_hmenu, IDM_PREVIEW, MF_ENABLED);
        EnableMenuItem(g_hmenu, IDM_DVTOFILE, MF_ENABLED);
        EnableMenuItem(g_hmenu, IDM_DVTOFILE_NOPRE, MF_ENABLED);
        EnableMenuItem(g_hmenu, IDM_FILETODV, MF_ENABLED);
        EnableMenuItem(g_hmenu, IDM_FILETODV_NOPRE, MF_ENABLED);
        EnableMenuItem(g_hmenu, IDM_DVTOFILE_TYPE2, MF_ENABLED);
        EnableMenuItem(g_hmenu, IDM_DVTOFILE_NOPRE_TYPE2, MF_ENABLED);
        EnableMenuItem(g_hmenu, IDM_FILETODV_TYPE2, MF_ENABLED);
        EnableMenuItem(g_hmenu, IDM_FILETODV_NOPRE_TYPE2, MF_ENABLED);


        //determine if we are in camera mode or vcr mode and disable unavailable menu items
        DV_RefreshMode();

        DV_StatusText(g_pGraph->m_DeviceName, 0);


        // make preview graph
        hr= g_pGraph->MakePreviewGraph( );
        if(FAILED(hr))
        {
            MBOX(TEXT("MakePreviewGraph() failed"));
            return hr;
        }  

        //get and set the display window size
        g_pGraph->GetVideoWindowDimensions(&g_iVWWidth, &g_iVWHeight, FALSE, NULL);
        g_iAppWidth = g_iVWWidth + WIDTH_EDGE;
        g_iAppHeight = g_iVWHeight + HEIGHT_EDGE;
        PostMessage(g_hwndApp, WM_SIZE, SIZE_RESTORED, MAKELONG(g_iAppWidth, g_iAppHeight));

        // register a window to process event notifications.     
        hr = g_pGraph->m_pMediaEvent->SetNotifyWindow((LONG_PTR) g_hwndApp, WM_FGNOTIFY, 0);
        if(FAILED(hr))
        {
            MBOX(TEXT("g_pGraph->m_pMediaEvent->SetNotifyWindow() failed"));
            return hr;
        }
     
        // update globals, log, toolbar, menu items for preview mode
        g_iGraphType = GRAPH_PREVIEW;
        Mark_GraphMode_Menu( g_hwndApp, IDM_PREVIEW );   
        Mark_ToolBar_Button( FALSE, TRUE );    
        SetPreviewWindow();
        
        // ready for user
        SetWindowText(g_hwndApp, DV_APPTITLE);     
    }

    return hr;
}

/*-------------------------------------------------------------------------
Routine:        DV_GraphModeCommand_WndProc
Purpose:        Message Handler for Graph Mode menu items
Arguments:    Usual message processing parameters
Returns:        Usual
Notes:          Builds the various kinds of Graph types preview/capture/transmit
------------------------------------------------------------------------*/
void CALLBACK DV_GraphModeCommand_WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{ 
    HRESULT hr;
    GRAPH_TYPE graphType;
    GetSelectedGraphMode( wParam, &graphType );

    // dont rebuild if the required graph already exists
    if( graphType == g_iGraphType )
        return;

    //stop the graph - this should never fail before rebuilding
    if(FAILED( g_pGraph->m_pMediaControl->Stop()))
    {
        return;
    }


    //let's look at the former graph
    //Disconnect everything
    if (GRAPH_FILE_TO_DV == g_iGraphType || GRAPH_FILE_TO_DV_NOPRE == g_iGraphType ||
        GRAPH_FILE_TO_DV_TYPE2 == g_iGraphType || GRAPH_FILE_TO_DV_NOPRE_TYPE2 == g_iGraphType)
    {
        //DisconnectAll removes only the downstream filters - we need to remove the file filter by hand.
         g_pGraph->NukeFilters( g_pGraph->m_pInputFileFilter, TRUE);
        if (g_pGraph->m_pInputFileFilter )
        {
            g_pGraph->m_pGraph->RemoveFilter( g_pGraph->m_pInputFileFilter );
            SAFE_RELEASE( g_pGraph->m_pInputFileFilter );        
        }
    } 
    else
    {
        //DisconnectAll removes only the downstream filters         
        hr = g_pGraph->NukeFilters( g_pGraph->m_pDeviceFilter, TRUE);
        if(FAILED(hr))
        {
            MBOX(TEXT("nukefilters() failed"));
        }
    }
    g_iGraphType = graphType;

    switch (LOWORD (wParam))
    {   
    case IDM_PREVIEW :
        if(SUCCEEDED(g_pGraph->MakePreviewGraph( )))
        {
            // update globals, log, toolbar, menu items
            SetPreviewWindow();
            Mark_GraphMode_Menu( hwnd, IDM_PREVIEW );   
            Mark_ToolBar_Button( FALSE, FALSE );    
        }
        else
            MBOX(TEXT("MakePreviewGraph() failed"));

        break;

    // Type 1 file (transmit & playback)    
    case IDM_FILETODV :
        if(SUCCEEDED(g_pGraph->MakeFileToDvGraph_Type1( g_InputFileName )))
        {
            // update globals, log, toolbar, menu items
            SetPreviewWindow();
            Mark_GraphMode_Menu( hwnd,IDM_FILETODV );   
            Mark_ToolBar_Button( TRUE, FALSE ); 
        } 
        else
            MBOX(TEXT("MakeFileToDvGraph_Type1() failed"));
    
        break;

    // Type 1 file (transmit)   
    case IDM_FILETODV_NOPRE :
        if(SUCCEEDED(g_pGraph->MakeFileToDvGraph_NoPre_Type1( g_InputFileName )))
        {
            // update globals, log, toolbar, menu items
            Mark_GraphMode_Menu( hwnd,IDM_FILETODV_NOPRE );   
            Mark_ToolBar_Button( TRUE, FALSE ); 
        }
        else
            MBOX(TEXT("MakeFileToDvGraph_NoPre_Type1() failed")); 
        break;
        
    // Type 1 file (capture & preview)  
    case IDM_DVTOFILE :
        if(SUCCEEDED(g_pGraph->MakeDvToFileGraph_Type1( g_OutputFileName )))
        {
            // update globals, log, toolbar, menu items
            SetPreviewWindow();
            Mark_GraphMode_Menu( hwnd,IDM_DVTOFILE );   
            Mark_ToolBar_Button( TRUE, TRUE );  
        }
        else
            MBOX(TEXT("MakeDvToFileGraph_Type1() failed"));
            
        break;

    // Type 1 file (capture)    
    case IDM_DVTOFILE_NOPRE :
        if(SUCCEEDED(g_pGraph->MakeDvToFileGraph_NoPre_Type1( g_OutputFileName )))
        {
            // update globals, log, toolbar, menu items
            Mark_GraphMode_Menu( hwnd,IDM_DVTOFILE_NOPRE );   
            Mark_ToolBar_Button( TRUE, TRUE );  
        }
        else
            MBOX(TEXT("MakeDvToFileGraph_NoPre_Type1() failed"));
        
        break;

    // Type 2 File (transmit & playback)
    case IDM_FILETODV_TYPE2 :
        if(SUCCEEDED(g_pGraph->MakeFileToDvGraph_Type2( g_InputFileName )))
        {
            // update globals, log, toolbar, menu items
            SetPreviewWindow();
            Mark_GraphMode_Menu( hwnd,IDM_FILETODV_TYPE2 );   
            Mark_ToolBar_Button( TRUE, FALSE ); 
        }
        else
            MBOX(TEXT("MakeFileToDvGraph_Type2() failed"));
                
        break;

    // Type 2 File (transmit)
    case IDM_FILETODV_NOPRE_TYPE2 :
        if(SUCCEEDED(g_pGraph->MakeFileToDvGraph_NoPre_Type2( g_InputFileName )))
        {
            // update globals, log, toolbar, menu items
            Mark_GraphMode_Menu( hwnd,IDM_FILETODV_NOPRE_TYPE2 );   
            Mark_ToolBar_Button( TRUE, FALSE ); 
        }
        else
            MBOX(TEXT("MakeFileToDvGraph_NoPre_Type2() failed"));
                
        break;
        
    // Type 2 File (capture & preview)
    case IDM_DVTOFILE_TYPE2 :
        if(SUCCEEDED(g_pGraph->MakeDvToFileGraph_Type2( g_OutputFileName )))
        {
            // update globals, log, toolbar, menu items
            SetPreviewWindow();
            Mark_GraphMode_Menu( hwnd,IDM_DVTOFILE_TYPE2 );   
            Mark_ToolBar_Button( TRUE, TRUE );  
        } 
        else
            MBOX(TEXT("MakeDvToFileGraph_Type2() failed"));
    
        break;

    // Type 2 File (capture)
    case IDM_DVTOFILE_NOPRE_TYPE2 :
        if(SUCCEEDED(g_pGraph->MakeDvToFileGraph_NoPre_Type2( g_OutputFileName )))
        {
            // update globals, log, toolbar, menu items
            Mark_GraphMode_Menu( hwnd,IDM_DVTOFILE_NOPRE_TYPE2 );   
            Mark_ToolBar_Button( TRUE, TRUE );  
        } 
        else
            MBOX(TEXT("MakeDvToFileGraph_NoPre_Type2() failed"));
        
        break;
    } // switch (LOWORD(wParam))
}

/*-------------------------------------------------------------------------
Routine:        DV_TransportCommand_WndProc 
Purpose:        Message Handler to control transport state of the device & the filtergraph state
Arguments:    Usual message processing parameters
Returns:        Usual
Notes:          Handles for the Toolbar button controls
------------------------------------------------------------------------*/
void CALLBACK DV_TransportCommand_WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
    switch (iMsg)
    {
    case WM_COMMAND :
        switch (LOWORD (wParam))
        {
        /*
            The VCR Tool bar commands need to behave differently depending on the 
            current graph.  

            In Preview (default) mode, the VCR commands should simply
            control the VCR functions on the vcr device.

            In DV To File mode, or File To DV mode the commands should start and stop the graph, 
            as  well as control the vcr mode (although we will disable some buttons to avoid confusion)
        */
        case IDM_PLAY :
        {
            //check if the current graph is a transmit graph
            if (GRAPH_FILE_TO_DV == g_iGraphType || GRAPH_FILE_TO_DV_NOPRE == g_iGraphType || 
                GRAPH_FILE_TO_DV_TYPE2 == g_iGraphType || GRAPH_FILE_TO_DV_NOPRE_TYPE2 == g_iGraphType)
            {
                // update the toolbar accordingly
                SendMessage(g_hwndTBar, TB_SETSTATE, IDM_PLAY, MAKELONG(TBSTATE_INDETERMINATE, 0L));
                // update the status bar with the dropped frames information
                g_CapStartTime = GetTickCount();
                SetTimer(hwnd, DV_TIMER_FRAMES,  DV_TIMERFREQ, (TIMERPROC) DV_DroppedFrameProc);
                // run the filter graph & wait for dshow events
                g_pGraph->StartGraph();
 
            } 
            else //capture and preview
            {
                // play the tape for the capture or preview graph
                DV_PutVcrMode(ED_MODE_PLAY);

                if(GRAPH_PREVIEW == g_iGraphType)
                    g_pGraph->StartGraph();

                //do we want to display timecodes?
                if (IsDlgButtonChecked(g_hwndTBar, IDC_TCCHECKBOX))
               {
                    SetTimer(hwnd, DV_TIMER_ATN,  DV_TIMERFREQ, (TIMERPROC) DV_TimecodeTimerProc);
                    g_bUseAtnTimer = TRUE;
                } 
            } 
            break;
        }

        /*
            The record button starts the *entire* graph, so preview (if selected), won't
            start until the recording starts.  
        */
        case IDM_RECORD :
            // update the toolbar accordingly
            SendMessage(g_hwndTBar, TB_SETSTATE, IDM_STEP_FWD, MAKELONG(TBSTATE_INDETERMINATE, 0L));
            SendMessage(g_hwndTBar, TB_SETSTATE, IDM_STEP_REV, MAKELONG(TBSTATE_INDETERMINATE, 0L));

            // check to see if it is a capture graph
            if (GRAPH_DV_TO_FILE == g_iGraphType || GRAPH_DV_TO_FILE_NOPRE == g_iGraphType ||
                GRAPH_DV_TO_FILE_TYPE2 == g_iGraphType || GRAPH_DV_TO_FILE_NOPRE_TYPE2 == g_iGraphType)
            {
                //do something here to record to an avi file on the disk - or to start recording on the vcr.
                switch (g_dwCaptureLimit)
                {
                    case DV_CAPLIMIT_NONE :
                        break;
                    case DV_CAPLIMIT_TIME :
                        SetTimer(hwnd, DV_TIMER_CAPLIMIT, g_dwTimeLimit * 1000, (TIMERPROC) DV_StopRecProc);
                        break;
                    case DV_CAPLIMIT_SIZE :
                        //rather than monitor disk usage, we'll just do the math and set a timer
                        SetTimer(hwnd, DV_TIMER_CAPLIMIT, ((g_dwDiskSpace * 100000) / DV_BYTESPERMSEC), (TIMERPROC) DV_StopRecProc);
                        break;
                    default :
                        //MBOX(TEXT("Bad value for g_dwCaptureLimit (%d)"), g_dwCaptureLimit);
                        break;
                }
                //update the status bar with the dropped frames information
                g_CapStartTime = GetTickCount();
                SetTimer(hwnd, DV_TIMER_FRAMES,  DV_TIMERFREQ, (TIMERPROC) DV_DroppedFrameProc);
                //run the graph - assume that the camera is already playing if in Vcr mode
                g_pGraph->StartGraph();             
            } 
            else if (GRAPH_FILE_TO_DV == g_iGraphType || GRAPH_FILE_TO_DV_NOPRE == g_iGraphType ||
                     GRAPH_FILE_TO_DV_TYPE2 == g_iGraphType || GRAPH_FILE_TO_DV_NOPRE_TYPE2 == g_iGraphType)
            {
                // if transmit graph then record on tape of the device
                DV_PutVcrMode(ED_MODE_RECORD);
            } 
            else
            {
                //we shouldn't get here
                MBOX( TEXT("Undefined graph mode (maybe GRAPH_PREVIEW) in IDM_RECORD message"));
            } 
            break;

        case IDM_STOP :   
            // handle the timers accordingly
            if (g_bUseAtnTimer)
                KillTimer(hwnd, DV_TIMER_ATN);
            //if we're here, these timers were set
            KillTimer(hwnd, DV_TIMER_CAPLIMIT);
            KillTimer(hwnd, DV_TIMER_FRAMES);

            g_pGraph->StopGraph();
 
            // Stop the transport on the device
            DV_PutVcrMode(ED_MODE_STOP);
            // update the toolbar 
            SendMessage(g_hwndTBar, TB_SETSTATE, IDM_PLAY, MAKELONG(TBSTATE_ENABLED, 0L));
            SendMessage(g_hwndTBar, TB_SETSTATE, IDM_STEP_FWD, MAKELONG(TBSTATE_INDETERMINATE, 0L));
            SendMessage(g_hwndTBar, TB_SETSTATE, IDM_STEP_REV, MAKELONG(TBSTATE_INDETERMINATE, 0L));
            break;

        case IDM_PAUSE :
            if (GRAPH_FILE_TO_DV == g_iGraphType || GRAPH_FILE_TO_DV_NOPRE == g_iGraphType ||
                GRAPH_FILE_TO_DV_TYPE2 == g_iGraphType || GRAPH_FILE_TO_DV_NOPRE_TYPE2 == g_iGraphType)
            {   // transmit graph
                g_pGraph->PauseGraph();
            } 
            else
            {   // capture or preview graph
                DV_PutVcrMode(ED_MODE_FREEZE);
                SendMessage(g_hwndTBar, TB_SETSTATE, IDM_STEP_FWD, MAKELONG(TBSTATE_ENABLED, 0L));
                SendMessage(g_hwndTBar, TB_SETSTATE, IDM_STEP_REV, MAKELONG(TBSTATE_ENABLED, 0L));
            } 
            //SendMessage(g_hwndTBar, TB_SETSTATE, IDM_PLAY, MAKELONG(TBSTATE_ENABLED, 0L));
            break;    

        case IDM_FF :
            // all graphs just forward the tape & update the toolbar
            DV_PutVcrMode(ED_MODE_FF);
            SendMessage(g_hwndTBar, TB_SETSTATE, IDM_STEP_FWD, MAKELONG(TBSTATE_INDETERMINATE, 0L));
            SendMessage(g_hwndTBar, TB_SETSTATE, IDM_STEP_REV, MAKELONG(TBSTATE_INDETERMINATE, 0L));
            break;

        case IDM_REW :
            // all graphs just rewind the tape & update the toolbar
            DV_PutVcrMode(ED_MODE_REW);
            SendMessage(g_hwndTBar, TB_SETSTATE, IDM_STEP_FWD, MAKELONG(TBSTATE_INDETERMINATE, 0L));
            SendMessage(g_hwndTBar, TB_SETSTATE, IDM_STEP_REV, MAKELONG(TBSTATE_INDETERMINATE, 0L));
            break;

        case IDM_PLAY_FAST_FF :
            // all graphs just forward the tape & update the toolbar
            DV_PutVcrMode(ED_MODE_PLAY_FASTEST_FWD);
            SendMessage(g_hwndTBar, TB_SETSTATE, IDM_STEP_FWD, MAKELONG(TBSTATE_INDETERMINATE, 0L));
            SendMessage(g_hwndTBar, TB_SETSTATE, IDM_STEP_REV, MAKELONG(TBSTATE_INDETERMINATE, 0L));
            break;

        case IDM_PLAY_FAST_REV :
            // all graphs just rewind the tape & update the toolbar
            DV_PutVcrMode(ED_MODE_PLAY_FASTEST_REV);
            SendMessage(g_hwndTBar, TB_SETSTATE, IDM_STEP_FWD, MAKELONG(TBSTATE_INDETERMINATE, 0L));
            SendMessage(g_hwndTBar, TB_SETSTATE, IDM_STEP_REV, MAKELONG(TBSTATE_INDETERMINATE, 0L));
            break;            

        case IDM_STEP_FWD :
            // all graphs just forward the tape & update the toolbar
            DV_PutVcrMode(ED_MODE_STEP_FWD);
            g_bUseAtnTimer = FALSE;
            DV_DisplayTimecode();
            break;

        case IDM_STEP_REV :
            // all graphs just rewind the tape & update the toolbar
            DV_PutVcrMode(ED_MODE_STEP_REV);
            g_bUseAtnTimer = FALSE;
            DV_DisplayTimecode();
            break;

        case IDM_SEEKTIMECODE :
            // ATN Seek & display on the toolbar
             DV_SeekATN();
             DV_DisplayTimecode();
             break;

        } // switch (LOWORD(wParam))
    } // switch (iMsg)
}


void Mark_GraphMode_Menu(HWND hwnd, int idmVal)
{
    //uncheck everything first
    CheckMenuItem(g_hmenu, IDM_PREVIEW, MF_UNCHECKED);
    CheckMenuItem(g_hmenu, IDM_FILETODV, MF_UNCHECKED);
    CheckMenuItem(g_hmenu, IDM_DVTOFILE, MF_UNCHECKED);
    CheckMenuItem(g_hmenu, IDM_FILETODV_NOPRE, MF_UNCHECKED);
    CheckMenuItem(g_hmenu, IDM_DVTOFILE_NOPRE, MF_UNCHECKED);

    CheckMenuItem(g_hmenu, IDM_FILETODV_TYPE2, MF_UNCHECKED);
    CheckMenuItem(g_hmenu, IDM_FILETODV_NOPRE_TYPE2, MF_UNCHECKED);
    CheckMenuItem(g_hmenu, IDM_DVTOFILE_TYPE2, MF_UNCHECKED);
    CheckMenuItem(g_hmenu, IDM_DVTOFILE_NOPRE_TYPE2, MF_UNCHECKED);

    // check the selected graph mode
    CheckMenuItem(g_hmenu, idmVal, MF_CHECKED);
}

void Mark_ToolBar_Button(BOOL bEnableRecord, BOOL bEnableOthers)
{
    if(bEnableRecord == TRUE)
    {
        SendMessage(g_hwndTBar, TB_SETSTATE, IDM_RECORD, MAKELONG(TBSTATE_ENABLED, 0L));
    }
    else
        SendMessage(g_hwndTBar, TB_SETSTATE, IDM_RECORD, MAKELONG(TBSTATE_INDETERMINATE, 0L));

    if(bEnableOthers == TRUE)
    {
       //re-enable everything 
        SendMessage(g_hwndTBar, TB_SETSTATE, IDM_STEP_REV, MAKELONG(TBSTATE_ENABLED, 0L));
        SendMessage(g_hwndTBar, TB_SETSTATE, IDM_REW, MAKELONG(TBSTATE_ENABLED, 0L));
        SendMessage(g_hwndTBar, TB_SETSTATE, IDM_PLAY_FAST_REV, MAKELONG(TBSTATE_ENABLED, 0L));
        SendMessage(g_hwndTBar, TB_SETSTATE, IDM_PLAY_FAST_FF, MAKELONG(TBSTATE_ENABLED, 0L));
        SendMessage(g_hwndTBar, TB_SETSTATE, IDM_FF, MAKELONG(TBSTATE_ENABLED, 0L));
        SendMessage(g_hwndTBar, TB_SETSTATE, IDM_STEP_FWD, MAKELONG(TBSTATE_ENABLED, 0L));
        SendMessage(g_hwndTBar, TB_SETSTATE, IDM_SEEKTIMECODE, MAKELONG(TBSTATE_ENABLED, 0L));
    }
    else
    {
        //disable everything except for play, pause, and stop
        SendMessage(g_hwndTBar, TB_SETSTATE, IDM_STEP_REV, MAKELONG(TBSTATE_INDETERMINATE, 0L));
        SendMessage(g_hwndTBar, TB_SETSTATE, IDM_REW, MAKELONG(TBSTATE_INDETERMINATE, 0L));
        SendMessage(g_hwndTBar, TB_SETSTATE, IDM_PLAY_FAST_REV, MAKELONG(TBSTATE_INDETERMINATE, 0L));
        SendMessage(g_hwndTBar, TB_SETSTATE, IDM_PLAY_FAST_FF, MAKELONG(TBSTATE_INDETERMINATE, 0L));
        SendMessage(g_hwndTBar, TB_SETSTATE, IDM_FF, MAKELONG(TBSTATE_INDETERMINATE, 0L));
        SendMessage(g_hwndTBar, TB_SETSTATE, IDM_STEP_FWD, MAKELONG(TBSTATE_INDETERMINATE, 0L));
        SendMessage(g_hwndTBar, TB_SETSTATE, IDM_SEEKTIMECODE, MAKELONG(TBSTATE_INDETERMINATE, 0L));
    }
}

void GetSelectedGraphMode( WPARAM wParam, GRAPH_TYPE* pGraphType)
{
    switch (LOWORD (wParam))
    {  
        case IDM_PREVIEW :
            *pGraphType = GRAPH_PREVIEW;
            break;
        case IDM_FILETODV :
            *pGraphType = GRAPH_FILE_TO_DV;
            break;
        case IDM_FILETODV_NOPRE :
            *pGraphType = GRAPH_FILE_TO_DV_NOPRE;
            break;
        case IDM_DVTOFILE :
            *pGraphType = GRAPH_DV_TO_FILE;
            break;
        case IDM_DVTOFILE_NOPRE :
            *pGraphType = GRAPH_DV_TO_FILE_NOPRE;
            break;
        case IDM_FILETODV_TYPE2 :
            *pGraphType = GRAPH_FILE_TO_DV_TYPE2;
            break;
        case IDM_FILETODV_NOPRE_TYPE2 :
            *pGraphType = GRAPH_FILE_TO_DV_NOPRE_TYPE2;
            break;
        case IDM_DVTOFILE_TYPE2 :
            *pGraphType = GRAPH_DV_TO_FILE_TYPE2;
            break;
        case IDM_DVTOFILE_NOPRE_TYPE2 :
            *pGraphType = GRAPH_DV_TO_FILE_NOPRE_TYPE2;
            break;
    }
}

/*-------------------------------------------------------------------------
Routine:        SetPreviewWindow
Purpose:        Hooks up stream  *from camera* to preview window
                    Note that the preview for the playback from the file is handled within DV_MakeFileToDvGraph() stuff
Arguments:    None
Returns:        HRESULT as appropriate
Notes:          
------------------------------------------------------------------------*/
HRESULT SetPreviewWindow(void)
{
    HRESULT hr = S_OK;

    ASSERT(g_pGraph->m_pVideoWindow!= NULL);
 
    //Set the video window.
    hr = g_pGraph->m_pVideoWindow->put_Owner((OAHWND)g_hwndApp);     // We own the window now
    if(FAILED(hr))
    {
        return hr;
    }

    // you are now a child
    hr = g_pGraph->m_pVideoWindow->put_WindowStyle(WS_CHILD| WS_CLIPSIBLINGS);     
    if(FAILED(hr))
    {
        return hr;
    }

    // give the preview window all our space but where the tool bar and status bar are        
    RECT clientRect, toolbarRect;
    GetClientRect(g_hwndApp, &clientRect);
    GetWindowRect(g_hwndTBar, &toolbarRect);

   // g_pGraph->m_pVideoWindow->SetWindowPosition(0, toolbarRect.bottom - toolbarRect.top, clientRect.right, clientRect.bottom);
    g_pGraph->m_pVideoWindow->SetWindowPosition(0, toolbarRect.bottom - toolbarRect.top, g_iVWWidth, g_iVWHeight);

    hr = g_pGraph->m_pVideoWindow->put_Visible(OATRUE);
    if(FAILED(hr))
    {
        return hr;
    }

    return hr;
} 

/*-------------------------------------------------------------------------
Routine:        DV_DroppedFrameProc
Purpose:        Callback proc to display dropped frame info
Arguments:    DWORD current time
Returns:        None
Notes:          For both Capture & Transmit graphs
------------------------------------------------------------------------*/
void CALLBACK DV_DroppedFrameProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
    HRESULT hr = S_OK;
    long dropped = 0, notdropped = 0;
    TCHAR buffer[128];
    DWORD time = dwTime - g_CapStartTime;

    BOOL bIsModeTransmit = TRUE;      
    hr = g_pGraph->getDroppedFrameNum( &bIsModeTransmit, &dropped, &notdropped);
    if(FAILED(hr))
    {
        DV_StatusText(TEXT("Cannot report dropped frame information"), 1);
        KillTimer(hwnd, DV_TIMER_FRAMES);
    }

    if( bIsModeTransmit == FALSE)
        wsprintf(buffer, TEXT("Captured %d frames (%d dropped) %d.%d sec."), notdropped, dropped, time / 1000, time / 100 - time / 1000 * 10);
    else
        wsprintf(buffer, TEXT("Transmitted %d frames (%d dropped) %d.%d sec."), notdropped, dropped, time / 1000, time / 100 - time / 1000 * 10);

    DV_StatusText(buffer, 1);
} 

/*-------------------------------------------------------------------------
Routine:        DV_TimecodeTimerProc
Purpose:        Callback function for the timer proc
Arguments:    Usual Timer Processing Parameters
Returns:        None
Notes:          
------------------------------------------------------------------------*/
void CALLBACK DV_TimecodeTimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
    DV_DisplayTimecode();
} 

/*-------------------------------------------------------------------------
Routine:        DV_DisplayTimecode
Purpose:        Routine to display timecodes
Arguments:    None
Returns:        None
Notes:          This is TimeCode Read not Absolute Track Number Read
------------------------------------------------------------------------*/
void DV_DisplayTimecode(void)
{
    TIMECODE_SAMPLE TimecodeSample;
    TimecodeSample.timecode.dwFrames = 0;
    static DWORD i1 = 0, i2 = 0, i3 = 0;

    TimecodeSample.dwFlags = ED_DEVCAP_TIMECODE_READ;

    // Query the TimeCode sample data
    HRESULT hr = g_pGraph->m_pIAMTCReader->GetTimecode(&TimecodeSample);
    if(FAILED(hr))
    {
        //MBOX(TEXT("DV_DisplayTimecode::g_pGraph->m_pIAMTCReader->GetTimecode() failed"));
    }
    TCHAR szBuf[4];
  
    // It's worth it to spend a few extra cycles, and to store the static variables
    // to avoid calling wsprintf and setwindowtext on every timer tick.
    if (i1 != (TimecodeSample.timecode.dwFrames & 0xff000000) >> 24)
    {
        wsprintf(szBuf, TEXT("%.2x"),((TimecodeSample.timecode.dwFrames & 0xff000000) >> 24));
        SetDlgItemText(g_hwndTBar, IDC_EDIT_HOUR, szBuf);
    } 
    i1 = (TimecodeSample.timecode.dwFrames & 0xff000000) >> 24;

    if (i2 != (TimecodeSample.timecode.dwFrames & 0x00ff0000) >> 16)
    {
        wsprintf(szBuf, TEXT("%.2x"),((TimecodeSample.timecode.dwFrames & 0x00ff0000) >> 16));
        SetDlgItemText(g_hwndTBar, IDC_EDIT_MINUTE, szBuf);
    } 
    i2 = (TimecodeSample.timecode.dwFrames & 0x00ff0000) >> 16;
    
    if (i3 != (TimecodeSample.timecode.dwFrames & 0x0000ff00) >>  8)
    {
        wsprintf(szBuf, TEXT("%.2x"),((TimecodeSample.timecode.dwFrames & 0x0000ff00) >>  8));
        SetDlgItemText(g_hwndTBar, IDC_EDIT_SECOND, szBuf);
    } 
    i3 = (TimecodeSample.timecode.dwFrames & 0x0000ff00) >>  8;

    wsprintf(szBuf, TEXT("%.2x"),(TimecodeSample.timecode.dwFrames & 0x000000ff));
    SetDlgItemText(g_hwndTBar, IDC_EDIT_FRAME, szBuf); 
} 

/*-------------------------------------------------------------------------
Routine:      DV_SeekATN
Purpose:      ATN Seek function - uses GetTransportBasicParameters to send RAW AVC command 
Arguments:    None
Returns:      TRUE if successful
Notes:        This is Absolute Track Number Seek not TimeCode Seek but uses the timecode display as input
------------------------------------------------------------------------*/
BOOL DV_SeekATN(void)
{
    BOOL bStatus = FALSE;
    HRESULT hr = S_OK;
    int iHr, iMn, iSc, iFr;
    //get the values from the edit fields
    iHr = GetDlgItemInt(g_hwndTBar, IDC_EDIT_HOUR,   &bStatus, FALSE);
    iMn = GetDlgItemInt(g_hwndTBar, IDC_EDIT_MINUTE, &bStatus, FALSE);
    iSc = GetDlgItemInt(g_hwndTBar, IDC_EDIT_SECOND, &bStatus, FALSE);
    iFr = GetDlgItemInt(g_hwndTBar, IDC_EDIT_FRAME,  &bStatus, FALSE);

    hr = g_pGraph->SeekATN( iHr, iMn, iSc, iFr );
    if(FAILED(hr))
    {
        MBOX(TEXT("Invalid Parameter - Time entered should be:\nHour:Minute:Second:Frame"));
        bStatus = FALSE;
    }

    return bStatus;
} 

/*-------------------------------------------------------------------------
Routine:        DV_UpdateTapeInfo
Purpose:        Get Frame rate and availability of dvcr tape
Arguments:    None
Returns:        HRESULT as appropriate
Notes:          
------------------------------------------------------------------------*/
BOOL DV_UpdateTapeInfo(void)
{
   //check information about the tape
    if (S_OK != g_pGraph->GetTapeInfo())
    {
        MBOX(TEXT("Tape is not inserted, or has an improper format.\nReinsert the tape and select Options - Check Tape"));
        DV_StatusText(TEXT("VCR Mode - No tape, or unknown format"), 2);
        return FALSE;
    } 
    else
    {
        switch(g_pGraph->m_VideoFormat)
        {
            case DVENCODERVIDEOFORMAT_NTSC:
                DV_StatusText(TEXT("VCR Mode - NTSC"), 2);
                break;
            case DVENCODERVIDEOFORMAT_PAL:
                DV_StatusText(TEXT("VCR Mode - PAL"), 2);
                break;
            default:
                MBOX(TEXT("Unsupported or unrecognized tape format type"));
                break;
        }
    }

    return TRUE;
}

/*-------------------------------------------------------------------------
Routine:        DV_RefreshMode
Purpose:        Use this to rebuild the necessary stuff to switch between VCR and camera mode
Arguments:    None
Returns:        TRUE if successful
Notes:          
------------------------------------------------------------------------*/
BOOL DV_RefreshMode(void)
{
    BOOL bStatus    = FALSE;

    // Query the current device type
    DV_MODE SubunitMode;
    g_pGraph->GetDVMode( &SubunitMode );
    switch(SubunitMode)
    {
        case CameraMode :
            // update the Graph Mode menu items & status window
            EnableMenuItem(GetMenu(g_hwndApp), IDM_FILETODV, MF_GRAYED);
            EnableMenuItem(GetMenu(g_hwndApp), IDM_FILETODV_NOPRE, MF_GRAYED);
            EnableMenuItem(GetMenu(g_hwndApp), IDM_FILETODV_TYPE2, MF_GRAYED);
            EnableMenuItem(GetMenu(g_hwndApp), IDM_FILETODV_NOPRE_TYPE2, MF_GRAYED);
            EnableMenuItem(GetMenu(g_hwndApp), IDM_CHECKTAPE, MF_GRAYED);
            DV_StatusText(TEXT("Camera Mode"), 2);
            bStatus = TRUE;
            break;

        case VcrMode :
            // Query the tape info & update the status bar
            DV_UpdateTapeInfo();  
            // update the Graph Mode menu items & status window
            EnableMenuItem(GetMenu(g_hwndApp), IDM_CHECKTAPE, MF_ENABLED);
            EnableMenuItem(GetMenu(g_hwndApp), IDM_FILETODV, MF_ENABLED);
            EnableMenuItem(GetMenu(g_hwndApp), IDM_FILETODV_NOPRE, MF_ENABLED);
            EnableMenuItem(GetMenu(g_hwndApp), IDM_FILETODV_TYPE2, MF_ENABLED);
            EnableMenuItem(GetMenu(g_hwndApp), IDM_FILETODV_NOPRE_TYPE2, MF_ENABLED);
            EnableMenuItem(GetMenu(g_hwndApp), IDM_CHECKTAPE, MF_ENABLED);
            bStatus = TRUE;
            break;

        case UnknownMode :
                MBOX(TEXT("Cannot determine camera / Vcr mode"));
                DV_StatusText(TEXT("Unknown Mode"), 2);            
                break;
            
        default :
               MBOX(TEXT("Bad return value from DV_RefreshMode"));
               break;
    }

    return bStatus;
} 

/*-------------------------------------------------------------------------
Routine:        DV_CapSizeDlgProc
Purpose:        Dialog proc for cap size dialog
Arguments:    Usual Dialog Processing parameters
Returns:        BOOL
Notes:          
------------------------------------------------------------------------*/
BOOL CALLBACK DV_CapSizeDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch ( msg )
    {
        case WM_INITDIALOG:
        {
            TCHAR           capDisk[8];
            ULARGE_INTEGER  ulFreeBytes;
            ULARGE_INTEGER  ulTotalBytes;
            ULARGE_INTEGER  ulAvailBytes;

            // make sure output file name has been input for capture
            if (!g_OutputFileName[0])
            {
                MBOX(TEXT("Please set up an output file name for recording"));
                EndDialog(hwnd, FALSE);
                return FALSE;
            } 

            //need to determine disk space and init the dialog appropriately
            lstrcpyn(capDisk, g_OutputFileName, 3);
            capDisk[4] = '\0';
            GetDiskFreeSpaceEx(capDisk, &ulFreeBytes, &ulTotalBytes, &ulAvailBytes);
            //Dump1(TEXT("Available MB on drive %s is %d MB"), capDisk, ulAvailBytes.QuadPart / (1024 * 1024));

            //let's see what our top limits are, and set our limits appropriately
            if ((ulAvailBytes.QuadPart / DV_BYTES_IN_MEG) < 120)
            {
                //less than 120 MB available - subtract 10 MB at a time until we get a usable amount
                UINT i = 110;
                while ((ulAvailBytes.QuadPart / DV_BYTES_IN_MEG) < i)
                {
                    i -= 10;
                } 
                SendMessage(GetDlgItem(hwnd, IDC_SPIN_TIME), UDM_SETRANGE, 0, MAKELONG(i / 4, 1));
                SendMessage(GetDlgItem(hwnd, IDC_SPIN_SIZE), UDM_SETRANGE, 0, MAKELONG(i, 1));
            }
            else
            {
                SendMessage(GetDlgItem(hwnd, IDC_SPIN_TIME), UDM_SETRANGE, 0, MAKELONG( ((ulAvailBytes.QuadPart / (1024 * 1024) ) - 10) / 4, 1));
                SendMessage(GetDlgItem(hwnd, IDC_SPIN_SIZE), UDM_SETRANGE, 0, MAKELONG( (ulAvailBytes.QuadPart / (1024 * 1024) ) - 10, 1));
            }

            //enable / disable the controls as appropriate
            switch (g_dwCaptureLimit)
            {
                case DV_CAPLIMIT_NONE :
                    Button_SetCheck(GetDlgItem(hwnd, IDC_RADIO_NOLIMIT), TRUE);
                    EnableWindow(GetDlgItem(hwnd, IDC_EDIT_SIZE), FALSE);
                    EnableWindow(GetDlgItem(hwnd, IDC_SPIN_SIZE), FALSE);
                    EnableWindow(GetDlgItem(hwnd, IDC_EDIT_TIME), FALSE);
                    EnableWindow(GetDlgItem(hwnd, IDC_SPIN_TIME), FALSE);
                    break;
                
                case DV_CAPLIMIT_TIME :
                {
                    /*check the radio button, disable the size based controls */
                    Button_SetCheck(GetDlgItem(hwnd, IDC_RADIO_TIME), TRUE);
                    EnableWindow(GetDlgItem(hwnd, IDC_EDIT_SIZE), FALSE);
                    EnableWindow(GetDlgItem(hwnd, IDC_SPIN_SIZE), FALSE);

                    break;
                }
                case DV_CAPLIMIT_SIZE :
                {
                    /*check the radio button, disable the time based controls */
                    Button_SetCheck(GetDlgItem(hwnd, IDC_RADIO_SIZE), TRUE);
                    EnableWindow(GetDlgItem(hwnd, IDC_EDIT_TIME), FALSE);
                    EnableWindow(GetDlgItem(hwnd, IDC_SPIN_TIME), FALSE);
                    
                    break;                    
                }
            }
            SetDlgItemInt(hwnd, IDC_EDIT_TIME, g_dwTimeLimit, FALSE);
            SetDlgItemInt(hwnd, IDC_EDIT_SIZE, g_dwDiskSpace, FALSE);
            break;
        } 
        case WM_COMMAND :
            switch LOWORD(wParam)
            {
                // Update the controls ui according to the choices made
                case IDC_RADIO_NOLIMIT :
                {
                    Button_SetCheck(GetDlgItem(hwnd, IDC_RADIO_NOLIMIT), TRUE);
                    EnableWindow(GetDlgItem(hwnd, IDC_EDIT_SIZE), FALSE);
                    EnableWindow(GetDlgItem(hwnd, IDC_SPIN_SIZE), FALSE);
                    EnableWindow(GetDlgItem(hwnd, IDC_EDIT_TIME), FALSE);
                    EnableWindow(GetDlgItem(hwnd, IDC_SPIN_TIME), FALSE);
                    break;
                }
                    
                case IDC_RADIO_TIME :
                {
                    Button_SetCheck(GetDlgItem(hwnd, IDC_RADIO_TIME), TRUE);
                    EnableWindow(GetDlgItem(hwnd, IDC_EDIT_SIZE), FALSE);
                    EnableWindow(GetDlgItem(hwnd, IDC_SPIN_SIZE), FALSE);
                    EnableWindow(GetDlgItem(hwnd, IDC_EDIT_TIME), TRUE);
                    EnableWindow(GetDlgItem(hwnd, IDC_SPIN_TIME), TRUE);
                    break;
                } 
                
                case IDC_RADIO_SIZE :
                {
                    Button_SetCheck(GetDlgItem(hwnd, IDC_RADIO_SIZE), TRUE);
                    EnableWindow(GetDlgItem(hwnd, IDC_EDIT_TIME), FALSE);
                    EnableWindow(GetDlgItem(hwnd, IDC_SPIN_TIME), FALSE);
                    EnableWindow(GetDlgItem(hwnd, IDC_EDIT_SIZE), TRUE);
                    EnableWindow(GetDlgItem(hwnd, IDC_SPIN_SIZE), TRUE);
                    break;
                } 

                case IDOK :
                {
                    BOOL bTranslated = FALSE;
                    // The selections are made
                    // update the new global capture flag
                    if (Button_GetCheck(GetDlgItem(hwnd, IDC_RADIO_NOLIMIT)))
                    {
                        g_dwCaptureLimit = DV_CAPLIMIT_NONE;
                        //DV_LogOut(LOG_PRIORITY_INFO, LOG_LEVEL_MEDIUM, TEXT("No capture size limit"));
                    } 
                    else if (Button_GetCheck(GetDlgItem(hwnd, IDC_RADIO_TIME)))   
                    {
                        g_dwTimeLimit = GetDlgItemInt(hwnd, IDC_EDIT_TIME, &bTranslated, FALSE);
                        g_dwCaptureLimit = DV_CAPLIMIT_TIME;
                        //DV_LogOut(LOG_PRIORITY_INFO, LOG_LEVEL_MEDIUM, TEXT("Time based limit - %d"), g_dwTimeLimit);
                    } 
                    else if (Button_GetCheck(GetDlgItem(hwnd, IDC_RADIO_SIZE)))   
                    {
                        g_dwDiskSpace = GetDlgItemInt(hwnd, IDC_EDIT_SIZE, &bTranslated, FALSE);
                        g_dwCaptureLimit = DV_CAPLIMIT_SIZE;
                        //DV_LogOut(LOG_PRIORITY_INFO, LOG_LEVEL_MEDIUM, TEXT("Disk based limit - %d MB"), g_dwDiskSpace);
                    } 
                    EndDialog(hwnd, TRUE);
                    return TRUE;
                }
                
                case IDCANCEL :
                    // update nothing much
                    EndDialog(hwnd, FALSE);
                    return FALSE;
                default :
                    return FALSE;
            }            
            return TRUE;
    }
    return FALSE;
}

/*-------------------------------------------------------------------------
Routine:        DV_StopRecProc
Purpose:        Callback to stop recording after a specified time
Arguments:    Usual Timer Processing Parameters
Returns:        None
Notes:          
------------------------------------------------------------------------*/
void CALLBACK DV_StopRecProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
    SendMessage(g_hwndApp, WM_COMMAND, IDM_STOP, 0);
} 

/*-------------------------------------------------------------------------
Routine:        DV_AboutDlgProc
Purpose:        simple standard about dialog box proc 
Arguments:    Usual Dialog Processing parameters
Returns:        BOOL
Notes:          
------------------------------------------------------------------------*/
BOOL CALLBACK DV_AboutDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch ( msg )
    {
    //the only command we process are those that close the dialog   
        case WM_COMMAND:
            EndDialog(hwnd, TRUE);
            return TRUE;
        case WM_INITDIALOG:
            return TRUE;
    }

    return FALSE;
}