#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <dsound.h>
#include <commctrl.h>

#include "DSShow3D.h"
#include "debug.h"
#include "GVars.h"
#include "LsnrInfo.h"
#include "finfo3d.h"


ListenerInfo::ListenerInfo()
    {
    m_hDistanceEdit = m_hDopplerEdit = m_hRolloffEdit = NULL;
    m_hXSlider = m_hYSlider = m_hZSlider = NULL;
    m_hXText = m_hYText = m_hZText = NULL;

    m_vPos.x = m_vPos.y = m_vPos.z = (D3DVALUE)0.0;
    }


ListenerInfo::~ListenerInfo()
    {
    }


BOOL ListenerInfo::OnInitDialog( HWND hDlg, WPARAM wParam )
    {
    m_hDistanceEdit = GetDlgItem( hDlg, IDC_LISTENER_DISTANCEFACTOR_EDIT );
    m_hDopplerEdit = GetDlgItem( hDlg, IDC_LISTENER_DOPPLERFACTOR_EDIT );
    m_hRolloffEdit = GetDlgItem( hDlg, IDC_LISTENER_ROLLOFFFACTOR_EDIT );

    m_hXSlider = GetDlgItem( hDlg, IDC_LISTENER_X_SLIDER );
    m_hYSlider = GetDlgItem( hDlg, IDC_LISTENER_Y_SLIDER );
    m_hZSlider = GetDlgItem( hDlg, IDC_LISTENER_Z_SLIDER );

    m_hXText = GetDlgItem( hDlg, IDC_LISTENER_X_TEXT );
    m_hYText = GetDlgItem( hDlg, IDC_LISTENER_Y_TEXT );
    m_hZText = GetDlgItem( hDlg, IDC_LISTENER_Z_TEXT );

    // Initialize the distance factor to metres
    Edit_SetText( m_hDistanceEdit, "1.0" );
    Edit_SetText( m_hDopplerEdit, "1.0" );
    Edit_SetText( m_hRolloffEdit, "1.0" );

    gp3DListener->SetPosition( D3DVAL(0.0f), D3DVAL(0.0f), D3DVAL(-10.0f), DS3D_IMMEDIATE );
//    gp3DListener->CommitDeferredSettings();

    SetSliders();

    return TRUE;
    }


void ListenerInfo::UpdateUI( void )
    {
    UpdateXSliderUI( 0, TRUE );
    UpdateYSliderUI( 0, TRUE );
    UpdateZSliderUI( 0, TRUE );
    }


BOOL ListenerInfo::OnCommand( WPARAM wParam, LPARAM lParam )
    {
    TCHAR   szEdit[16];

    if( HIWORD(wParam) == EN_CHANGE )
	{
	if( (HWND)lParam == m_hDistanceEdit )
	    {
	    Edit_GetText( m_hDistanceEdit, szEdit, sizeof(szEdit));

	    ASSERT( NULL != gp3DListener );
	    DPF( 3, "Setting Distance Factor: %s", szEdit );
	    gp3DListener->SetDistanceFactor(D3DVAL(atof(szEdit)), DS3D_IMMEDIATE );
	    }
	else if((HWND)lParam == m_hDopplerEdit )
	    {
	    Edit_GetText( m_hDopplerEdit, szEdit, sizeof(szEdit));

	    ASSERT( NULL != gp3DListener );
	    DPF( 3, "Setting Doppler Factor: %s", szEdit );
	    gp3DListener->SetDopplerFactor(D3DVAL(atof(szEdit)), DS3D_IMMEDIATE );
	    }
	else if((HWND)lParam == m_hRolloffEdit )
	    {
	    Edit_GetText( m_hRolloffEdit, szEdit, sizeof(szEdit));

	    ASSERT( NULL != gp3DListener );
	    DPF( 3, "Setting Rolloff Factor: %s", szEdit );
	    gp3DListener->SetRolloffFactor(D3DVAL(atof(szEdit)), DS3D_IMMEDIATE );
	    }
	}
    return FALSE;
    }


///////////////////////////////////////////////////////////////////////////////
// OnHScroll()
//
//    Main message handler for the WM_HSCROLL message.  this function basically
// figures out which horizontal scrolling control is responsible for sending
// the message and passes on handling to an appropriate function for handling.
//
BOOL ListenerInfo::OnHScroll( WORD wNotification, LONG lPos, HWND hControl )
    {
    if( !hControl )
	return FALSE;
    
    if( hControl == m_hXSlider )
	{
	HandleXSliderScroll( wNotification, lPos );
	return TRUE;
	}
    else if( hControl == m_hYSlider )
	{
	HandleYSliderScroll( wNotification, lPos );
	return TRUE;
	}
    else if( hControl == m_hZSlider )
	{
	HandleZSliderScroll( wNotification, lPos );
	return TRUE;
	}
    else
	return FALSE;
    }


void ListenerInfo::OnDestroy( void )
    {
    }


BOOL CALLBACK ListenerInfoDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
    {
    PLISTENERINFO   pli;

    switch( message )
	{
	case WM_INITDIALOG:
	    ASSERT( lParam != NULL );
	    pli = (PLISTENERINFO)lParam;
	    SetWindowLong( hDlg, DWL_USER, (LONG)pli );

	    if( !pli->OnInitDialog( hDlg, wParam ))
		DestroyWindow( hDlg );
	    return TRUE;

	case WM_HSCROLL:
	    pli = (PLISTENERINFO)GetWindowLong( hDlg, DWL_USER );
	    ASSERT( NULL != pli );
	    return !pli->OnHScroll( LOWORD(wParam), (LONG)HIWORD(wParam), (HWND)lParam );

	case WM_COMMAND:
	    pli = (PLISTENERINFO)GetWindowLong( hDlg, DWL_USER );
	    // It's possible to get child notifications before the
	    // INITDIALOG message, so we'll handle a NULL class item
	    // here a less stringently
	    if( !pli )
		return FALSE;
	    return !pli->OnCommand( wParam, lParam );

	case WM_DESTROY:
	    pli = (PLISTENERINFO)GetWindowLong( hDlg, DWL_USER );
	    ASSERT( NULL != pli );
	    pli->OnDestroy();
	    return TRUE;

	default:
	    return FALSE;
	}
    }


///////////////////////////////////////////////////////////////////////////////
//
//
//
//
void ListenerInfo::SetSliders( void )
    {
    SendMessage( m_hXSlider, TBM_SETRANGEMIN, FALSE,
	(LPARAM)(LISTENER_SLIDER_MIN + LISTENER_SLIDER_SHIFT) / LISTENER_SLIDER_FACTOR );
    SendMessage( m_hXSlider, TBM_SETRANGEMAX, FALSE,
	(LPARAM)(LISTENER_SLIDER_MAX + LISTENER_SLIDER_SHIFT) / LISTENER_SLIDER_FACTOR );
    SendMessage( m_hXSlider, TBM_SETPAGESIZE, 0, LISTENER_SLIDER_FACTOR);

    SendMessage( m_hYSlider, TBM_SETRANGEMIN, FALSE,
	(LPARAM)(LISTENER_SLIDER_MIN + LISTENER_SLIDER_SHIFT) / LISTENER_SLIDER_FACTOR );
    SendMessage( m_hYSlider, TBM_SETRANGEMAX, FALSE,
	(LPARAM)(LISTENER_SLIDER_MAX + LISTENER_SLIDER_SHIFT) / LISTENER_SLIDER_FACTOR );
    SendMessage( m_hYSlider, TBM_SETPAGESIZE, 0, LISTENER_SLIDER_FACTOR);

    SendMessage( m_hZSlider, TBM_SETRANGEMIN, FALSE,
	(LPARAM)(LISTENER_SLIDER_MIN + LISTENER_SLIDER_SHIFT) / LISTENER_SLIDER_FACTOR );
    SendMessage( m_hZSlider, TBM_SETRANGEMAX, FALSE,
	(LPARAM)(LISTENER_SLIDER_MAX + LISTENER_SLIDER_SHIFT) / LISTENER_SLIDER_FACTOR );
    SendMessage( m_hZSlider, TBM_SETPAGESIZE, 0, LISTENER_SLIDER_FACTOR);

    UpdateUI();
    }


///////////////////////////////////////////////////////////////////////////////
//
//
//
//
void ListenerInfo::HandleXSliderScroll( WORD wNot, LONG lPos )
    {
    HRESULT hr;
    BOOL fUpdate = TRUE;

    switch( wNot )
	{
	case TB_THUMBTRACK:
	    break;

	case TB_ENDTRACK:
	case TB_LINEDOWN:
	case TB_LINEUP:
	case TB_PAGEDOWN:
	case TB_PAGEUP:
	    lPos = SendMessage( m_hXSlider, TBM_GETPOS, 0, 0 );
	    break;

	default:
	    fUpdate = FALSE;
	}
    
    if( fUpdate && NULL != gp3DListener )
	{
	m_vPos.x = D3DVAL(lPos * LISTENER_SLIDER_FACTOR) - LISTENER_SLIDER_SHIFT;
	DPF( 3, "Setting listener pos: (%i, %i, %i)", (int)m_vPos.x, (int)m_vPos.y, (int)m_vPos.z );
	if( FAILED( hr = gp3DListener->SetPosition( m_vPos.x, m_vPos.y,
						m_vPos.z, DS3D_IMMEDIATE )))
	    DPF( 0, "IDirectSound3DListener::SetPosition returned : %s", TranslateDSError(hr));
	UpdateXSliderUI((lPos * LISTENER_SLIDER_FACTOR) - LISTENER_SLIDER_SHIFT, FALSE );
	}
    }


///////////////////////////////////////////////////////////////////////////////
//
//
//
//
void ListenerInfo::HandleYSliderScroll( WORD wNot, LONG lPos )
    {
    HRESULT hr;
    BOOL fUpdate = TRUE;

    switch( wNot )
	{
	case TB_THUMBTRACK:
	    break;

	case TB_ENDTRACK:
	case TB_LINEDOWN:
	case TB_LINEUP:
	case TB_PAGEDOWN:
	case TB_PAGEUP:
	    lPos = SendMessage( m_hYSlider, TBM_GETPOS, 0, 0 );
	    break;

	default:
	    fUpdate = FALSE;
	}
    
    if( fUpdate && NULL != gp3DListener )
	{
	m_vPos.y = D3DVAL(lPos * LISTENER_SLIDER_FACTOR) - LISTENER_SLIDER_SHIFT;
	DPF( 3, "Setting listener pos: (%i, %i, %i)", (int)m_vPos.x, (int)m_vPos.y, (int)m_vPos.z );
	if( FAILED( hr = gp3DListener->SetPosition( m_vPos.x, m_vPos.y,
						m_vPos.z, DS3D_IMMEDIATE )))
	    DPF( 0, "IDirectSound3DListener::SetPosition returned : %s", TranslateDSError(hr));
	UpdateYSliderUI((lPos * LISTENER_SLIDER_FACTOR) - LISTENER_SLIDER_SHIFT, FALSE );
	}
    }


///////////////////////////////////////////////////////////////////////////////
//
//
//
//
void ListenerInfo::HandleZSliderScroll( WORD wNot, LONG lPos )
    {
    HRESULT hr;
    BOOL fUpdate = TRUE;

    switch( wNot )
	{
	case TB_THUMBTRACK:
	    break;

	case TB_ENDTRACK:
	case TB_LINEDOWN:
	case TB_LINEUP:
	case TB_PAGEDOWN:
	case TB_PAGEUP:
	    lPos = SendMessage( m_hZSlider, TBM_GETPOS, 0, 0 );
	    break;

	default:
	    fUpdate = FALSE;
	}
    
    if( fUpdate && NULL != gp3DListener )
	{
	m_vPos.z = D3DVAL(lPos * LISTENER_SLIDER_FACTOR) - LISTENER_SLIDER_SHIFT;
	DPF( 3, "Setting listener pos: (%i, %i, %i)", (int)m_vPos.x, (int)m_vPos.y, (int)m_vPos.z );
	if( FAILED( hr = gp3DListener->SetPosition( m_vPos.x, m_vPos.y,
						m_vPos.z, DS3D_IMMEDIATE )))
	    DPF( 0, "IDirectSound3DListener::SetPosition returned : %s", TranslateDSError(hr));
	UpdateZSliderUI((lPos * LISTENER_SLIDER_FACTOR) - LISTENER_SLIDER_SHIFT, FALSE );
	}
    }


///////////////////////////////////////////////////////////////////////////////
// UpdateXSliderUI()
//
//    Update anything that displays the position of the X slider in some manner
// to reflect the new position, either from the buffer or from the parameter.
//
void ListenerInfo::UpdateXSliderUI( LONG lForcePos, BOOL fFromInterface )
    {
    TCHAR   szText[8];
    LONG    lPos;

    if( fFromInterface )
        {
	if( NULL != gp3DListener )
	    {
	    gp3DListener->GetPosition( &m_vPos );
	    lPos = (LONG)m_vPos.x;
	    }
	else
	    lPos = 0;
	}
    else
	lPos = lForcePos;

    SendMessage( m_hXSlider, TBM_SETPOS, (WPARAM)TRUE,
		    (LPARAM)(lPos + LISTENER_SLIDER_SHIFT) / LISTENER_SLIDER_FACTOR );
    wsprintf( szText, "%i", lPos );
    Static_SetText( m_hXText, szText );
    }


///////////////////////////////////////////////////////////////////////////////
// UpdateYSliderUI()
//
//    Update anything that displays the position of the Y slider in some manner
// to reflect the new position, either from the buffer or from the parameter.
//
void ListenerInfo::UpdateYSliderUI( LONG lForcePos, BOOL fFromInterface )
    {
    TCHAR   szText[8];
    LONG    lPos;

    if( fFromInterface )
        {
	if( NULL != gp3DListener )
	    {
	    gp3DListener->GetPosition( &m_vPos );
	    lPos = (LONG)m_vPos.y;
	    }
	else
	    lPos = 0;
	}
    else
	lPos = lForcePos;

    SendMessage( m_hYSlider, TBM_SETPOS, (WPARAM)TRUE,
		    (LPARAM)(lPos + LISTENER_SLIDER_SHIFT) / LISTENER_SLIDER_FACTOR );
    wsprintf( szText, "%i", lPos );
    Static_SetText( m_hYText, szText );
    }


///////////////////////////////////////////////////////////////////////////////
// UpdateZSliderUI()
//
//    Update anything that displays the position of the Z slider in some manner
// to reflect the new position, either from the buffer or from the parameter.
//
void ListenerInfo::UpdateZSliderUI( LONG lForcePos, BOOL fFromInterface )
    {
    TCHAR	szText[8];
    LONG	lPos;

    if( fFromInterface )
        {
	if( NULL != gp3DListener )
	    {
	    gp3DListener->GetPosition( &m_vPos );
	    lPos = (LONG)m_vPos.z;
	    }
	else
	    lPos = 0;
	}
    else
	lPos = lForcePos;

    SendMessage( m_hZSlider, TBM_SETPOS, (WPARAM)TRUE,
		    (LPARAM)(lPos + LISTENER_SLIDER_SHIFT) / LISTENER_SLIDER_FACTOR );
    wsprintf( szText, "%i", lPos );
    Static_SetText( m_hZText, szText );
    }

