// TimeFrame.cpp : implementation file
//

#include "stdafx.h"
#include "GosView.h"
#include "TimeFrame.h"

#include "GosViewDoc.h"
#include "GosEventView.h"
#include "GosEventPane.h"
#include "GosStatPane.h"
#include "GosEventIdPane.h"
#include "GosTracePane.h"

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



/////////////////////////////////////////////////////////////////////////////
// CTimeFrame

IMPLEMENT_DYNCREATE(CTimeFrame, CMDIChildWnd)

CTimeFrame::CTimeFrame()
{
	m_nNumRowsVisible = 0;
	m_nTopVisibleRow = 0;
	m_rRowHeight = 20.0;
	m_nNumBandColors = 4;
	m_pBandColor = new CBrush[4];
	m_fChildrenReady = false;
	m_nNumEnabledBands = 0;
	m_FirstID = 0;
	m_nNumParentNames = 0;
	m_fAlphaSort = false;

	m_pBandColor[0].CreateSolidBrush( RGB( 230,230,230 ) );
	m_pBandColor[1].CreateSolidBrush( RGB( 210,210,210 ) );
	m_pBandColor[2].CreateSolidBrush( RGB( 190,190,190 ) );
	m_pBandColor[3].CreateSolidBrush( RGB( 170,170,170 ) );

	m_pDoc = NULL;

}


CTimeFrame::~CTimeFrame()
{
	delete[] m_pBandColor;
}


BEGIN_MESSAGE_MAP(CTimeFrame, CMDIChildWnd)
	//{{AFX_MSG_MAP(CTimeFrame)
	ON_WM_CREATE()
	ON_WM_SETFOCUS()
	ON_WM_SIZE()
	ON_WM_HSCROLL()
	ON_BN_CLICKED(IDC_BUTTON1, OnButton1)
	ON_COMMAND(ID_ALPHA_SORT, OnAlphaSort)
	ON_UPDATE_COMMAND_UI(ID_ALPHA_SORT, OnUpdateAlphaSort)
	ON_COMMAND(ID_NUM_SORT, OnNumSort)
	ON_UPDATE_COMMAND_UI(ID_NUM_SORT, OnUpdateNumSort)
	ON_COMMAND(ID_FULL_NAMES, OnFullNames)
	ON_UPDATE_COMMAND_UI(ID_FULL_NAMES, OnUpdateFullNames)
	ON_COMMAND(ID_PART_NAMES, OnPartNames)
	ON_UPDATE_COMMAND_UI(ID_PART_NAMES, OnUpdatePartNames)
	ON_WM_VSCROLL()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTimeFrame message handlers

int CTimeFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CMDIChildWnd::OnCreate(lpCreateStruct) == -1)
		return -1;
/*
	if (!m_wndToolBar.Create( this, WS_CHILD|WS_VISIBLE|CBRS_TOP,IDR_TIME_TOOLBAR) ||
		!m_wndToolBar.LoadToolBar(IDR_TIME_TOOLBAR))
	{
		TRACE0("Failed to create toolbar\n");
		return -1;      // fail to create
	}
*/	
/*
	if (!m_wndDlgBar.Create(IDD_DIALOG_ZOOM,this))
	{
		TRACE0("Failed to create dialogbar\n");
		return -1;		// fail to create
	}

*/	




	return 0;
}

BOOL CTimeFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) 
{
/*
	if( !m_wndTimeToolBar.Create( this, WS_CHILD|WS_VISIBLE|CBRS_TOP,IDR_TIME_TOOLBAR) ||
		!m_wndTimeToolBar.LoadToolBar( IDR_TIME_TOOLBAR ) )
	{
		TRACE0("Failed top create new toolbar");
		return -1;
	}
*/



	if (!m_wndReBar.Create(this) )
	{
		TRACE0("Failed to create rebar\n");
		return -1;      // fail to create
	}

	m_wndZoomDialog.Create( IDD_ZOOM_DIALOG, this );
	

	if( !m_wndReBar.AddBar( &m_wndZoomDialog, NULL ) )
	{
		TRACE0("Failed to add xoom bar to rebar\n");
		return -1;      // fail to create
	}


	CRect rect;
	GetClientRect( &rect );
	CSize size1 = rect.Size();
	CSize size2 = rect.Size();
	CSize size3 = rect.Size();
	size3.cx = 265;
	size2.cx = 290;
	size1.cx -= ( size2.cx + size3.cx );

	if( !m_wndSplitter.CreateStatic( this, 1, 3, WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL ) )
	{
		TRACE0( "Failed to create splitter\n" );
		return FALSE;
	}
	CCreateContext localContext = *pContext;
	localContext.m_pCurrentFrame = this;


//	if( !m_wndSplitter.CreateView( 0, 0, RUNTIME_CLASS( CEventTraceView ), size, pContext ) )
	if( ! m_wndSplitter.CreateView( 0, 0, RUNTIME_CLASS( CGosTracePane ), size1, &localContext ) ||
		! m_wndSplitter.CreateView( 0, 1, RUNTIME_CLASS( CGosEventIdPane ), size2, &localContext ) ||
	/*	! m_wndSplitter.CreateView( 0, 2, RUNTIME_CLASS( CGosEventPane ), size, &localContext ) || */
		! m_wndSplitter.CreateView( 0, 2, RUNTIME_CLASS( CGosStatPane ), size3, &localContext ) ) 
	{
		TRACE0( "Failed to create view\n" );
		return FALSE;
	}

	SCROLLINFO si = { sizeof(SCROLLINFO), SIF_PAGE | SIF_RANGE /* | SIF_POS */,
		0, 40, 30, 0, 0 };
	
	m_wndSplitter.GetPane(0,0)->SetScrollInfo( SB_VERT, &si );

	m_fChildrenReady = true;



		

	return CMDIChildWnd::OnCreateClient(lpcs, pContext);
}

void CTimeFrame::OnSetFocus(CWnd* pOldWnd) 
{
	CMDIChildWnd::OnSetFocus(pOldWnd);

	m_pDoc = (CGosViewDoc *)(this->GetActiveDocument());

	m_pDoc->SetFocus();

}


void CTimeFrame::AddId( DWORD id )
{
	if( !m_pDoc )
		return;

	TagNode *pPrev = m_TagList.Last();
	if( pPrev )
		pPrev->idSibling = id;
	TagNode *pNew = m_TagList.Allocate(id);
	pNew->id = id;
	pNew->idChild = 0;
	pNew->idSibling = 0;
	pNew->idParent = 0;
	pNew->expand = true;
	pNew->screenY = 0;
}

void CTimeFrame::UpdateAllIds()
{
	if( !m_pDoc )
		return;

	m_TagList.Clear();

	for( CGosViewDoc::TagInfoEntry *pTIE = m_pDoc->m_TagInfo.First(); pTIE; pTIE = m_pDoc->m_TagInfo.Next(pTIE) )
		AddId(pTIE->id);

	if( !m_pDoc )
		return;
}

void CTimeFrame::SortAndAddParents()
{
	CGosViewDoc::TagInfoEntry *pTIE;

	if( !m_pDoc )
		return;

	for( m_nOldParentNames=0; m_nOldParentNames<m_nNumParentNames; m_nOldParentNames++ )
	{
		strcpy( m_aOldParentName[m_nOldParentNames], m_aParentName[m_nOldParentNames] );
		m_aOldParentExpand[m_nOldParentNames] = m_ParentList.Find(m_aParentID[m_nOldParentNames])->expand;
	}

	m_nNumParentNames = 0;
	m_ParentList.Clear();
	m_FirstID = 0;

	for( pTIE = m_pDoc->m_TagInfo.First(); pTIE; pTIE = m_pDoc->m_TagInfo.Next(pTIE) )
	{
		TagNode *pNode = m_TagList.Find(pTIE->id);
		pNode->idSibling = 0;
		pNode->idChild = 0;
		pNode->idParent = 0;
	}

	char parentName[256];


	for( pTIE = m_pDoc->m_TagInfo.First(); pTIE; pTIE = m_pDoc->m_TagInfo.Next(pTIE) )
	{
		TagNode *pNode = m_TagList.Find(pTIE->id);
		int idParent = 0;
		int level = 0;
		char *pName = pTIE->Name;
		for( int nameLen = 1; pTIE->Name[nameLen] != '\0'; nameLen++ )
		{
			if( ( pTIE->Name[nameLen] == ':' ) && ( pTIE->Name[nameLen+1] == ':' ) )
			{
				strcpy( parentName, pTIE->Name );
				parentName[nameLen] = '\0';
				idParent = IDFindOrAddParent( idParent, parentName );
				AnyIDToTag(idParent)->indent = level++;
			}
			if( pTIE->Name[nameLen] == '\\' )
			{
				strcpy( parentName, pTIE->Name );
				parentName[nameLen] = '\0';
				idParent = IDFindOrAddParent( idParent, parentName );
				AnyIDToTag(idParent)->indent = level++;
			}
		}
		AddToChildrenOf( idParent, pName, pTIE->id, pNode );
		pNode->indent = level;
	}

	int i,j;
	for( i=0; i<m_nNumParentNames; i++ )
	{
		for( j=0; j<m_nOldParentNames; j++ )
		{
			if( strcmp( m_aOldParentName[j], m_aParentName[i] ) == 0 )
				AnyIDToTag(m_aParentID[i])->expand = m_aOldParentExpand[j];
		}
	}
}

int CTimeFrame::CompareID( char *pName, DWORD id, DWORD oldID )
{
	if( m_fAlphaSort )
	{
		return strcmp( pName, AnyIDToName(oldID) );
	}
	else
	{
		return ((int)id) - ((int) oldID );
	}
}


void CTimeFrame::AddToChildrenOf( DWORD idParent, char *pName, DWORD id, TagNode *pNode )
{
	TagNode *pParentNode;

	if( idParent )
		pParentNode = AnyIDToTag( idParent );
	pNode->idParent = idParent;
	if( ( idParent == 0 ) && ( m_FirstID == 0 ) )
	{
		m_FirstID = id;
	}
	else if( ( idParent == 0 ) && CompareID( pName, id, m_FirstID ) < 0 )
	{
		pNode->idSibling = m_FirstID;
		m_FirstID = id;
	}
	else if( ( idParent != 0 ) && ( pParentNode->idChild == 0 ) )
	{
		pParentNode->idChild = id;
	}
	else if( ( idParent != 0 ) && ( CompareID( pName, id, pParentNode->idChild ) < 0 ) )
	{
		pNode->idSibling = pParentNode->idChild;
		pParentNode->idChild = id;
	}
	else
	{
		DWORD idRightSibling;
		DWORD idLeftSibling = idParent?pParentNode->idChild:m_FirstID;

		do
		{
			idRightSibling = AnyIDToTag(idLeftSibling)->idSibling;
			if( ( idRightSibling == 0 ) || CompareID( pName, id, idRightSibling ) < 0 )
			{
				AnyIDToTag(idLeftSibling)->idSibling = id;
				pNode->idSibling = idRightSibling;
				break;
			}
			idLeftSibling = idRightSibling;
		} while ( 1 );
	}
}

char *CTimeFrame::AnyIDToName( DWORD id )
{
	if( id == 0 )
	{
		DebugBreak();
	}
	if( id & 0x80000000 )
	{
		for( int i=0; i<m_nNumParentNames; i++ )
		{
			if( m_aParentID[i] == id )
				return m_aParentName[i];
		}
		DebugBreak();	// shouldn't happen
	}

	return m_pDoc->m_TagInfo.Find(id)->Name;
}

CTimeFrame::TagNode *CTimeFrame::AnyIDToTag( DWORD id )
{
	if( id == 0 )
	{
		DebugBreak();
	}
	if( id & 0x80000000 )
	{
		return m_ParentList.Find(id);
	}
	else
	{
		return m_TagList.Find(id);
	}
}
//		AddToChildrenOf( idParent, pName, pTIE->id, pNode );

DWORD CTimeFrame::IDFindOrAddParent( DWORD idParent, char *pName )
{
	DWORD id;
	TagNode *pNode;


	for( CGosViewDoc::TagInfoEntry *pTIE = m_pDoc->m_TagInfo.First(); pTIE; pTIE = m_pDoc->m_TagInfo.Next(pTIE) )
	{
		if( strcmp( pTIE->Name, pName ) == 0 )
			return pTIE->id;
	}
	for( int i=0; i<m_nNumParentNames; i++ )
	{
		if( strcmp( m_aParentName[i], pName ) == 0 )
		{
			return m_aParentID[i];
		}
	}

	// have to add a new dummy parent
	int index = m_nNumParentNames++;
	id = m_nNumParentNames + 0x80000001;
	strcpy( m_aParentName[index], pName );
	m_aParentID[index] = id;
	pNode = m_ParentList.Allocate( id );
	pNode->id = id;
	pNode->idChild = 0;
	pNode->idSibling = 0;
	pNode->expand = true;
	pNode->idNextEnabled = 0;
	pNode->sumTicks = 0;
	pNode->onScreen = false;
	pNode->countEntry = 0;
	AddToChildrenOf( idParent, pName, id, pNode );

	return id;
}


void CTimeFrame::ProcessNewLog()
{
	SortAndAddParents();
	AnalyzeTagNodes();
}

void CTimeFrame::AnalyzeTagNodes()
{
	// Assumes parent/child/sibling relations are OK, does depth-first scan to determine y-values

	TagNode *pNode;
	m_nNumEnabledBands = 0;
	TagNode *pPrevNode = NULL;
	int treeLevel = 0;
	bool independent = true;
	int topIndependent = 0;

	for( pNode = AnyIDToTag(m_FirstID); pNode;  )
	{
		if( pPrevNode )
			pPrevNode->idNextEnabled = pNode->id;
		pNode->independent = independent;
		pPrevNode = pNode;
		m_nNumEnabledBands++;
		if( pNode->expand && pNode->idChild )
		{
			treeLevel++;
			pNode = AnyIDToTag(pNode->idChild);
		}
		else if( true && pNode->idChild )	// change true to ("Show children of non-expanded")
		{
			if( independent )
			{
				independent = false;
				topIndependent = treeLevel;
			}
			treeLevel++;
			pNode = AnyIDToTag(pNode->idChild);
		}
		else if( pNode->idSibling )
		{
			pNode = AnyIDToTag(pNode->idSibling);
		}
		else
		{
			if( !pNode->idParent )
				pNode = NULL;
			while( pNode )
			{
				pNode = AnyIDToTag(pNode->idParent);
				treeLevel--;
				if( treeLevel == topIndependent )
					independent = true;
				if( pNode->idSibling )
				{
					pNode = AnyIDToTag( pNode->idSibling );
					break;
				}
				if( !pNode->idParent )
					pNode = NULL;
			}
		}
	}

	if( pPrevNode )
		pPrevNode->idNextEnabled = 0;


	DetermineVisibleTags();
}

void CTimeFrame::DetermineVisibleTags()
{
	TagNode *pNode;
	DWORD idParent;
	SCROLLINFO si;

	if( m_fChildrenReady )
	{
		RECT rect;
		m_wndSplitter.GetPane(0,0)->GetWindowRect(&rect);
		int childHeight = rect.bottom-rect.top;	// test properly!
		if( m_nNumEnabledBands == 0 )
		{
			m_rRowHeight = (double)childHeight;
			m_nNumRowsVisible = 1;
		}
		else if( childHeight < 16 * m_nNumEnabledBands )
		{
			m_nNumRowsVisible = (int)((double)childHeight/16.0);
			m_rRowHeight = (double)childHeight / (double)m_nNumRowsVisible;
		}
		else
		{
			m_nNumRowsVisible = m_nNumEnabledBands;
			m_rRowHeight = (double)childHeight / (double)m_nNumEnabledBands;
		}

		m_wndSplitter.GetPane(0,0)->GetScrollBarCtrl(SB_VERT)->GetScrollInfo( &si, SIF_ALL );

		// adjust length of thumb
		si.nPage = m_nNumRowsVisible;

		// if( si.nMax >= m_nNumEnabledBands )
			si.nMax = m_nNumEnabledBands-1;
		if( si.nPos > si.nMax )
			si.nPos = si.nMax;

		m_nTopVisibleRow = si.nPos;

		{
			char msg[256];
			sprintf( msg, "TopVisRow=%d", m_nTopVisibleRow );
			CGosEventPane::SetDbgMsg(0,msg);
		}

		for( pNode = m_TagList.First(); pNode; pNode = m_TagList.Next(pNode) )
		{
			pNode->onScreen = false;
			pNode->screenY = -10;
		}
		for( pNode = m_ParentList.First(); pNode; pNode = m_ParentList.Next(pNode) )
		{
			pNode->onScreen = false;
			pNode->screenY = -10;
		}

		int rowNo = 0;

		for( DWORD id = m_FirstID; id; id = pNode->idNextEnabled, rowNo++ )
		{
			pNode = AnyIDToTag( id );
			if( !pNode->independent )
				rowNo--;
			if( ( rowNo >= m_nTopVisibleRow ) && ( rowNo < m_nTopVisibleRow + m_nNumRowsVisible ) )
			{
				pNode->onScreen = true;
				pNode->screenY = (int)(((double)( rowNo - m_nTopVisibleRow ) + 0.5 ) * m_rRowHeight);
			}
		}

		// Now handle collapsed tree sections:

		for( pNode = m_TagList.First(); pNode; pNode = m_TagList.Next(pNode) )
		{
			if( pNode->independent )
			{
				pNode->pAssignTo = pNode;
				continue;
			}
			pNode->pAssignTo = NULL;

			for( idParent = pNode->idParent; idParent; idParent = AnyIDToTag(idParent)->idParent )
			{
				if( AnyIDToTag(idParent)->independent )
				{
					pNode->pAssignTo = AnyIDToTag(idParent);
					break;
				}
			}
		}

		m_wndSplitter.GetPane(0,0)->SetScrollInfo( SB_VERT, &si );

	}
}


void CTimeFrame::OnSize(UINT nType, int cx, int cy) 
{
	
	HWND hWnd = m_wndSplitter.GetSafeHwnd();

	if( hWnd == NULL || !::IsWindow( hWnd ) )
		return;	// the splitter isn't ready yet

	if( m_fChildrenReady )
	{
		int cxCur0, cxCur1, cxCur2, cxMin;
		m_wndSplitter.GetColumnInfo( 1, cxCur1, cxMin );
		m_wndSplitter.GetColumnInfo( 2, cxCur2, cxMin );
		m_wndSplitter.GetColumnInfo( 0, cxCur0, cxMin );

		if( cxCur1 + cxCur2 + 34 < cx )
		{
			m_wndSplitter.SetColumnInfo( 0, cx - cxCur1 - cxCur2 - 34, cxMin );
			m_wndSplitter.RecalcLayout();
		}
	}
	
	CMDIChildWnd::OnSize(nType, cx, cy);



	if( m_pDoc && m_fChildrenReady && m_pDoc->TraceReady() )
	{

		DetermineVisibleTags();

		m_pDoc->UpdateAllViews(NULL);	
	}
}

void CTimeFrame::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
	// TODO: Add your message handler code here and/or call default
	
	CMDIChildWnd::OnHScroll(nSBCode, nPos, pScrollBar);
}

BOOL CTimeFrame::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult) 
{
	// TODO: Add your specialized code here and/or call the base class

	int v = 1;

	if( wParam == IDC_ZOOM_SLIDER )
	{
		if( m_fChildrenReady )
		{
			m_wndSplitter.GetPane(0,0)->Invalidate();
		}
	}
	
	return CMDIChildWnd::OnNotify(wParam, lParam, pResult);
}

void CTimeFrame::OnButton1() 
{
	// TODO: Add your control notification handler code here
	DebugBreak();
	
}

void CTimeFrame::OnAlphaSort() 
{
	m_fAlphaSort = true;
	if( m_pDoc && m_pDoc->TraceReady() )
	{
		ProcessNewLog();
		m_pDoc->UpdateAllViews(NULL);
	}
}

void CTimeFrame::OnUpdateAlphaSort(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable( !m_fAlphaSort );
}

void CTimeFrame::OnNumSort() 
{
	m_fAlphaSort = false;
	if( m_pDoc && m_pDoc->TraceReady() )
	{
		ProcessNewLog();
		m_pDoc->UpdateAllViews(NULL);
	}
}

void CTimeFrame::OnUpdateNumSort(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable( m_fAlphaSort );
}

void CTimeFrame::OnFullNames() 
{
	m_fFullNames = true;
	if( m_pDoc )
		m_pDoc->UpdateAllViews(NULL);
}

void CTimeFrame::OnUpdateFullNames(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable( !m_fFullNames );
}

void CTimeFrame::OnPartNames() 
{
	m_fFullNames = false;
	if( m_pDoc )
		m_pDoc->UpdateAllViews(NULL);
}

void CTimeFrame::OnUpdatePartNames(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable( m_fFullNames );
}

void CTimeFrame::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
	// TODO: Add your message handler code here and/or call default
	
	CMDIChildWnd::OnVScroll(nSBCode, nPos, pScrollBar);
}
