//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
//=============================================================================//

#include "stdafx.h"
#include <oleauto.h>
#include <oaidl.h>
#if _MSC_VER < 1300
#include <afxpriv.h>
#endif
#include "CustomMessages.h"
#include "GlobalFunctions.h"
#include "History.h"
#include "FaceEditSheet.h"
#include "IEditorTexture.h"
#include "MainFrm.h"
#include "MapDoc.h"
#include "MapWorld.h"
#include "ReplaceTexDlg.h"
#include "TextureBrowser.h"
#include "TextureSystem.h"
#include "hammer.h"
#include "Selection.h"

// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>

static LPCTSTR pszIniSection = "Texture Browser";


CStringArray CTextureBrowser::m_FilterHistory;
int CTextureBrowser::m_nFilterHistory;
char CTextureBrowser::m_szLastKeywords[MAX_PATH];


BEGIN_MESSAGE_MAP(CTextureBrowser, CDialog)
	//{{AFX_MSG_MAP(CTextureBrowser)
	ON_WM_SIZE()
	ON_CBN_SELENDOK(IDC_TEXTURESIZE, OnSelendokTexturesize)
	ON_WM_CLOSE()
	ON_WM_TIMER()
	ON_CBN_EDITCHANGE(IDC_FILTER, OnChangeFilterOrKeywords)
	ON_CBN_SELENDOK(IDC_FILTER, OnUpdateFiltersNOW)
	ON_CBN_EDITCHANGE(IDC_KEYWORDS, OnChangeFilterOrKeywords)
	ON_CBN_SELENDOK(IDC_KEYWORDS, OnUpdateKeywordsNOW)
	ON_BN_CLICKED(IDC_FILTER_OPAQUE, OnFilterOpaque)
	ON_BN_CLICKED(IDC_FILTER_TRANSLUCENT, OnFilterTranslucent)
	ON_BN_CLICKED(IDC_FILTER_SELFILLUM, OnFilterSelfIllum)
	ON_BN_CLICKED(IDC_FILTER_ENVMASK, OnFilterEnvmask)
	ON_BN_CLICKED(IDC_SHOW_ERROR, OnShowErrors)
	ON_BN_CLICKED(IDC_USED, OnUsed)
	ON_BN_CLICKED(IDC_MARK, OnMark)
	ON_BN_CLICKED(IDC_REPLACE, OnReplace)
	ON_BN_CLICKED(IDC_TEXTURES_OPEN_SOURCE, OnOpenSource)
	ON_BN_CLICKED(IDC_TEXTURES_RELOAD, OnReload)
	ON_MESSAGE(TWN_SELCHANGED, OnTexturewindowSelchange)
	ON_MESSAGE(TWN_LBUTTONDBLCLK, OnTextureWindowDblClk)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pParent - 
//-----------------------------------------------------------------------------
CTextureBrowser::CTextureBrowser(CWnd* pParent)
	: CDialog(IDD, pParent)
{
	m_szNameFilter[0] = '\0';
	szInitialTexture[0] = '\0';
	m_bFilterChanged = FALSE;
	m_uLastFilterChange = 0xffffffff;
	m_bUsed = FALSE;
	m_szLastKeywords[0] = '\0';
}

//-----------------------------------------------------------------------------
// Purpose: Handles resize messages. Moves the child windows to the proper positions.
// Input  : nType - 
//			cx - 
//			cy - 
//-----------------------------------------------------------------------------
void CTextureBrowser::OnSize(UINT nType, int cx, int cy) 
{
	if (nType == SIZE_MINIMIZED || !IsWindow(m_cTextureWindow.m_hWnd))
	{
		CDialog::OnSize(nType, cx, cy);
		return;
	}
	
	// reposition controls
	CRect clientrect;
	GetClientRect(&clientrect);

	CRect CtrlRect;
	GetDlgItem(IDC_CONTROLHEIGHT)->GetWindowRect(&CtrlRect);

	int iControlHeight = (CtrlRect.bottom - CtrlRect.top);
	int iThisCtrlHeight;

	//
	// Resize the texture window.
	//
	CtrlRect = clientrect;
	CtrlRect.bottom -= iControlHeight;
	m_cTextureWindow.MoveWindow(CtrlRect);

	clientrect.top = (clientrect.bottom - iControlHeight) + 4;

	//
	// Move the top row of controls to the correct vertical position,
	// leaving their horizontal position as it was set up in the dialog.
	//
	int iIDList[] = 
	{
		IDC_TEXTURESIZE,
		IDC_SIZEPROMPT,
		IDC_FILTERPROMPT,
		IDC_FILTER,
		IDC_CURNAME,
		IDC_FILTER_OPAQUE,
		IDC_FILTER_SELFILLUM,
		IDC_SHOW_ERROR,
		IDC_TEXTURES_OPEN_SOURCE,
		-1
	};

	for (int i = 0; iIDList[i] != -1; i++)
	{
		CWnd *pWnd = GetDlgItem(iIDList[i]);
		Assert(pWnd != NULL);

		if (pWnd != NULL)
		{
			pWnd->GetWindowRect(&CtrlRect);
			ScreenToClient(CtrlRect);
			iThisCtrlHeight = CtrlRect.bottom - CtrlRect.top;
			CtrlRect.top = clientrect.top;
			CtrlRect.bottom = CtrlRect.top + iThisCtrlHeight;
			pWnd->MoveWindow(CtrlRect);
		}
	}

	//
	// Move the middle row of controls to the correct vertical position,
	// leaving their horizontal position as it was set up in the dialog.
	//
	int iIDList2[] = 
	{ 
		IDC_KEYWORDS_TEXT,
		IDC_KEYWORDS,
		IDC_USED,
		IDC_MARK,
		IDC_REPLACE,
		IDC_CURDESCRIPTION,
		IDC_FILTER_TRANSLUCENT,
		IDC_FILTER_ENVMASK,
		IDC_TEXTURES_RELOAD,
		-1
	};

	for (int i = 0; iIDList2[i] != -1; i++)
	{
		CWnd *pWnd = GetDlgItem(iIDList2[i]);
		Assert(pWnd != NULL);

		if (pWnd != NULL)
		{
			pWnd->GetWindowRect(&CtrlRect);
			ScreenToClient(CtrlRect);
			iThisCtrlHeight = CtrlRect.bottom - CtrlRect.top;
			CtrlRect.top = clientrect.top + iControlHeight / 2 + 2;
			CtrlRect.bottom = CtrlRect.top + iThisCtrlHeight;
			pWnd->MoveWindow(CtrlRect);
		}
	}

	CDialog::OnSize(nType, cx, cy);
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : bUsed - 
//-----------------------------------------------------------------------------
void CTextureBrowser::SetUsed(BOOL bUsed)
{
	m_bUsed = bUsed;

	if (m_bUsed)
	{
		CUsedTextureList Used;
		GetActiveWorld()->GetUsedTextures(Used);

		m_TextureSubList.RemoveAll();
		for (int i = 0; i < Used.Count(); i++)
		{
			TextureWindowTex_t Tex;
			Tex.pTex = Used.Element(i).pTex;
			Tex.nUsageCount = Used.Element(i).nUsageCount;

			m_TextureSubList.AddToTail(Tex);
		}

		m_cTextureWindow.SetSpecificList(&m_TextureSubList);
	}
	else
	{
		m_cTextureWindow.SetSpecificList(NULL);
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTextureBrowser::OnClose(void)
{
	WriteSettings();
	SaveAndExit();
	CDialog::OnCancel();
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTextureBrowser::OnCancel()
{
	WriteSettings();
	SaveAndExit();
	CDialog::OnCancel();
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTextureBrowser::OnUsed()
{
	if(!GetActiveWorld())
		return;

	SetUsed(m_cUsed.GetCheck());
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pszTexture - 
//-----------------------------------------------------------------------------
void CTextureBrowser::SetInitialTexture(LPCTSTR pszTexture)
{
	strcpy(szInitialTexture, pszTexture);
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTextureBrowser::OnSelendokTexturesize() 
{
	// change size of textures the texutre window displays
	int iCurSel = m_cSizeList.GetCurSel();

	switch(iCurSel)
	{
	case 0:
		m_cTextureWindow.SetDisplaySize(128);
		break;
	case 1:
		m_cTextureWindow.SetDisplaySize(256);
		break;
	case 2:
		m_cTextureWindow.SetDisplaySize(512);
		break;
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
BOOL CTextureBrowser::OnInitDialog() 
{
	CDialog::OnInitDialog();

	// Iterate all the active textures for debugging.
	//int nCount = g_Textures.GetActiveTextureCount();
	//for (int nTexture = 0; nTexture < nCount; nTexture++)
	//{
	//	IEditorTexture *pTexture = g_Textures.GetActiveTexture(nTexture);
	//	const char *pszName = pTexture->GetName();
	//	DBG("%d: %s\n", nTexture, pszName);
	//}
	
	m_cSizeList.SubclassDlgItem(IDC_TEXTURESIZE, this);
	m_cFilter.SubclassDlgItem(IDC_FILTER, this);
	m_cKeywords.SubclassDlgItem(IDC_KEYWORDS, this);
	m_cCurName.SubclassDlgItem(IDC_CURNAME, this);
	m_cCurDescription.SubclassDlgItem(IDC_CURDESCRIPTION, this);
	m_cUsed.SubclassDlgItem(IDC_USED, this);
   
	m_FilterOpaque.SubclassDlgItem(IDC_FILTER_OPAQUE, this);
	m_FilterTranslucent.SubclassDlgItem(IDC_FILTER_TRANSLUCENT, this);
	m_FilterSelfIllum.SubclassDlgItem(IDC_FILTER_SELFILLUM, this);
	m_FilterEnvMask.SubclassDlgItem(IDC_FILTER_ENVMASK, this);
	m_ShowErrors.SubclassDlgItem(IDC_SHOW_ERROR, this);

	m_FilterOpaque.SetCheck( true );
	m_FilterTranslucent.SetCheck( true );
	m_FilterSelfIllum.SetCheck( true );
	m_FilterEnvMask.SetCheck( true );
	m_ShowErrors.SetCheck( true );

	//
	// Create CTextureWindow that takes up area of dummy control.
	//
	{
		RECT r;
		GetDlgItem( IDC_BROWSERDUMMY )->GetClientRect( &r );
		m_cTextureWindow.Create( this, r );
	}

	// Show everything initially
	m_cTextureWindow.SetTypeFilter( ~0, true );

	//
	// Add latest history to the filter combo.
	//
	for (int i = 0; i < m_nFilterHistory; i++)
	{
		m_cFilter.AddString(m_FilterHistory[i]);
	}

	//
	// Set the name filter unless one was explicitly specified.
	//
	if (m_szNameFilter[0] == '\0')
	{
		//
		// No name filter specified. Use whatever is on top of the history.
		//
		if (m_cFilter.GetCount() > 0)
		{
			m_cFilter.GetLBText(0, m_szNameFilter);
			m_cFilter.SetCurSel(0);
		}
	}

	m_cFilter.SetWindowText(m_szNameFilter);
	m_cTextureWindow.SetNameFilter(m_szNameFilter);

	//
	// Done with the name filter; clear it for next time.
	//
	m_szNameFilter[0] = '\0';

	// Add the global list of keywords to the keywords combo.
	for( int i=0; i< g_Textures.GetNumKeywords(); i++ )
	{
		m_cKeywords.AddString( g_Textures.GetKeyword(i) );
	}

	//
	// Set the keyword filter.
	//
	m_cKeywords.SetWindowText(m_szLastKeywords);
	m_cTextureWindow.SetKeywords(m_szLastKeywords);

	m_cUsed.SetCheck(m_bUsed);

	// Refresh the list of used textures if enabled.
	if (m_bUsed)
	{
		SetUsed(TRUE);
	}

	CWinApp *pApp = AfxGetApp();
	CString str = pApp->GetProfileString(pszIniSection, "Position");
	if (!str.IsEmpty())
	{
		RECT r;
		sscanf(str, "%d %d %d %d", &r.left, &r.top, &r.right, &r.bottom);

		if (r.left < 0)
		{
			ShowWindow(SW_SHOWMAXIMIZED);
		}
		else
		{
			MoveWindow(r.left, r.top, r.right-r.left, r.bottom-r.top, FALSE);
		}
	}

	int iSize = pApp->GetProfileInt(pszIniSection, "ShowSize", 0);
	m_cSizeList.SetCurSel(iSize);
	OnSelendokTexturesize();

	if (szInitialTexture[0])
	{
		m_cTextureWindow.SelectTexture(szInitialTexture);
	}

	m_cTextureWindow.ShowWindow(SW_SHOW);

	SetTimer(1, 500, NULL);

	m_cFilter.SetFocus();

	return(FALSE);
}


//-----------------------------------------------------------------------------
// Purpose: Called when either the filter combo or the keywords combo text changes.
//-----------------------------------------------------------------------------
void CTextureBrowser::OnChangeFilterOrKeywords() 
{
	//
	// Start a timer to repaint the texture window using the new filters.
	//
	m_uLastFilterChange = time(NULL);
	m_bFilterChanged = TRUE;
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTextureBrowser::OnUpdateFiltersNOW() 
{
	m_uLastFilterChange = time(NULL);
	m_bFilterChanged = FALSE;

	CString str;
	int iSel = m_cFilter.GetCurSel();
	m_cFilter.GetLBText(iSel, str);
	m_cTextureWindow.SetNameFilter(str);
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTextureBrowser::OnUpdateKeywordsNOW() 
{
	m_uLastFilterChange = time(NULL);
	m_bFilterChanged = FALSE;

	CString str;
	int iSel = m_cKeywords.GetCurSel();
	m_cKeywords.GetLBText(iSel, str);
	m_cTextureWindow.SetKeywords(str);
}


//-----------------------------------------------------------------------------
// Purpose: Timer used to control updates when the filter terms change.
// Input  : nIDEvent - 
//-----------------------------------------------------------------------------
void CTextureBrowser::OnTimer(UINT nIDEvent) 
{
	if (!m_bFilterChanged)
	{
		return;
	}

	if ((time(NULL) - m_uLastFilterChange) > 0)
	{
		KillTimer(nIDEvent);
		m_bFilterChanged = FALSE;

		m_cTextureWindow.EnableUpdate(false);

		CString str;
		m_cFilter.GetWindowText(str);
		m_cTextureWindow.SetNameFilter(str);

		m_cTextureWindow.EnableUpdate(true);

		m_cKeywords.GetWindowText(str);
		m_cTextureWindow.SetKeywords(str);

		SetTimer(nIDEvent, 500, NULL);
	}

	CDialog::OnTimer(nIDEvent);
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : wParam - 
//			lParam - 
// Output : LRESULT
//-----------------------------------------------------------------------------
LRESULT CTextureBrowser::OnTextureWindowDblClk(WPARAM wParam, LPARAM lParam)
{
	WriteSettings();
	SaveAndExit();
	return(0);
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : wParam - 
//			lParam - 
// Output : LRESULT
//-----------------------------------------------------------------------------
LRESULT CTextureBrowser::OnTexturewindowSelchange(WPARAM wParam, LPARAM lParam)
{
	IEditorTexture *pTex = g_Textures.FindActiveTexture(m_cTextureWindow.szCurTexture);
	CString str;
	char szName[MAX_PATH];

	if (pTex != NULL)
	{
		// create description of texture
		str.Format("%dx%d", pTex->GetWidth(), pTex->GetHeight());
		pTex->GetShortName(szName);
	}
	else
	{
		szName[0] = '\0';
	}

	m_cCurName.SetWindowText(szName);
	m_cCurDescription.SetWindowText(str);

	return(0);
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTextureBrowser::WriteSettings()
{
	// write position information
	CWinApp *pApp = AfxGetApp();
	CString str;
	CRect r;
	GetWindowRect(r);
	str.Format("%d %d %d %d", r.left, r.top, r.right, r.bottom);
	pApp->WriteProfileString(pszIniSection, "Position", str);
	pApp->WriteProfileInt(pszIniSection, "ShowSize", m_cSizeList.GetCurSel());
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTextureBrowser::SaveAndExit()
{
	// save current filter string
	CString str;
	m_cFilter.GetWindowText(str);

	int i;
	for(i = 0; i < m_nFilterHistory; i++)
	{
		if(!m_FilterHistory[i].CompareNoCase(str))
			break;
	}

	if(i != m_nFilterHistory)	// delete first
	{
		m_FilterHistory.RemoveAt(i);
		--m_nFilterHistory;
	}
	
	m_FilterHistory.InsertAt(0, str);
	++m_nFilterHistory;

	m_cKeywords.GetWindowText(m_szLastKeywords, sizeof(m_szLastKeywords));

	EndDialog(IDOK);
}


//-----------------------------------------------------------------------------
// Purpose: Sets a name filter that will override whatever is in the history
//			for this browser session.
//-----------------------------------------------------------------------------
void CTextureBrowser::SetFilter(const char *pszFilter)
{
	if (pszFilter)
	{
		strcpy(m_szNameFilter, pszFilter);
	}
	else
	{
		m_szNameFilter[0] = '\0';
	}
}


//-----------------------------------------------------------------------------
// Filter buttons
//-----------------------------------------------------------------------------

void CTextureBrowser::OnFilterOpaque(void)
{
	bool checked = m_FilterOpaque.GetCheck( ) != 0;
	m_cTextureWindow.SetTypeFilter( CTextureWindow::TYPEFILTER_OPAQUE, checked ); 
}

void CTextureBrowser::OnFilterTranslucent(void)
{
	bool checked = m_FilterTranslucent.GetCheck( ) != 0;
	m_cTextureWindow.SetTypeFilter( CTextureWindow::TYPEFILTER_TRANSLUCENT, checked ); 
}

void CTextureBrowser::OnFilterSelfIllum(void)
{
	bool checked = m_FilterSelfIllum.GetCheck( ) != 0;
	m_cTextureWindow.SetTypeFilter( CTextureWindow::TYPEFILTER_SELFILLUM, checked ); 
}

void CTextureBrowser::OnFilterEnvmask(void)
{
	bool checked = m_FilterEnvMask.GetCheck( ) != 0;
	m_cTextureWindow.SetTypeFilter( CTextureWindow::TYPEFILTER_ENVMASK, checked ); 
}

void CTextureBrowser::OnShowErrors(void)
{
	bool checked = m_ShowErrors.GetCheck( ) != 0;
	m_cTextureWindow.ShowErrors( checked ); 
}


//-----------------------------------------------------------------------------
// Opens the source file: 
//-----------------------------------------------------------------------------
void CTextureBrowser::OnOpenSource()
{
	if ( m_cTextureWindow.szCurTexture[0] )
	{
		g_Textures.OpenSource( m_cTextureWindow.szCurTexture );
	}
}

void CTextureBrowser::OnReload()
{
	if ( m_cTextureWindow.szCurTexture[0] )
	{
		g_Textures.ReloadTextures( m_cTextureWindow.szCurTexture );
		m_cTextureWindow.Invalidate();

		if (GetMainWnd())
		{
			GetMainWnd()->m_TextureBar.NotifyGraphicsChanged();
			GetMainWnd()->m_pFaceEditSheet->NotifyGraphicsChanged();
		}
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTextureBrowser::OnMark(void)
{
	CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
	if (pDoc != NULL)
	{
		pDoc->ReplaceTextures(m_cTextureWindow.szCurTexture, "", TRUE, 0x100, FALSE, FALSE);
		EndDialog(IDOK);
	}
}


//-----------------------------------------------------------------------------
// Purpose: Invokes the replace texture dialog.
//-----------------------------------------------------------------------------
void CTextureBrowser::OnReplace(void)
{
	CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
	if(!pDoc)
		return;

	CReplaceTexDlg dlg(pDoc->GetSelection()->GetCount());

	dlg.m_strFind = m_cTextureWindow.szCurTexture;

	if(dlg.DoModal() != IDOK)
		return;
	
	// mark undo position
	GetHistory()->MarkUndoPosition(pDoc->GetSelection()->GetList(), "Replace Textures");

	if(dlg.m_bMarkOnly)
	{
		pDoc->SelectObject(NULL, scClear);	// clear selection first
	}

	dlg.DoReplaceTextures();

	//EndDialog(IDOK);

	if (m_bUsed)
	{
		SetUsed(TRUE);
	}
}


//-----------------------------------------------------------------------------
// Purpose: Sets the texture format for browsing. Only textures of the given
//			format will be visible in the browse window. By default, this
//			format will be the same as the current active texture format.
// Input  : eTextureFormat - Texture format to use for browsing.
//-----------------------------------------------------------------------------
void CTextureBrowser::SetTextureFormat(TEXTUREFORMAT eTextureFormat)
{
	m_cTextureWindow.SetTextureFormat(eTextureFormat);
}