/***************************************************************************
         Copyright (c) Microsoft Corporation, All rights reserved.             
    This code sample is provided "AS IS" without warranty of any kind, 
    it is not recommended for use in a production environment.
***************************************************************************/

#include "project.h"

#include "lservice.h"
#include "preferences.h"
#include "source.h"
#include "codewinmgr.h"
#include "viewfilter.h"
#include "dllmain.h"

/*---------------------------------------------------------
  CodeWindowManager
-----------------------------------------------------------*/
CodeWindowManager::CodeWindowManager( in LanguageService* lservice, 
                                      in Preferences*     preferences,
                                      in IVsCodeWindow*   codeWindow )
: m_viewFilters()
{
  TRACE_CREATE( "CodeWindowManager", static_cast<IVsCodeWindowManager*>(this) );
  ASSERT(lservice);
  ASSERT(codeWindow);
  ASSERT(preferences);

  m_refCount      = 1;
  m_codeWindow    = codeWindow;   ADDREF(m_codeWindow);
  m_lservice      = lservice;     ADDREF(m_lservice);
  m_preferences   = preferences;  ADDREF(m_preferences);
  m_dropdownBar   = NULL;
  m_objectBrowserList  = NULL;
  m_dropdownBarCache   = NULL;
  m_dropdownBarEntries = 0;
}


CodeWindowManager::~CodeWindowManager()
{
  TRACE_DESTROY( static_cast<IVsCodeWindowManager*>(this) );

  RemoveAdornments();
  
  //close the associated source
  HRESULT hr;
  IVsTextLines* textLines;
  hr = m_codeWindow->GetBuffer( &textLines );
  if (SUCCEEDED(hr))
  {
    Source* source;
    hr = m_lservice->GetSource( textLines, &source );
    RELEASE(textLines);
    if (hr == S_OK)  // S_FALSE means there isn't such Source
    {
      if (hr == S_OK) m_lservice->CloseSource( source ); //calls eventually [Source::Done]
      RELEASE(source);
    }
  }

  FlushDropdownBarCache();

  RELEASE(m_preferences);
  RELEASE(m_codeWindow);
  RELEASE(m_lservice);
  RELEASE(m_dropdownBar);
  RELEASE(m_objectBrowserList);
}

/*---------------------------------------------------------
  IUnknown
-----------------------------------------------------------*/
STDMETHODIMP CodeWindowManager::QueryInterface( in REFIID iid, out void** obj )
{
  OUTARG(obj);

	if (iid == IID_IUnknown || iid == IID_IVsCodeWindowManager)
	{
		TRACE("CodeWindowManager::QueryInterface for IUnknown/IVsCodeWindowManager");
    *obj = static_cast<IVsCodeWindowManager*>(this);
  }
  else if (iid == IID_IVsDropdownBarClient)
  {
    TRACE("CodeWindowManager::QueryInterface for IVsDropdownBarClient");
    *obj = static_cast<IVsDropdownBarClient*>(this);
  }
  else
    return E_NOINTERFACE;

  AddRef();
  return S_OK;
}

STDMETHODIMP_(ULONG) CodeWindowManager::AddRef()
{
  return IncRefCount(&m_refCount);
}

STDMETHODIMP_(ULONG) CodeWindowManager::Release()
{
  if (DecRefCount(&m_refCount) == 0)
  {
    delete this;
    return 0;
  }
  else
    return m_refCount;
}

/*---------------------------------------------------------
  IVsCodeWindowManager
-----------------------------------------------------------*/
STDMETHODIMP CodeWindowManager::AddAdornments()
{
  TRACE("CodeWindowManager::AddAdornments");
  HRESULT hr;
 
  IVsDropdownBarManager *dropdownBarManager;
  hr = m_codeWindow->QueryInterface( IID_IVsDropdownBarManager, 
																	   reinterpret_cast<void **>(&dropdownBarManager) );
  if (FAILED(hr)) return hr;

	hr = dropdownBarManager->GetDropdownBar( &m_dropdownBar );
	if (FAILED(hr)) { 
		RELEASE(dropdownBarManager);
		return hr;
	}

	if (m_dropdownBar != NULL)
	{
		// There's already a drop-down bar, so don't create our own.
		RELEASE(m_dropdownBar);
		m_dropdownBar = NULL;
	}
	else
	{
		hr = dropdownBarManager->AddDropdownBar( 1/* for now */, 
																			       static_cast<IVsDropdownBarClient*>(this) );
		if (FAILED(hr)) {
			RELEASE(dropdownBarManager);
			return hr;
		}
	
	  IVsTextLines* textLines;
		hr = m_codeWindow->GetBuffer( &textLines );
		if (SUCCEEDED(hr))
		{
	    Source* source;
			hr = m_lservice->GetSource( textLines, &source );
			RELEASE(textLines);
			if (hr == S_OK)  // S_FALSE means there isn't such Source
			{
	      source->SetCodeWinMgr( this );
				RELEASE(source);
			}
		}
  }
	RELEASE(dropdownBarManager);

  IVsTextView* textView;
  hr = m_codeWindow->GetPrimaryView( &textView );
  if (FAILED(hr)) return hr;

  hr = OnNewView( textView );
  RELEASE(textView);
  if (FAILED(hr)) return hr;

  hr = m_codeWindow->GetSecondaryView( &textView );
	if (FAILED(hr) && hr != E_FAIL) return hr;

	if (hr != E_FAIL) {
		hr = OnNewView( textView );
		RELEASE(textView);
		if (FAILED(hr)) return hr;
	}

  return S_OK;
}

STDMETHODIMP CodeWindowManager::RemoveAdornments()
{
  TRACE("CodeWindowManager::RemoveAdornments");
  ViewFilter* viewFilter;
  ObjectElem* elem;

  for (m_viewFilters.GetFirst( &elem, (IUnknown**)(&viewFilter)); viewFilter; m_viewFilters.GetNext( &elem, (IUnknown**)(&viewFilter)) )
  {
    viewFilter->Done();
  }

  m_viewFilters.Clear();
  
	HRESULT hr;
	IVsDropdownBarManager *dropdownBarManager;
  hr = m_codeWindow->QueryInterface( IID_IVsDropdownBarManager, 
																	   reinterpret_cast<void **>(&dropdownBarManager) );
	if (FAILED(hr)) return hr;
  dropdownBarManager->RemoveDropdownBar();
	RELEASE(dropdownBarManager);
	
	return S_OK;
}

STDMETHODIMP CodeWindowManager::OnNewView( in IVsTextView* textView )
{
  TRACE("CodeWindowManager::OnNewView");
  HRESULT hr;

  ViewFilter* viewFilter = new ViewFilter( m_lservice, m_preferences, textView, this );
  if (!viewFilter) return E_OUTOFMEMORY;

  hr = viewFilter->Init();
  if (FAILED(hr)) { viewFilter->Done(); RELEASE(viewFilter); return hr; }

  hr = m_viewFilters.Insert( static_cast<IVsTextViewFilter*>(viewFilter) );
  RELEASE(viewFilter);
  if (FAILED(hr)) return hr;

  return S_OK;
}

/*---------------------------------------------------------
  Updating
-----------------------------------------------------------*/

STDMETHODIMP CodeWindowManager::UpdateDropdowns( in IBabelProject *project, in VSITEMID itemid )
{
	HRESULT hr;
	TRACE("DropdownBar::Update");

	FlushDropdownBarCache();

	RELEASE(m_objectBrowserList);
	if (project != NULL)
	{
		hr = project->GetObjectBrowserList( itemid, &m_objectBrowserList );
		if (FAILED(hr)) return hr;
	}

	if (m_dropdownBar != NULL)
	{
		m_dropdownBar->RefreshCombo(0,0);
	}

	return S_OK;
}

STDMETHODIMP_(void) CodeWindowManager::FlushDropdownBarCache()
{  
	// flush the cache
	ULONG i;
	for (i = 0; i < m_dropdownBarEntries; i++) {
		FREE(m_dropdownBarCache[i]);
	}
	FREE(m_dropdownBarCache); // sets it to NULL too
	m_dropdownBarEntries = 0;
}

/*---------------------------------------------------------
  IVsDropdownBarClient
-----------------------------------------------------------*/

STDMETHODIMP CodeWindowManager::SetDropdownBar( 
            /* [in] */ IVsDropdownBar *pDropdownBar)
{
	TRACE("CodeWindowManager::SetDropdownBar");
	RELEASE(m_dropdownBar);
	m_dropdownBar = pDropdownBar;
	if (m_dropdownBar) { ADDREF(m_dropdownBar); }
	return S_OK;
}

STDMETHODIMP CodeWindowManager::GetComboAttributes( 
            /* [in] */ long iCombo,
            /* [out] */ ULONG *pcEntries,
            /* [out] */ ULONG *puEntryType,
            /* [out] */ HANDLE *phImageList)
{
	TRACE1("CodeWindowManager::GetComboAttributes %d", iCombo);

	HRESULT hr;
	if (iCombo != 0) return E_FAIL;
	
	if (m_objectBrowserList == NULL) {
		if (pcEntries != NULL) {
				*pcEntries = 0;
		}
	} else {
		hr = m_objectBrowserList->GetItemCount( pcEntries );
		if (FAILED(hr)) return hr;
	}

	// Start a fresh cache if we need to
	ULONG count = *pcEntries;
	if (count > 0 && 
			(count != m_dropdownBarEntries || m_dropdownBarCache == NULL))
	{
		FlushDropdownBarCache();
		m_dropdownBarCache = NALLOC(WCHAR *, count);
		if (!m_dropdownBarCache) { return E_OUTOFMEMORY; }
		memset(m_dropdownBarCache, 0, count * sizeof(WCHAR *));
		m_dropdownBarEntries = count;
	}

	if (puEntryType != NULL) {
			*puEntryType = ENTRY_TEXT | ENTRY_IMAGE; // later: ENTRY_ATTR
	}
	if (phImageList != NULL) {
			// ToDo: do we need to duplicate here?	
			*phImageList = g_imageList;
	}
	return S_OK;
}

STDMETHODIMP CodeWindowManager::GetEntryText( 
            /* [in] */ long iCombo,
            /* [in] */ long iIndex,
            /* [out] */ WCHAR **ppszText)
{
	TRACE1("CodeWindowManager::GetEntryText %d", iIndex);
	OUTARG(ppszText);
  
	if (iCombo != 0 || iIndex < 0) return E_FAIL;

	if (ppszText != NULL && m_objectBrowserList != NULL)
	{
		// Grab it from the cache if we can.
		// NOTE: contrary to the usual COM conventions, the WCHAR* returned
		// from this function is not freed by the caller.  It is our responsibility
		// to free it later, which is the whole reason we have the dropdownBarCache.
		ASSERT(m_dropdownBarCache != NULL);
		ASSERT((long)m_dropdownBarEntries >= iIndex);
		if (m_dropdownBarCache[iIndex] != NULL) {
			*ppszText =	m_dropdownBarCache[iIndex];
			return S_OK;
		}

		HRESULT hr;
		WCHAR *str, *str2;
		
		hr = m_objectBrowserList->GetText( iIndex, TTO_DISPLAYTEXT, (const WCHAR **) &str );
		if (FAILED(hr)) return hr;
		
		str2 = NALLOC(WCHAR,wcslen(str)+1);
		if (str2 == NULL)
			return E_OUTOFMEMORY;
		wcscpy(str2, str);
		*ppszText = str2;
		m_dropdownBarCache[iIndex] = str2;
	}
	return S_OK;
}

STDMETHODIMP CodeWindowManager::GetEntryAttributes( 
            /* [in] */ long iCombo,
            /* [in] */ long iIndex,
            /* [out] */ ULONG *pAttr)
{
	TRACE1("CodeWindowManager::GetEntryAttributes %d", iIndex);
	OUTARG(pAttr);
 
	if (iCombo != 0 || iIndex < 0) return E_FAIL;
	
	if (pAttr != NULL) *pAttr = FONTATTR_PLAIN;

	return S_OK;
}

STDMETHODIMP CodeWindowManager::GetEntryImage( 
            /* [in] */ long iCombo,
            /* [in] */ long iIndex,
						/* [out] */ long *piImageIndex)
{
	TRACE1("CodeWindowManager::GetEntryImage %d", iIndex);
	OUTARG(piImageIndex);
 
	if (iCombo != 0 || iIndex < 0) return E_FAIL;
	
	HRESULT hr;
	if (piImageIndex != NULL && m_objectBrowserList != NULL)
	{
		VSTREEDISPLAYDATA dispd;
		hr = m_objectBrowserList->GetDisplayData( iIndex, &dispd );
		if (FAILED(hr)) return hr;

		*piImageIndex = dispd.Image;
	}
	return S_OK; 
}

STDMETHODIMP CodeWindowManager::OnItemSelected( 
            /* [in] */ long iCombo,
            /* [in] */ long iIndex)
{
	TRACE("CodeWindowManager::OnItemSelected");
  return S_OK;
}

STDMETHODIMP CodeWindowManager::OnItemChosen( 
            /* [in] */ long iCombo,
            /* [in] */ long iIndex)
{
	TRACE("CodeWindowManager::OnItemChosen");
 
	if ( m_objectBrowserList != NULL)
	{
		return m_objectBrowserList->GoToSource( iIndex, GS_DEFINITION );
	}
	else
	{
		return S_OK;
	}
}

STDMETHODIMP CodeWindowManager::JumpToSpan( in long sline, in long scol,
																						in long eline, in long ecol )
{
	TRACE("CodeWindowManager::JumpToSpan");


	IVsTextManager* textManager;
	IVsTextLines*   textLines;
	IVsTextView*    textView;

	HRESULT hr;
	hr = m_codeWindow->GetLastActiveView( &textView );
	if (FAILED(hr)) return hr;
	hr = textView->GetBuffer( &textLines );
	RELEASE(textView);
	if (FAILED(hr)) return hr;

	TextSpan span;
	span.iStartLine = sline;
	span.iStartIndex = scol;
	span.iEndLine = eline;
	span.iEndIndex = ecol;

	hr = m_preferences->GetTextManager( &textManager );
	if (FAILED(hr)) {
		RELEASE(textLines);
		return hr;
	}

	hr = textManager->NavigateToLineAndColumn( 
						static_cast<IVsTextBuffer*>(textLines), 
						LOGVIEWID_TextView, 
						span.iStartLine, span.iStartIndex,
						span.iEndLine, span.iEndIndex );
	RELEASE(textManager);
	RELEASE(textLines);
	return hr;
}

STDMETHODIMP CodeWindowManager::OnComboGetFocus( 
            /* [in] */ long iCombo)
{
	TRACE("CodeWindowManager::OnComboGetFocus");
  return E_NOTIMPL;
}
        
STDMETHODIMP CodeWindowManager::GetComboTipText( 
            /* [in] */ long iCombo,
            /* [out] */ BSTR *pbstrText)
{
	TRACE("CodeWindowManager::GetComboTipText");
	OUTARG(pbstrText);
  return E_NOTIMPL;
}

/* Emacs stuff:
     ;;; Local Variables: ***
     ;;; tab-width: 2 ***
     ;;; End: ***
*/
