//------------------------------------------------------------------------------
// File: MultiPlayerDlg.cpp
//
// Desc: DirectShow sample code - MultiVMR9 MultiPlayer sample
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------------------------

#include "stdafx.h"
#include "MultiPlayer.h"
#include "MultiPlayerDlg.h"
#include "VMR9Subgraph.h"

#include <list>
using namespace std;

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

const DWORD g_TimerID = 0x12345;
extern HINSTANCE g_hInstance;


// auxiliary array to simplify management of the dialog's controls
UINT g_nControlIDs[] = 
{
    IDC_COMBO_SOURCES,
    IDC_SLIDER_TIME,
    IDC_SLIDER_ALPHA,
    IDC_SLIDER_YPOS,
    IDC_SLIDER_XSIZE,
    IDC_SLIDER_ZORDER,
    IDC_SLIDER_XPOS,
    IDC_SLIDER_YSIZE,
    IDC_BUTTON_START,
    IDC_STATIC_TOTAL,
    IDC_STATIC_PATH,
    IDC_STATIC_STATE,
    IDC_STATIC_ALPHA,
    IDC_STATIC_XPOS,
    IDC_STATIC_XSIZE,
    IDC_STATIC_ZORDER,
    IDC_STATIC_YPOS,
    IDC_STATIC_YSIZE,
    IDC_SLIDER_SETFPS,
    IDC_STATIC_SETFPS,
};

int g_nControls = sizeof(g_nControlIDs) / sizeof(UINT);

// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
    CAboutDlg();

// Dialog Data
    enum { IDD = IDD_ABOUTBOX };

    protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

// Implementation
protected:
    DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()


/******************************Public*Routine******************************\
* CMultiPlayerSession::Initialize
*
* default initialization of the wizard
\**************************************************************************/
HRESULT CMultiPlayerSession::Initialize()
{
    HRESULT hr = S_OK;

    try
    {
        if( m_pWizard )
            throw VFW_E_WRONG_STATE;

        // create wizard
        hr = CoCreateInstance( CLSID_MultiVMR9Wizard,
                                NULL,
                                CLSCTX_INPROC_SERVER,
                                IID_IMultiVMR9Wizard,
                                (void**)&m_pWizard);
        CHECK_HR( hr, DbgMsg("CMultiPlayerSession::Initialize: Failed to create instance of MultiVMR9Wizard, hr = 0x%08x", hr));

        // create video window
        CHECK_HR(
            hr = CreateVideoWindow_(400, 300, WS_CAPTION|WS_OVERLAPPED|WS_THICKFRAME|WS_POPUP),
            DbgMsg("CMultiPlayerSession::Initialize: failed to create video window"));

        // initialize wizard with default
        CHECK_HR(
            hr = m_pWizard->Initialize(NULL, m_hwndVideo, NULL),
            DbgMsg("CMultiPlayerSession::Initialize: failed to initialize the wizard, hr = 0x%08x", hr));

        // get interfaces
        CHECK_HR(
            m_pWizard->GetRenderEngine( &m_pRenderEngine),
            DbgMsg("CMultiPlayerSession::Initialize: failed to get Render Engine, hr = 0x%08x", hr));

        CHECK_HR(
            m_pRenderEngine->GetMixerControl( &m_pMixerControl),
            DbgMsg("CMultiPlayerSession::Initialize: failed to get Mixer Control, hr = 0x%08x", hr));

        CHECK_HR(
            m_pRenderEngine->GetUILayer( &m_pUILayer),
            DbgMsg("CMultiPlayerSession::Initialize: failed to get UILayer, hr = 0x%08x", hr));
    }
    catch( HRESULT hr1 )
    {
        hr = hr1;
    }

    return hr;
}

/******************************Public*Routine******************************\
* CMultiPlayerSession::Terminate
*
* default termination of the wizard
\**************************************************************************/
HRESULT CMultiPlayerSession::Terminate()
{
    // since CMultiPlayerSession adds no data to the base CMultigraphSession,
    // default cleaning in the ~CMultigraphSession() is all we need
    return S_OK;
}

// CMultiPlayerDlg dialog

/******************************Public*Routine******************************\
* CMultiPlayerDlg
*
* constructor
\**************************************************************************/
CMultiPlayerDlg::CMultiPlayerDlg(CWnd* pParent /*=NULL*/)
    : CDialog(CMultiPlayerDlg::IDD, pParent)
    , m_pSession( NULL )
    , m_strTotal(_T(""))
    , m_strPath(_T(""))
    , m_strSourceState(_T(""))
    , m_strAlpha(_T(""))
    , m_strXPos(_T(""))
    , m_strXSize(_T(""))
    , m_strYPos(_T(""))
    , m_strYSize(_T(""))
    , m_strZOrder(_T(""))
    , m_strFPS(_T(""))
    , m_dwTotalSources(0L)
    , m_dwSourceCounter(0)
    , m_fSetFPS( 30.f)
    , m_fGetFPS( 30.f)
    , m_nTimer( 0)
    , m_bmpAttach( NULL )
    , m_bmpAttachGray( NULL )
    , m_bmpDetach( NULL )
    , m_bmpDetachGray( NULL )
    , m_bmpPlay( NULL )
    , m_bmpPlayGray( NULL )
    , m_bmpPause( NULL )
    , m_bmpPauseGray( NULL )
    , m_bmpScale( NULL )
    , m_bmpScaleGray( NULL )
    , m_bmpColor( NULL )
    , m_bmpColorGray( NULL )
{
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

/******************************Public*Routine******************************\
* DoDataExchange
*
* MFC-generated
\**************************************************************************/
void CMultiPlayerDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    DDX_Text(pDX, IDC_STATIC_TOTAL, m_strTotal);
    DDX_Text(pDX, IDC_STATIC_PATH, m_strPath);
    DDX_Text(pDX, IDC_STATIC_ALPHA, m_strAlpha);
    DDX_Text(pDX, IDC_STATIC_XPOS, m_strXPos);
    DDX_Text(pDX, IDC_STATIC_XSIZE, m_strXSize);
    DDX_Text(pDX, IDC_STATIC_YPOS, m_strYPos);
    DDX_Text(pDX, IDC_STATIC_YSIZE, m_strYSize);
    DDX_Text(pDX, IDC_STATIC_ZORDER, m_strZOrder);
    DDX_Text(pDX, IDC_STATIC_SETFPS, m_strFPS);
    DDX_Text(pDX, IDC_STATIC_START, m_strStartTime);
    DDX_Text(pDX, IDC_STATIC_CURTIME, m_strCurTime);
    DDX_Text(pDX, IDC_STATIC_STOP, m_strStopTime);
    DDX_Control(pDX, IDC_COMBO_SOURCES, m_comboSources);
    DDX_Control(pDX, IDC_SLIDER_TIME, m_sliderTime);
    DDX_Control(pDX, IDC_SLIDER_ALPHA, m_sliderAlpha);
    DDX_Control(pDX, IDC_SLIDER_XPOS, m_sliderXPos);
    DDX_Control(pDX, IDC_SLIDER_XSIZE, m_sliderXSize);
    DDX_Control(pDX, IDC_SLIDER_YPOS, m_sliderYPos);
    DDX_Control(pDX, IDC_SLIDER_YSIZE, m_sliderYSize);
    DDX_Control(pDX, IDC_SLIDER_ZORDER, m_sliderZOrder);
    DDX_Control(pDX, IDC_SLIDER_SETFPS, m_sliderFPS);
}

/******************************Public*Routine******************************\
* MESSAGE_MAP
*
* MFC-generated
\**************************************************************************/
BEGIN_MESSAGE_MAP(CMultiPlayerDlg, CDialog)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_DESTROY()
    ON_WM_TIMER()
    ON_WM_QUERYDRAGICON()
    ON_CBN_SELCHANGE(IDC_COMBO_SOURCES, OnCbnSelchangeComboSources)
    ON_BN_CLICKED(IDC_BUTTON_ATTACH, OnBnClickedButtonAttach)
    ON_BN_CLICKED(IDC_BUTTON_DETACH, OnBnClickedButtonDetach)
    ON_BN_CLICKED(IDC_BUTTON_PLAY, OnBnClickedButtonPlay)
    ON_BN_CLICKED(IDC_BUTTON_PAUSE, OnBnClickedButtonPause)
    ON_BN_CLICKED(IDOK, OnOK)
    ON_BN_CLICKED(IDC_BUTTON_FIT, OnBnClickedButtonFit)
    ON_BN_CLICKED(IDC_BUTTON_COLOR, OnBnClickedButtonColor)
    ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER_ALPHA, OnNMReleasedcaptureSliderAlpha)
    ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER_SETFPS, OnNMReleasedcaptureSliderSetfps)
    ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER_TIME, OnNMReleasedcaptureSliderTime)
    ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER_ZORDER, OnNMReleasedcaptureSliderZorder)
    ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER_XPOS, OnNMReleasedcaptureSliderXpos)
    ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER_YPOS, OnNMReleasedcaptureSliderYpos)
    ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER_XSIZE, OnNMReleasedcaptureSliderXsize)
    ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER_YSIZE, OnNMReleasedcaptureSliderYsize)
END_MESSAGE_MAP()


// CMultiPlayerDlg message handlers
/******************************Public*Routine******************************\
* OnInitDialog
*
* In addition to dialog initialization, set initial ranges and positions
* of sliders and default string values; load buttons' bitmaps, initialize
* timer.
* Once this is done, cocreate instance of the wizard
*
\**************************************************************************/
BOOL CMultiPlayerDlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    // Add "About..." menu item to system menu.

    // IDM_ABOUTBOX must be in the system command range.
    ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
    ASSERT(IDM_ABOUTBOX < 0xF000);

    CMenu* pSysMenu = GetSystemMenu(FALSE);
    if (pSysMenu != NULL)
    {
        CString strAboutMenu;
        strAboutMenu.LoadString(IDS_ABOUTBOX);
        if (!strAboutMenu.IsEmpty())
        {
            pSysMenu->AppendMenu(MF_SEPARATOR);
            pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
        }
    }

    // Set the icon for this dialog.  The framework does this automatically
    //  when the application's main window is not a dialog
    SetIcon(m_hIcon, TRUE);         // Set big icon
    SetIcon(m_hIcon, FALSE);        // Set small icon

    // set sliders
    m_sliderAlpha.SetRange(0,100,TRUE);
    m_sliderTime.SetRange(0,100,TRUE);
    m_sliderXPos.SetRange(-100,100,TRUE);
    m_sliderXSize.SetRange(-100,100,TRUE);
    m_sliderYPos.SetRange(-100,100,TRUE);
    m_sliderYSize.SetRange(-100,100,TRUE);
    m_sliderZOrder.SetRange(0,1,TRUE);
    m_sliderFPS.SetRange(0,100,TRUE);

    m_sliderAlpha.SetTicFreq(5);
    m_sliderTime.SetTicFreq(5);
    m_sliderXPos.SetTicFreq(10);
    m_sliderYPos.SetTicFreq(10);
    m_sliderXSize.SetTicFreq(10);
    m_sliderYSize.SetTicFreq(10);
    m_sliderFPS.SetTicFreq(5);

    HINSTANCE hInst = AfxGetInstanceHandle();
    m_bmpAttach = LoadBitmap( hInst, MAKEINTRESOURCE(IDB_BITMAP_ATTACH));
    m_bmpAttachGray = LoadBitmap( hInst, MAKEINTRESOURCE(IDB_BITMAP_ATTACH_DISABLE));
    m_bmpDetach = LoadBitmap( hInst, MAKEINTRESOURCE(IDB_BITMAP_DETACH));
    m_bmpDetachGray = LoadBitmap( hInst, MAKEINTRESOURCE(IDB_BITMAP_DETACH_DISABLE));
    m_bmpPlay = LoadBitmap( hInst, MAKEINTRESOURCE(IDB_BITMAP_PLAY));
    m_bmpPlayGray = LoadBitmap( hInst, MAKEINTRESOURCE(IDB_BITMAP_PLAY_DISABLE));
    m_bmpPause = LoadBitmap( hInst, MAKEINTRESOURCE(IDB_BITMAP_PAUSE));
    m_bmpPauseGray = LoadBitmap( hInst, MAKEINTRESOURCE(IDB_BITMAP_PAUSE_DISABLE));
    m_bmpScale = LoadBitmap( hInst, MAKEINTRESOURCE(IDB_BITMAP_SCALE));
    m_bmpScaleGray = LoadBitmap( hInst, MAKEINTRESOURCE(IDB_BITMAP_SCALE_DISABLE));
    m_bmpColor = LoadBitmap( hInst, MAKEINTRESOURCE(IDB_BITMAP_COLOR));
    m_bmpColorGray = LoadBitmap( hInst, MAKEINTRESOURCE(IDB_BITMAP_COLOR_DISABLE));


    m_nTimer = SetTimer( g_TimerID, 500, 0);
    SetWindowPos(NULL,1,1,0,0,SWP_NOOWNERZORDER|SWP_NOSIZE);
    UpdateWindow();

    m_pSession = new CMultiPlayerSession;
    if( !m_pSession )
    {
        AfxMessageBox(_T("Memory allocation error"),MB_OK,0);
        return FALSE;
    }

    HRESULT hr = m_pSession->Initialize();
    if( FAILED(hr))
    {
        AfxMessageBox(_T("Failed to create MultiVMR9Wizard object.\r\nMake sure you have registered MultiVMR9.dll"),MB_OK,0);
        return FALSE;
    }

    UpdateMediaButtons_();
    UpdateState_();
    UpdateSubgraphInfo_();

    UINT uintIDs[] = 
    {
        IDC_BUTTON_ATTACH,
        IDC_BUTTON_DETACH,
        IDC_BUTTON_PLAY,
        IDC_BUTTON_PAUSE,
        IDC_BUTTON_FIT,
        IDC_BUTTON_COLOR
    };

    UINT uintTips[] = 
    {
        IDS_TT_ADD,
        IDS_TT_DETACH,
        IDS_TT_PLAY,
        IDS_TT_PAUSE,
        IDS_TT_FIT,
        IDS_TT_BKCOLOR
    };

    // initialize tool tips
    for( int i=0; i< g_nButtons; i++ )
    {
        m_hwndToolTips[i] = ::CreateWindow(TOOLTIPS_CLASS, NULL,
                                         WS_POPUP | TTS_NOPREFIX,
                                         0, 0, 0, 0, NULL, NULL, g_hInstance, NULL );
        if( m_hwndToolTips[i] )
        {
            ::SetWindowPos( m_hwndToolTips[i], HWND_TOPMOST, 0,0,0,0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE );
            HWND hwndBtn = ::GetDlgItem( GetSafeHwnd(), uintIDs[i]);
            if( !IsWindow( hwndBtn ) )
            {
                continue;
            }

            m_ti[i].cbSize = sizeof( TOOLINFO );
            m_ti[i].uFlags = TTF_TRANSPARENT | TTF_CENTERTIP | TTF_SUBCLASS | TTF_IDISHWND;
            m_ti[i].hwnd = hwndBtn;
            m_ti[i].uId =  (UINT_PTR)hwndBtn;
            m_ti[i].hinst = g_hInstance;
            m_ti[i].lpszText =MAKEINTRESOURCE(uintTips[i]);
            ::GetClientRect( m_hwndToolTips[i], &(m_ti[i].rect));
            LRESULT lRes = ::SendMessage( m_hwndToolTips[i], TTM_ADDTOOL, 0, (LPARAM)&(m_ti[i]) );
        } // if
    }// for

    //m_ToolTips.Activate( TRUE );
    
    //SetWindowPos(&wndTopMost,0,0,0,0,SWP_NOSIZE);

    return TRUE;  // return TRUE  unless you set the focus to a control
}

/******************************Public*Routine******************************\
* OnOK
*
* Response to "close" button
*
* First off, clean MultiVMR9-specific stuff,
* terminate and destroy Wizard
\**************************************************************************/
void CMultiPlayerDlg::OnOK()
{
    Clean_();
    HRESULT hr = S_OK;
    CString strMsg;

    if( m_pSession )
    {
        hr = m_pSession->Terminate();
        delete m_pSession;
        m_pSession = NULL;
    }

    CDialog::OnOK();
}


/******************************Public*Routine******************************\
* OnSysCommand
*
* MFC-generated
\**************************************************************************/
void CMultiPlayerDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
    if ((nID & 0xFFF0) == IDM_ABOUTBOX)
    {
        CAboutDlg dlgAbout;
        dlgAbout.DoModal();
    }
    else
    {
        CDialog::OnSysCommand(nID, lParam);
    }
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

/******************************Public*Routine******************************\
* OnPaint
*
* MFC-generated
\**************************************************************************/
void CMultiPlayerDlg::OnPaint() 
{
    if (IsIconic())
    {
        CPaintDC dc(this); // device context for painting

        SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

        // Center icon in client rectangle
        int cxIcon = GetSystemMetrics(SM_CXICON);
        int cyIcon = GetSystemMetrics(SM_CYICON);
        CRect rect;
        GetClientRect(&rect);
        int x = (rect.Width() - cxIcon + 1) / 2;
        int y = (rect.Height() - cyIcon + 1) / 2;

        // Draw the icon
        dc.DrawIcon(x, y, m_hIcon);
    }
    else
    {
        CDialog::OnPaint();
    }
}

/******************************Public*Routine******************************\
* OnQueryDragIcon
*
* MFC-generated
\**************************************************************************/
// The system calls this function to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CMultiPlayerDlg::OnQueryDragIcon()
{
    return static_cast<HCURSOR>(m_hIcon);
}

/******************************Public*Routine******************************\
* OnDestroy
*
* clean MultiVMR9-specific stuff, delete bitmaps, kill the timer
\**************************************************************************/
void CMultiPlayerDlg::OnDestroy()
{
    CDialog::OnDestroy();

    KillTimer( m_nTimer );
    Clean_();

    DeleteObject( m_bmpAttach);
    DeleteObject( m_bmpAttachGray);
    DeleteObject( m_bmpDetach);
    DeleteObject( m_bmpDetachGray);
    DeleteObject( m_bmpPlay);
    DeleteObject( m_bmpPlayGray);
    DeleteObject( m_bmpPause);
    DeleteObject( m_bmpPauseGray);
    DeleteObject( m_bmpScale);
    DeleteObject( m_bmpScaleGray);
    DeleteObject( m_bmpColor);
    DeleteObject( m_bmpColorGray);

    for( int i=0; i<g_nButtons; i++)
    {
        ::SendMessage( m_hwndToolTips[i], TTM_DELTOOL, 0, (LPARAM)&(m_ti[i]) );
        ::DestroyWindow( m_hwndToolTips[i] );
    }

}


/******************************Public*Routine******************************\
* OnTimer
*
* call-back function to update dialog controls reflecting states and 
* properties of video sources
\**************************************************************************/
void CMultiPlayerDlg::OnTimer(UINT nIDEvent)
{
    DWORD_PTR dwID = NULL;
    CVMR9Subgraph *pSrc = NULL;
    int FramesPerSecBy100;
    int nItem = -1;
    DWORD dwZ = 0L;

    HRESULT hr = S_OK;

    if( !m_pSession )
    {
        return;
    }

    FramesPerSecBy100 = m_pSession->GetFrameRate();
    m_fGetFPS = (float)FramesPerSecBy100/100.f;

    nItem = m_comboSources.GetCurSel();
    if( nItem == -1 )
    {
        return;
    }

    dwID = m_comboSources.GetItemData( nItem );
    pSrc = m_pSession->GetSubgraph( dwID );
    // for actively selected source
    if( !pSrc )
    {
        return;
    }

    UpdateSubgraphInfo_();

    // also, for all sources, check if a movie is close to the end and 
    // restart to provide looping play mode
    m_pSession->LoopSources();

    CDialog::OnTimer(nIDEvent);
}

/******************************Public*Routine******************************\
* GetHHMMSS
*
* converts LONGLONG value of the reftime (timestamp from IMediaSeeking) to
* the CString of format "HH:MM:SS" (hours, minutes, seconds)
\**************************************************************************/
CString CMultiPlayerDlg::GetHHMMSS( LONGLONG llT )
{
    int hh;
    int mm;
    int ss;
    LONG SecTotal;
    CString strRet;

    SecTotal = (LONG)(llT / 10000000L);
    hh = (int)(SecTotal / 3600);
    mm = (int)((SecTotal - hh * 3600) / 60);
    ss = (int)(SecTotal - hh * 3600 - mm * 60);

    strRet.Format(_T("%02d:%02d:%02d"), hh, mm, ss);
    return strRet;
}

/******************************Public*Routine******************************\
* OnBnClickedButtonAttach
*
* response to "Attach" button
\**************************************************************************/
void CMultiPlayerDlg::OnBnClickedButtonAttach()
{
    TCHAR achPath[MAX_PATH];
    WCHAR wcPath[MAX_PATH];
    DWORD_PTR dwID = NULL;
    HRESULT hr = S_OK;
    int nItem;
    HWND hwnd = NULL;

    if( !m_pSession )
    {
        return;
    }

    if( m_pSession->GetSize() > 16 ) 
    {
        AfxMessageBox(_T("This sample allows to render no more than 16 different streams at once"), MB_OK, NULL);
        return;
    }

    CFileDialog file(TRUE, NULL, NULL,OFN_FILEMUSTEXIST,
        _T("Media Files (*.asf;*.avi;*.mpg;*.mpeg;*.wmv)|*.asf;*.avi;*.mpg;*.mpeg;*.wmv|All Files (*.*)|*.*\0\0"),
        NULL);

    if( IDOK == file.DoModal())
    {
        lstrcpy( achPath, LPCTSTR(file.GetPathName()));
#ifdef UNICODE
        wcscpy( wcPath, achPath );
#else
        MultiByteToWideChar(CP_ACP, 0, achPath,-1, wcPath, MAX_PATH);
#endif
        hr = m_pSession->AddSource( wcPath, dwID);
        if( FAILED(hr))
        {
            CString strMessage;
            strMessage.Format(_T("Failed to attach media source to MultiVMR9. Error code 0x%08x"),hr);
            AfxMessageBox( strMessage, MB_OK, 0);
            return;
        }
        nItem = m_comboSources.AddString( achPath );
        m_comboSources.SetItemData( nItem, dwID);
        m_comboSources.SetCurSel( nItem );

        hwnd = m_pSession->GetWindow();
        ::ShowWindow( hwnd, SW_SHOW);

        UpdateState_();
        UpdateSubgraphInfo_();
        
        UpdateMediaButtons_();
        OnBnClickedButtonPlay();
    }
}

/******************************Public*Routine******************************\
* OnBnClickedButtonDetach
*
* response to "Detach" button
\**************************************************************************/
void CMultiPlayerDlg::OnBnClickedButtonDetach()
{
    HRESULT hr = S_OK;
    int nItem = -1;
    DWORD_PTR dwID = NULL;
    HWND hwnd = NULL;

    if( !m_pSession )
        return;

    nItem = m_comboSources.GetCurSel();
    if( -1 == nItem )
        return;

    dwID = m_comboSources.GetItemData( nItem );

    hr = m_pSession->DeleteSource( dwID );
    if( FAILED( hr))
    {
        CString strMsg;
        strMsg.Format(TEXT("Failed to detach the graph from the wizard: hr = 0x%08x"), hr);
        MessageBox( LPCTSTR(strMsg));
        return;
    }

    m_comboSources.DeleteString(nItem);
    if( m_comboSources.GetCount() == 0)
    {
        m_comboSources.SetWindowText(TEXT(""));
        hwnd = m_pSession->GetWindow();
        ::ShowWindow( hwnd, SW_HIDE);
    }
    else
    {
        m_comboSources.SetCurSel(0);
    }

    UpdateData(FALSE);
    UpdateSubgraphInfo_();
    UpdateMediaButtons_();
}

/******************************Public*Routine******************************\
* OnBnClickedButtonPlay
*
* response to "Play" button
\**************************************************************************/
void CMultiPlayerDlg::OnBnClickedButtonPlay()
{
    HRESULT hr = S_OK;
    DWORD_PTR dwID = NULL;
    CVMR9Subgraph *pSrc = NULL;
    int nItem = -1;
    void* pData = NULL;

    if( !m_pSession )
    {
        return;
    }

    nItem = m_comboSources.GetCurSel();
    if( -1 == nItem )
    {
        return;
    }

    dwID = m_comboSources.GetItemData( nItem );
    pSrc = m_pSession->GetSubgraph( dwID);

    if( !pSrc ) 
        return;

    pSrc->Run();
    UpdateMediaButtons_();
}

/******************************Public*Routine******************************\
* OnBnClickedButtonPause
*
* response to "Pause" button
\**************************************************************************/
void CMultiPlayerDlg::OnBnClickedButtonPause()
{
    HRESULT hr = S_OK;
    DWORD_PTR dwID = NULL;
    CVMR9Subgraph *pSrc = NULL;
    int nItem = -1;
    void* pData = NULL;

    if( !m_pSession )
    {
        return;
    }

    nItem = m_comboSources.GetCurSel();
    if( -1 == nItem )
    {
        return;
    }

    dwID = m_comboSources.GetItemData( nItem );
    pSrc = m_pSession->GetSubgraph( dwID);

    if( !pSrc ) 
        return;

    pSrc->Pause();
    UpdateMediaButtons_();
}

/******************************Public*Routine******************************\
* OnBnClickedButtonColor
*
* response to "Set background color" button
\**************************************************************************/
void CMultiPlayerDlg::OnBnClickedButtonColor()
{
    HRESULT hr = S_OK;
    COLORREF color;
    CColorDialog ClrDlg(0,CC_ANYCOLOR,this);

    if( !m_pSession )
        return;

    if( IDOK == ClrDlg.DoModal())
    {
        color = ClrDlg.GetColor();
        m_pSession->SetColor( color );
    }
}

/******************************Public*Routine******************************\
* OnBnClickedButtonFit
*
* response to "Fit source to the window" button
\**************************************************************************/
void CMultiPlayerDlg::OnBnClickedButtonFit()
{
    HRESULT hr = S_OK;
    NORMALIZEDRECT nr;
    DWORD_PTR dwID = NULL;
    int nItem = -1;
    RECT rc;
    HWND hwnd = NULL;
    CComPtr<IMultiVMR9MixerControl> pMixer;

    if( !m_pSession )
        return;

    ::GetClientRect( hwnd, &rc);

    nItem = m_comboSources.GetCurSel();
    if( -1 == nItem )
    {
        return;
    }
    dwID = m_comboSources.GetItemData( nItem );

    pMixer = m_pSession->GetMixerControl();

    hwnd = m_pSession->GetWindow();
    if( !IsWindow( hwnd ))
        return;

    ::GetClientRect( hwnd, &rc);

    hr = pMixer->GetIdealOutputRect( dwID, rc.right, rc.bottom, &nr);
    hr = pMixer->SetOutputRect( dwID, &nr);
}

/******************************Public*Routine******************************\
* OnCbnSelchangeComboSources
*
* response to selection change in the combobox
\**************************************************************************/
void CMultiPlayerDlg::OnCbnSelchangeComboSources()
{
    UpdateMediaButtons_();
    UpdateSubgraphInfo_();
}

/***************************** sliders ************************************/

/******************************Public*Routine******************************\
* OnNMReleasedcaptureSliderAlpha
*
* slider for alpha
\**************************************************************************/
void CMultiPlayerDlg::OnNMReleasedcaptureSliderAlpha(NMHDR *pNMHDR, LRESULT *pResult)
{
    int nItem = -1;
    DWORD_PTR dwID = NULL;
    HRESULT hr = S_OK;
    CComPtr<IMultiVMR9MixerControl> pMixer;
    float alpha;

    if( !m_pSession )
    {
        return;
    }

    nItem = m_comboSources.GetCurSel();
    if( -1 == nItem )
    {
        return;
    }
    dwID = m_comboSources.GetItemData(nItem);

    int nPos = m_sliderAlpha.GetPos();
    alpha = (float)nPos/100.f;

    pMixer = m_pSession->GetMixerControl();

    if( !pMixer )
    {
        return;
    }

    hr = pMixer->SetAlpha( dwID, alpha );
    *pResult = 0;
}

/******************************Public*Routine******************************\
* OnNMReleasedcaptureSliderSetfps
*
* slider for desired FPS
\**************************************************************************/
void CMultiPlayerDlg::OnNMReleasedcaptureSliderSetfps(NMHDR *pNMHDR, LRESULT *pResult)
{
    HRESULT hr = S_OK;
    int nPos = m_sliderFPS.GetPos();

    CComPtr<IMultiVMR9RenderEngine> pRenderEngine;

    if( !m_pSession )
        return;

    pRenderEngine = m_pSession->GetRenderEngine();

    if( !pRenderEngine )
        return;

    m_fSetFPS = (float)nPos;
    hr = pRenderEngine->SetFrameRate( nPos * 100);
    *pResult = 0;
}

/******************************Public*Routine******************************\
* OnNMReleasedcaptureSliderTime
*
* slider for timeline
\**************************************************************************/
void CMultiPlayerDlg::OnNMReleasedcaptureSliderTime(NMHDR *pNMHDR, LRESULT *pResult)
{
    HRESULT hr = S_OK;
    DWORD_PTR dwID;
    CVMR9Subgraph *pSrc = NULL;
    LONGLONG  llCur, llDur;
    int nItem = -1;
    int nPos;

    if( !m_pSession )
        return;

    nItem = m_comboSources.GetCurSel();
    if( -1 == nItem )
        return;

    dwID = m_comboSources.GetItemData( nItem );
    pSrc = m_pSession->GetSubgraph( dwID);
    if( !pSrc )
        return;

    hr = pSrc->GetTimes( llCur, llDur );
    if( FAILED(hr))
        return;

    nPos = m_sliderTime.GetPos();
    llCur = nPos * llDur / 100L;
    pSrc->SetTime( llCur );

    *pResult = 0;
}

/******************************Public*Routine******************************\
* OnNMReleasedcaptureSliderZorder
*
* slider for Z-order
\**************************************************************************/
void CMultiPlayerDlg::OnNMReleasedcaptureSliderZorder(NMHDR *pNMHDR, LRESULT *pResult)
{
    HRESULT hr = S_OK;
    DWORD_PTR dwID = NULL;
    CComPtr<IMultiVMR9MixerControl> pMixer;
    int nItem = -1;
    int nPos;

    if( !m_pSession )
        return;

    pMixer = m_pSession->GetMixerControl();

    if( !pMixer )
        return;

    nItem = m_comboSources.GetCurSel();
    if( -1 == nItem )
        return;

    dwID = m_comboSources.GetItemData( nItem);
    nPos = m_sliderZOrder.GetPos();

    hr = pMixer->SetZOrder( dwID, nPos);
    *pResult = 0;
}

/******************************Public*Routine******************************\
* OnNMReleasedcaptureSliderXpos
*
* slider for X-position
\**************************************************************************/
void CMultiPlayerDlg::OnNMReleasedcaptureSliderXpos(NMHDR *pNMHDR, LRESULT *pResult)
{
    UpdateOutputRect_();
    *pResult = 0;
}

/******************************Public*Routine******************************\
* OnNMReleasedcaptureSliderYpos
*
* slider for Y-position
\**************************************************************************/
void CMultiPlayerDlg::OnNMReleasedcaptureSliderYpos(NMHDR *pNMHDR, LRESULT *pResult)
{
    UpdateOutputRect_();
    *pResult = 0;
}

/******************************Public*Routine******************************\
* OnNMReleasedcaptureSliderXsize
*
* slider for X-size
\**************************************************************************/
void CMultiPlayerDlg::OnNMReleasedcaptureSliderXsize(NMHDR *pNMHDR, LRESULT *pResult)
{
    UpdateOutputRect_();
    *pResult = 0;
}

/******************************Public*Routine******************************\
* OnNMReleasedcaptureSliderYsize
*
* slider for Y-size
\**************************************************************************/
void CMultiPlayerDlg::OnNMReleasedcaptureSliderYsize(NMHDR *pNMHDR, LRESULT *pResult)
{
    UpdateOutputRect_();
    *pResult = 0;
}

////////////////////////////// PRIVATE ROUTINE /////////////////////////////

/******************************Private*Routine*****************************\
* Clean_
*
* Cleans MultiVMR9-specific data; stops all the subgraphs and detaches 
* them from the wizard; destroys m_listSources container; cleans combo box
*
\**************************************************************************/
void CMultiPlayerDlg::Clean_()
{
    HRESULT hr = S_OK;
    CString strMsg;

    // detach all players
    if( m_pSession )
    {
        m_pSession->Terminate();
        delete m_pSession;
        m_pSession = NULL;
    }
    m_comboSources.Clear();

    KillTimer( g_TimerID );
    Sleep(100);

}

/******************************Private*Routine*****************************\
* UpdateState_
*
* Updates controls depending on whether we have any source selected or not
* (when list is empty)
\**************************************************************************/
void CMultiPlayerDlg::UpdateState_()
{
    int i = 0;

    if( !m_pSession )
        return;

    int nItems = m_pSession->GetSize();

    if( 0 == nItems ) // we are empty
    {
        // disable anything but 'Add new video', 'total sources', and 'close',
        for( i=0; i<g_nControls; i++)
        {
            if( GetDlgItem( g_nControlIDs[i] ))
            {
                if( IDC_STATIC_TOTAL == g_nControlIDs[i] )
                {
                    GetDlgItem( g_nControlIDs[i])->EnableWindow( TRUE );
                }
                else
                {
                    GetDlgItem( g_nControlIDs[i])->EnableWindow( FALSE );
                }
            }
        }
    }
    else // we are not empty
    {
        // enable everything
        for( i=0; i<g_nControls; i++)
        {
            if( GetDlgItem( g_nControlIDs[i] ))
            {
                GetDlgItem( g_nControlIDs[i])->EnableWindow( TRUE );
            }
        }
    }

    UpdateData( FALSE );
}

/******************************Private*Routine*****************************\
* UpdateSubgraphInfo_
*
* Updates controls to reflect currently selected source
\**************************************************************************/
void CMultiPlayerDlg::UpdateSubgraphInfo_()
{
    int nItem = -1;
    CVMR9Subgraph *pSrc = NULL;
    void *pData = NULL;
    DWORD_PTR dwID;

    if( m_pSession )
    {
        m_strTotal.Format(_T("Total sources: %ld"), m_pSession->GetSize());
        m_strFPS.Format( _T("Desired FPS = %5.2f, Actual FPS = %5.2f"), m_fSetFPS, m_fGetFPS);
    }

    SetSliderPosition_( m_sliderFPS, (int)(m_fGetFPS + 10.f));

    nItem = m_comboSources.GetCurSel();
    if( -1 == nItem || !m_pSession)
    {
        // clean and return to defaults
        // update strings
        m_strPath = _T("");
        m_strAlpha.Format(_T("Alpha-level: 1.0"));
        m_strXPos.Format(_T("X-position = 0.0"));
        m_strYPos.Format(_T("Y-position = 0.0"));
        m_strXSize.Format(_T("X-size = 1.0"));
        m_strYSize.Format(_T("Y-size = 1.0"));
        m_strZOrder.Format(_T("Z-order = 0"));

        // update sliders
        SetSliderPosition_( m_sliderAlpha, 100);
        SetSliderPosition_( m_sliderXPos, 0);
        SetSliderPosition_( m_sliderYPos, 0);
        SetSliderPosition_( m_sliderXSize, 100);
        SetSliderPosition_( m_sliderYSize, 100);
        SetSliderPosition_( m_sliderZOrder, 0);
    }
    else
    {
        TCHAR achPath[MAX_PATH];
        float alpha = 1.f;
        DWORD dwZ = 0;
        LONGLONG llCur;
        LONGLONG llDur;
        NORMALIZEDRECT nr;
        NORMALIZEDRECT nrI;
        HWND hwnd;
        RECT rc;

        CComPtr<IMultiVMR9MixerControl> pMixer;

        pMixer = m_pSession->GetMixerControl();
        dwID = m_comboSources.GetItemData( nItem );
        pSrc = m_pSession->GetSubgraph( dwID);

        if( pSrc && pMixer )
        {
            pSrc->GetPathT( achPath );
            m_strPath.Format( _T("%s"), achPath);

            // Update strings
            pMixer->GetAlpha( dwID, &alpha );
            m_strAlpha.Format( _T("Alpha = %5.2f"), alpha );

            pSrc->GetTimes( llCur, llDur );
            m_strStartTime = GetHHMMSS( 0L);
            m_strCurTime = GetHHMMSS( llCur );
            m_strStopTime = GetHHMMSS( llDur );

            pMixer->GetZOrder( dwID, &dwZ);
            m_strZOrder.Format( _T("Z-order = %ld"), dwZ);

            // Update sliders
            SetSliderPosition_( m_sliderAlpha, (int)(alpha * 100.f));
            if( llDur )
            {
                SetSliderPosition_( m_sliderTime, 
                    (int)(llCur * 100 / llDur));
            }
            m_sliderZOrder.SetRange( 0, m_pSession->GetSize()-1, TRUE);
            SetSliderPosition_( m_sliderZOrder, dwZ );

            hwnd = m_pSession->GetWindow();
            ::GetClientRect( hwnd, &rc);

            pMixer->GetOutputRect( dwID, &nr);
            pMixer->GetIdealOutputRect( dwID, rc.right, rc.bottom, &nrI);
            // Update positions
            float cx  = (nr.left +  nr.right) / 2.f;
            float cy  = (nr.top +  nr.bottom) / 2.f;
            float cxi = (nrI.left + nrI.right) / 2.f;
            float cyi = (nrI.top + nrI.bottom) / 2.f;

            float w  = nr.right - nr.left;
            float h  = nr.bottom - nr.top;
            float wi = nrI.right - nrI.left;
            float hi = nrI.bottom - nrI.top;

            float XPos = (cx - cxi) / wi;
            float YPos = (cy - cyi) / hi;

            float XSize = w / wi;
            float YSize = h / hi;

            // set strings
            m_strXPos.Format(_T("X-position = %5.2f"), XPos);
            m_strYPos.Format(_T("Y-position = %5.2f"), YPos);
            m_strXSize.Format(_T("X-size = %5.2f"), XSize);
            m_strYSize.Format(_T("Y-size = %5.2f"), YSize);

            // set sliders
            SetSliderPosition_( m_sliderXPos, (int)(XPos * 100.f));
            SetSliderPosition_( m_sliderYPos, (int)(YPos * 100.f));
            SetSliderPosition_( m_sliderXSize, (int)(XSize * 100.f));
            SetSliderPosition_( m_sliderYSize, (int)(YSize * 100.f));
        }
    }

    UpdateData(FALSE);
    return;
}

/******************************Private*Routine*****************************\
* UpdateMediaButtons_
*
* Updates media buttons depending on the number of attached sources and 
* the state of currently selected source
\**************************************************************************/
void CMultiPlayerDlg::UpdateMediaButtons_()
{
    OAFilterState state;
    CVMR9Subgraph *pSrc = NULL;
    int nItems;
    DWORD_PTR dwID;

    if( !m_pSession )
        return;

    nItems = m_pSession->GetSize();

    if( GetDlgItem(IDC_BUTTON_ATTACH) )
    {
        ::SendMessage(  GetDlgItem(IDC_BUTTON_ATTACH)->GetSafeHwnd(), 
                        BM_SETIMAGE, 
                        (WPARAM)IMAGE_BITMAP, 
                        (LPARAM)( nItems<16 ? m_bmpAttach : m_bmpAttachGray) );
    }
    if( GetDlgItem(IDC_BUTTON_DETACH) )
    {
        ::SendMessage(  GetDlgItem(IDC_BUTTON_DETACH)->GetSafeHwnd(), 
                        BM_SETIMAGE, 
                        (WPARAM)IMAGE_BITMAP, 
                        (LPARAM)(nItems>0 ? m_bmpDetach : m_bmpDetachGray));
    }

    if( GetDlgItem(IDC_BUTTON_COLOR) )
    {
        ::SendMessage(  GetDlgItem(IDC_BUTTON_COLOR)->GetSafeHwnd(), 
                        BM_SETIMAGE, 
                        (WPARAM)IMAGE_BITMAP, 
                        (LPARAM)m_bmpColor);
    }


    if( 0 < m_comboSources.GetCount() && m_pSession)
    {
        int nItem = 0;
        
        nItem = m_comboSources.GetCurSel();
        if( -1 != nItem )
        {
            dwID = m_comboSources.GetItemData( nItem );
            pSrc = m_pSession->GetSubgraph( dwID);
        }
    }

    if( NULL == pSrc )
    {
        ::SendMessage(  GetDlgItem(IDC_BUTTON_PLAY)->GetSafeHwnd(), 
                        BM_SETIMAGE, 
                        (WPARAM)IMAGE_BITMAP, 
                        (LPARAM)(m_bmpPlayGray));
        ::SendMessage(  GetDlgItem(IDC_BUTTON_PAUSE)->GetSafeHwnd(), 
                        BM_SETIMAGE, 
                        (WPARAM)IMAGE_BITMAP, 
                        (LPARAM)(m_bmpPauseGray));

        ::SendMessage(  GetDlgItem(IDC_BUTTON_FIT)->GetSafeHwnd(), 
                        BM_SETIMAGE, 
                        (WPARAM)IMAGE_BITMAP, 
                        (LPARAM)(m_bmpScaleGray));
    }
    else
    {
        ::SendMessage(  GetDlgItem(IDC_BUTTON_FIT)->GetSafeHwnd(), 
                        BM_SETIMAGE, 
                        (WPARAM)IMAGE_BITMAP, 
                        (LPARAM)(m_bmpScale));

        state = pSrc->GetState();
        if( State_Running == state )
        {
            ::SendMessage(  GetDlgItem(IDC_BUTTON_PLAY)->GetSafeHwnd(), 
                            BM_SETIMAGE, 
                            (WPARAM)IMAGE_BITMAP, 
                            (LPARAM)(m_bmpPlayGray));
            ::SendMessage(  GetDlgItem(IDC_BUTTON_PAUSE)->GetSafeHwnd(), 
                            BM_SETIMAGE, 
                            (WPARAM)IMAGE_BITMAP, 
                            (LPARAM)(m_bmpPause));

        }
        else
        {
            ::SendMessage(  GetDlgItem(IDC_BUTTON_PLAY)->GetSafeHwnd(), 
                            BM_SETIMAGE, 
                            (WPARAM)IMAGE_BITMAP, 
                            (LPARAM)(m_bmpPlay));
            ::SendMessage(  GetDlgItem(IDC_BUTTON_PAUSE)->GetSafeHwnd(), 
                            BM_SETIMAGE, 
                            (WPARAM)IMAGE_BITMAP, 
                            (LPARAM)(m_bmpPauseGray));
        }
    }
}

/******************************Private*Routine*****************************\
* UpdateOutputRect_
*
* Transforms positions of "pos" and "size" sliders to NORMALIZEDRECT
\**************************************************************************/
void CMultiPlayerDlg::UpdateOutputRect_()
{
    int nItem = -1;
    DWORD_PTR dwID = NULL;
    CVMR9Subgraph *pSrc = NULL;
    int nPos = 0;
    CComPtr<IMultiVMR9MixerControl> pMixer;
    NORMALIZEDRECT nr;
    NORMALIZEDRECT nrI;
    HWND hwnd;
    RECT rc;
    HRESULT hr = S_OK;
    
    if( !m_pSession )
        return;

    nItem = m_comboSources.GetCurSel();
    if( -1 == nItem )
        return;

    dwID = m_comboSources.GetItemData( nItem );
    pSrc = m_pSession->GetSubgraph( dwID );
    if( !pSrc )
        return;

    pMixer = m_pSession->GetMixerControl();
    if( !pMixer )
        return;

    hwnd = m_pSession->GetWindow();
    if( !IsWindow( hwnd ))
        return;

    ::GetClientRect( hwnd, &rc);

    pMixer->GetOutputRect( dwID, &nr);
    pMixer->GetIdealOutputRect( dwID, rc.right, rc.bottom, &nrI);


    float wi = nrI.right - nrI.left;
    float hi = nrI.bottom - nrI.top;

    if( 0.f == wi || 0.f == hi )
        return;

    float cxi = (nrI.left + nrI.right) / 2.f;
    float cyi = (nrI.top + nrI.bottom) / 2.f;

    float cx = cxi + wi * (float)(m_sliderXPos.GetPos()) / 100.f;
    float cy = cyi + hi * (float)(m_sliderYPos.GetPos()) / 100.f;

    float w = (float)(m_sliderXSize.GetPos()) / 100.f / wi / 2.f;
    float h = (float)(m_sliderYSize.GetPos()) / 100.f / hi / 2.f;

    nr.left   = cx - w;
    nr.right  = cx + w;
    nr.top    = cy - h;
    nr.bottom = cy + h;

    pMixer->SetOutputRect( dwID, &nr);
}

/******************************Private*Routine*****************************\
* SetSliderPosition_
*
* Sets slider position only if slider is not captured by mouse
\**************************************************************************/
void CMultiPlayerDlg::SetSliderPosition_( CSliderCtrl& slider, int Pos)
{
    CWnd *pwnd = GetCapture();

    if( pwnd ==(CWnd*)(&slider) )
    {
        return;
    }
    slider.SetPos( Pos );
}


