// TimesegmentTypeInDlg.cpp : implementation file
//

#include "stdafx.h"
#include "demopaja.h"
#include "demopajadoc.h"
#include "TimesegmentTypeInDlg.h"
#include "UndoC.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

using namespace Composition;
using namespace PajaTypes;
using namespace Edit;

/////////////////////////////////////////////////////////////////////////////
// CTimesegmentTypeInDlg dialog


void
AFXAPI
CTimesegmentTypeInDlg::DDX_MyText( CDataExchange* pDX, int nIDC, int& value )
{
	TCHAR	szBuffer[32];
	HWND	hWndCtrl = pDX->PrepareEditCtrl( nIDC );

	if( pDX->m_bSaveAndValidate ) {
		// to value
		szBuffer[0] = 0;
		::GetWindowText( hWndCtrl, szBuffer, 30 );
		value = _ttoi( szBuffer );
	}
	else {
		// from value
		_tcscpy( szBuffer, _itot( value, szBuffer, 10 ) );
		::SetWindowText( hWndCtrl, szBuffer );
	}
}



CTimesegmentTypeInDlg::CTimesegmentTypeInDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CTimesegmentTypeInDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CTimesegmentTypeInDlg)
	m_i32Key = 0;
	m_i32Origo = 0;
	m_i32KeyTime = 0;
	//}}AFX_DATA_INIT
	m_bUpdating = false;
	m_pCurKey = 0;
	m_bUpdatingOrigo = false;
	m_bUpdatingKey = false;
	m_bUpdatingKeyTime = false;
}


void CTimesegmentTypeInDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CTimesegmentTypeInDlg)
	DDX_Control(pDX, IDOK, m_rOk);
	DDX_Control(pDX, IDCANCEL, m_rCancel);
	DDX_Control(pDX, IDC_STATICSTARTTIME, m_rStaticStartTime);
	DDX_Control(pDX, IDC_STATICENDTIME, m_rStaticEndTime);
	DDX_Control(pDX, IDC_EDITKEY, m_rEditKey);
	DDX_Control(pDX, IDC_STATICSEGMENT, m_rStaticSegment);
	DDX_Control(pDX, IDC_STATICORIGOTIME, m_rStaticOrigoTime);
	DDX_Control(pDX, IDC_STATICKEYTIME, m_rStaticKeyTime);
	DDX_Control(pDX, IDC_STATICKEYCOUNT, m_rStaticKeyCount);
	DDX_Control(pDX, IDC_STATICDURATIONTIME, m_rStaticDurationTime);
	DDX_Control(pDX, IDC_STATICDURATIONFRAMES, m_rStaticDurationFrames);
	DDX_Control(pDX, IDC_EDITTIME, m_rEditTime);
	DDX_Control(pDX, IDC_EDITORIGO, m_rEditOrigo);
	//}}AFX_DATA_MAP
	if( !m_bUpdatingKey )
		DDX_MyText(pDX, IDC_EDITKEY, m_i32Key );
	if( !m_bUpdatingOrigo )
		DDX_MyText(pDX, IDC_EDITORIGO, m_i32Origo);
	if( !m_bUpdatingKeyTime )
		DDX_MyText(pDX, IDC_EDITTIME, m_i32KeyTime);
}


BEGIN_MESSAGE_MAP(CTimesegmentTypeInDlg, CDialog)
	//{{AFX_MSG_MAP(CTimesegmentTypeInDlg)
	ON_EN_CHANGE(IDC_EDITORIGO, OnChangeEditorigo)
	ON_EN_CHANGE(IDC_EDITTIME, OnChangeEdittime)
	ON_WM_PAINT()
	ON_EN_CHANGE(IDC_EDITKEY, OnChangeEditkey)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTimesegmentTypeInDlg message handlers

void CTimesegmentTypeInDlg::OnChangeEditorigo() 
{
	if( m_bUpdating )
		return;

	UpdateData( TRUE );
	m_rStaticOrigoTime.SetWindowText( FormatTime( m_i32Origo * 256 / m_i32EditAccuracy ) );
	m_pTimeSegment->set_segment_start( m_i32Origo * 256 / m_i32EditAccuracy );
	
	m_bUpdatingOrigo = true;
	
	UpdateKey();
	UpdateLabels();

	CDemopajaDoc*	pDoc = GetDoc();
	ASSERT_VALID( pDoc );
	pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );

	m_bUpdatingOrigo = false;
}

void CTimesegmentTypeInDlg::OnChangeEdittime() 
{
	if( m_bUpdating )
		return;

	UpdateData( TRUE );
	m_rStaticKeyTime.SetWindowText( FormatTime( m_i32KeyTime * (256 / m_i32EditAccuracy) ) );

	if( m_pCurKey ) {
		UndoC*	pUndo = m_pCurKey->begin_editing( m_pTimeSegment->get_undo() );
		m_pCurKey->set_time( m_i32KeyTime * (256 / m_i32EditAccuracy) - m_pTimeSegment->get_segment_start() );
		m_pTimeSegment->sort_keys();
		m_pCurKey->end_editing( pUndo );
	}

	// if the order has changed... change the key num too
	m_bUpdating = true;
	m_bUpdatingKeyTime = true;

	int32	i32CurSelKey = 1;
	for( int32 i = 0; i < m_pTimeSegment->get_key_count(); i++ ) {
		if( m_pTimeSegment->get_key( i ) == m_pCurKey ) {
			i32CurSelKey = i + 1;
			break;
		}
	}


	if( i32CurSelKey != m_i32Key ) {
		m_i32Key = i32CurSelKey;
		UpdateData( FALSE );
	}

	m_bUpdatingKeyTime = false;
	m_bUpdating = false;

	UpdateDuration();
	UpdateLabels();
	Redraw();
	CDemopajaDoc*	pDoc = GetDoc();
	ASSERT_VALID( pDoc );
	pDoc->NotifyViews( NOTIFY_REDRAW_GRAPHICS );
}

void CTimesegmentTypeInDlg::OnChangeEditkey() 
{
	if( m_bUpdating )
		return;

	UpdateData( TRUE );

	m_bUpdatingKey = true;

	if( m_i32Key < 1 ) {
		m_i32Key = 1;
		m_bUpdating = true;
		UpdateData( FALSE );
		m_bUpdating = false;
	}
	else if( m_i32Key > m_pTimeSegment->get_key_count() ) {
		m_i32Key = m_pTimeSegment->get_key_count();
		m_bUpdating = true;
		UpdateData( FALSE );
		m_bUpdating = false;
	}

	m_pCurKey = 0;
	if( m_pTimeSegment->get_key_count() )
		m_pCurKey = m_pTimeSegment->get_key( m_i32Key - 1 );

	UpdateKey();
	UpdateLabels();

	Redraw();

	m_bUpdatingKey = false;
}

BOOL CTimesegmentTypeInDlg::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext) 
{
	
	return CDialog::Create(IDD, pParentWnd);
}

BOOL CTimesegmentTypeInDlg::OnInitDialog() 
{
	CDialog::OnInitDialog();

	if( !m_pTimeSegment )
		return FALSE;

	m_bUpdating = true;

	// Create imagelist for images used in timesegment
	if( !m_rImageList.Create( IDB_TIMESEGTYPEIN, 8, 2, RGB( 255, 0, 255 ) ) )
		return FALSE;

	// buttons
	if( !m_rSpinnerOrigo.Create( WS_CHILD | WS_VISIBLE | SPNB_SETBUDDYINT, CRect( 0, 0, 0, 0 ), this, IDC_SPINNER1 ) )
		return FALSE;
	if( !m_rSpinnerKey.Create( WS_CHILD | WS_VISIBLE | SPNB_SETBUDDYINT | SPNB_USELIMITS, CRect( 0, 0, 0, 0 ), this, IDC_SPINNER2 ) )
		return FALSE;
	if( !m_rSpinnerKeyTime.Create( WS_CHILD | WS_VISIBLE | SPNB_SETBUDDYINT, CRect( 0, 0, 0, 0 ), this, IDC_SPINNER3 ) )
		return FALSE;

	m_rSpinnerOrigo.SetScale( 1 );
	m_rSpinnerOrigo.SetBuddy( &m_rEditOrigo, SPNB_ATTACH_RIGHT );

	m_rSpinnerKey.SetScale( 1 );
	m_rSpinnerKey.SetMinMax( 1, m_pTimeSegment->get_key_count() );
	m_rSpinnerKey.SetBuddy( &m_rEditKey, SPNB_ATTACH_RIGHT );

	m_rSpinnerKeyTime.SetScale( 1 );
	m_rSpinnerKeyTime.SetBuddy( &m_rEditTime, SPNB_ATTACH_RIGHT );

	// statics
	CString	sStr;

	sStr.Format( "/ %d", m_pTimeSegment->get_key_count() );
	m_rStaticKeyCount.SetWindowText( sStr );

	// origin
	m_i32Origo = m_pTimeSegment->get_segment_start() / (256 / m_i32EditAccuracy);
	m_rStaticOrigoTime.SetWindowText( FormatTime( m_i32Origo ) );

	m_i32Key = 1;
	if( m_pTimeSegment->get_key_count() )
		m_pCurKey = m_pTimeSegment->get_key( 0 );

	UpdateData( FALSE );

	UpdateDuration();
	UpdateKey();
	UpdateLabels();

	Redraw();

	m_bUpdating = false;

	m_rOk.SetFlat( FALSE );
	m_rCancel.SetFlat( FALSE );

	return TRUE;
}

CString CTimesegmentTypeInDlg::FormatTime( int32 i32Time )
{
	CString	sDuration;
	int32	i32Hours, i32Mins, i32Secs, i32Frames;
	int32	i32TimeScale = m_i32BeatsPerMin * m_i32QNotesPerBeat * 256;

	i32Frames = (i32Time * 60 * 60 / i32TimeScale); // / (256 / m_i32EditAccuracy);
	i32Secs = (i32Time * 60 / i32TimeScale) % 60;
	i32Mins = (i32Time / i32TimeScale) % 60;
	i32Hours = i32Time / 60 / i32TimeScale;
	i32Frames = (i32Time - ((i32Hours * 3600 + i32Mins * 60 + i32Secs) * i32TimeScale) / 60) / (256 / m_i32EditAccuracy);

	sDuration.Format( "%d:%02d:%02d:%02d", i32Hours, i32Mins, i32Secs, i32Frames );

	return sDuration;
}

void CTimesegmentTypeInDlg::UpdateKey()
{
	m_i32KeyTime = m_pTimeSegment->get_key_time( m_i32Key - 1 ) / (256 / m_i32EditAccuracy);
	UpdateData( FALSE );
	m_rStaticKeyTime.SetWindowText( FormatTime( m_i32KeyTime * (256 / m_i32EditAccuracy) ) );
}

void CTimesegmentTypeInDlg::UpdateLabels()
{
	int32	i32StartTime, i32EndTime;

	i32StartTime = i32EndTime = m_pTimeSegment->get_segment_start();

	if( m_pTimeSegment->get_key_count() > 1 ) {
		i32StartTime = m_pTimeSegment->get_key_time( 0 );
		i32EndTime = m_pTimeSegment->get_key_time( m_pTimeSegment->get_key_count() - 1 );
	}

	// start label
	CString	sVal;
	sVal.Format( "%d\n", i32StartTime / (256 / m_i32EditAccuracy) );
	sVal += FormatTime( i32StartTime );
	m_rStaticStartTime.SetWindowText( sVal );

	// end label
	sVal.Format( "%d\n", i32EndTime / (256 / m_i32EditAccuracy) );
	sVal += FormatTime( i32EndTime );
	m_rStaticEndTime.SetWindowText( sVal );
}

void CTimesegmentTypeInDlg::UpdateDuration()
{
	int32	i32Duration = 0;

	if( m_pTimeSegment->get_key_count() > 1 ) {
		int32	i32StartTime = m_pTimeSegment->get_key_time( 0 );
		int32	i32EndTime = m_pTimeSegment->get_key_time( m_pTimeSegment->get_key_count() - 1 );
		i32Duration = i32EndTime - i32StartTime;
	}

	CString	sVal;
	sVal.Format( "%d", i32Duration / (256 / m_i32EditAccuracy) );
	m_rStaticDurationFrames.SetWindowText( sVal );
	m_rStaticDurationTime.SetWindowText( FormatTime( i32Duration ) );
}

void CTimesegmentTypeInDlg::Redraw()
{
	CRect	rRect;
	m_rStaticSegment.GetClientRect( &rRect );
	m_rStaticSegment.ClientToScreen( &rRect );
	ScreenToClient( &rRect );

	rRect.bottom = rRect.top + 16;

	InvalidateRect( &rRect, FALSE );
}

void CTimesegmentTypeInDlg::OnPaint() 
{
	CPaintDC dc(this); // device context for painting

	if( m_pTimeSegment->get_key_count() < 1 )
		return;

	// draw time segment

	COLORREF	rHilight; // = ::GetSysColor( COLOR_HIGHLIGHT );

	if( m_i32Flags & ITEM_LAYER ) {
		rHilight = RGB( 196, 128, 160 );
	}
	else if( m_i32Flags & ITEM_EFFECT ) {
		rHilight = RGB( 128, 160, 196 );
	}


	CRect	rSegRect;
	m_rStaticSegment.GetClientRect( &rSegRect );
	m_rStaticSegment.ClientToScreen( &rSegRect );
	ScreenToClient( &rSegRect );

	int32	i32SegmentOrigo = m_pTimeSegment->get_segment_start();

	uint32		r = GetRValue( rHilight ), g = GetGValue( rHilight ), b = GetBValue( rHilight );
	COLORREF	rDarkHilight = RGB( r * 2 / 3, g * 2 / 3, b * 2 / 3 );
//	COLORREF	rLightHilight = RGB( 255 - (255 - r) / 2, 255 - (255 - g) / 2, 255 - (255 - b) / 2 );

	CPen	rBlackPen( PS_SOLID, 1, ::GetSysColor( COLOR_BTNTEXT ) );
	CPen	rDarkPen( PS_SOLID, 1, rDarkHilight );
	CPen*	pOldPen = dc.SelectObject( &rDarkPen );
	CBrush	rHiBrush( rHilight );
	CBrush*	pOldBrush = dc.SelectObject( &rHiBrush );

	int32	i32StartTime = m_pTimeSegment->get_key_time( 0 );
	int32	i32EndTime = m_pTimeSegment->get_key_time( m_pTimeSegment->get_key_count() - 1 );

	if( i32SegmentOrigo < i32StartTime )
		i32StartTime = i32SegmentOrigo;
	if( i32SegmentOrigo > i32EndTime )
		i32EndTime = i32SegmentOrigo;

	int32	i32Duration = i32EndTime - i32StartTime;

	// tempo rect
	CRect	rRect;

	// draw slot
	rRect = rSegRect;
	rRect.bottom = rRect.top + 16;
	dc.Draw3dRect( rRect, ::GetSysColor( COLOR_3DSHADOW ), ::GetSysColor( COLOR_3DHILIGHT ) );
	rRect.DeflateRect( 1, 1, 1, 1 );
	dc.FillSolidRect( rRect, ::GetSysColor( COLOR_WINDOW ) );

	// adjust rect
	rSegRect.left += 6;
	rSegRect.right -= 5;
	rSegRect.top += 2;
	int32	i32SegWidth = rSegRect.Width();

	dc.SelectObject( &rDarkPen );

	if( i32Duration > 0 ) {

		// draw origo
		CPoint	rOrigoPt( rSegRect.left - 4 + (i32SegmentOrigo - i32StartTime) * i32SegWidth / i32Duration, rSegRect.top );
		m_rImageList.DrawIndirect( &dc, 0, rOrigoPt, CSize( 8, 12 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );

		// draw boxes
		for( uint32 i = 0; i < m_pTimeSegment->get_key_count(); i += 2 ) {

			if( i + 1 >= m_pTimeSegment->get_key_count() )
				break;

			int32	i32Time1 = m_pTimeSegment->get_key_time( i );
			int32	i32Time2 = m_pTimeSegment->get_key_time( i + 1 );

			int32	i32Frame1 = (i32Time1 - i32StartTime) * i32SegWidth / i32Duration;
			int32	i32Frame2 = (i32Time2 - i32StartTime) * i32SegWidth / i32Duration;

			rRect.left = rSegRect.left + i32Frame1;
			rRect.right = rSegRect.left + i32Frame2;
			rRect.top = rSegRect.top + 4;
			rRect.bottom = rSegRect.top + 12;

			dc.SelectObject( &rDarkPen );
			dc.Rectangle( rRect );
//			rRect.DeflateRect( 1, 1, 1, 1 );
//			dc.Draw3dRect( rRect, rLightHilight, rDarkHilight );
		}

		// draw keys
		for( i = 0; i < m_pTimeSegment->get_key_count(); i++ ) {

			int32	i32Frame = (m_pTimeSegment->get_key_time( i ) - i32StartTime) * i32SegWidth / i32Duration;

			CPoint	rPt( rSegRect.left + i32Frame - 4, rSegRect.top );

			int32	i32BaseImg = 1;
			if( i & 1 )
				i32BaseImg = 3;

			if( i == (m_i32Key - 1) ) {
				//selected key
				m_rImageList.DrawIndirect( &dc, i32BaseImg + 1, rPt, CSize( 8, 12 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );
			}
			else {
				// normal
				m_rImageList.DrawIndirect( &dc, i32BaseImg, rPt, CSize( 8, 12 ), CPoint( 0, 0 ), ILD_NORMAL, SRCCOPY, CLR_NONE, CLR_DEFAULT );
			}
		}

	}

	dc.SelectObject( pOldPen );
	dc.SelectObject( pOldBrush );
}
