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

#include "stdafx.h"
#include "hammer.h"
#include "GameConfig.h"
#include "OptionProperties.h"
#include "OPTTextures.h"
#include "Options.h"
#include "tier1/strtools.h"
#include <shlobj.h>

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

IMPLEMENT_DYNCREATE(COPTTextures, CPropertyPage)

COPTTextures::COPTTextures() : CPropertyPage(COPTTextures::IDD)
{
	//{{AFX_DATA_INIT(COPTTextures)
		// NOTE: the ClassWizard will add member initialization here
	//}}AFX_DATA_INIT

	m_pMaterialConfig = NULL;
	m_bDeleted = FALSE;
}

COPTTextures::~COPTTextures()
{
	// detach the material exclusion list box
	m_MaterialExcludeList.Detach();
}

void COPTTextures::DoDataExchange(CDataExchange* pDX)
{
	CPropertyPage::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(COPTTextures)
	DDX_Control(pDX, IDC_TEXTUREFILES, m_TextureFiles);
	DDX_Control(pDX, IDC_BRIGHTNESS, m_cBrightness);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(COPTTextures, CPropertyPage)
	//{{AFX_MSG_MAP(COPTTextures)
	ON_BN_CLICKED(IDC_EXTRACT, OnExtract)
	ON_BN_CLICKED(IDC_ADDTEXFILE, OnAddtexfile)
	ON_BN_CLICKED(IDC_REMOVETEXFILE, OnRemovetexfile)
	ON_WM_HSCROLL()
	ON_BN_CLICKED(IDC_ADDTEXFILE2, OnAddtexfile2)

	ON_BN_CLICKED( ID_MATERIALEXCLUDE_ADD, OnMaterialExcludeAdd )
	ON_BN_CLICKED( ID_MATERIALEXCLUDE_REM, OnMaterialExcludeRemove )
	ON_LBN_SELCHANGE(ID_MATERIALEXCLUDE_LIST, OnMaterialExcludeListSel)

	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// COPTTextures message handlers

BOOL COPTTextures::OnInitDialog() 
{
	CPropertyPage::OnInitDialog();
	
	// load texture file list with options
	int i;
	for(i = 0; i < Options.textures.nTextureFiles; i++)
	{
		m_TextureFiles.AddString(Options.textures.TextureFiles[i]);
	}

	// set brightness control & values
	m_cBrightness.SetRange(1, 50); // 10 is default
	m_cBrightness.SetPos(int(Options.textures.fBrightness * 10));

	// attach the material exclusion list box
	m_MaterialExcludeList.Attach( GetDlgItem( ID_MATERIALEXCLUDE_LIST )->m_hWnd );

	return TRUE;
}


void COPTTextures::OnExtract() 
{
	// redo listbox content
	m_TextureFiles.ResetContent();
	for(int i = 0; i < Options.textures.nTextureFiles; i++)
	{
		m_TextureFiles.AddString(Options.textures.TextureFiles[i]);
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : 
//-----------------------------------------------------------------------------
void COPTTextures::OnAddtexfile(void)
{
	static char szInitialDir[MAX_PATH] = "\0";

	CFileDialog dlg(TRUE, "wad", NULL, OFN_ALLOWMULTISELECT | OFN_HIDEREADONLY | OFN_NOCHANGEDIR | OFN_FILEMUSTEXIST, "Texture files (*.wad;*.pak)|*.wad; *.pak||");

	if (szInitialDir[0] == '\0')
	{
		Q_snprintf( szInitialDir, sizeof( szInitialDir ), "%s\\wads\\", g_pGameConfig->m_szModDir );
	}

	dlg.m_ofn.lpstrInitialDir = szInitialDir;

	if (dlg.DoModal() != IDOK)
	{
		return;
	}

	//
	// Get all the filenames from the open file dialog.
	//
	POSITION pos = dlg.GetStartPosition();
	CString str;
	while (pos != NULL)
	{
		str = dlg.GetNextPathName(pos);
		str.MakeLower();
		m_TextureFiles.AddString(str);
		SetModified();
	}

	//
	// Use this directory as the default directory for the next time.
	//
	int nBackslash = str.ReverseFind('\\');
	if (nBackslash != -1)
	{
		lstrcpyn(szInitialDir, str, nBackslash + 1);
	}
}


void COPTTextures::OnRemovetexfile() 
{
	int i = m_TextureFiles.GetCount();

	for (i--; i >= 0; i--)
	{
		if (m_TextureFiles.GetSel(i))
			m_TextureFiles.DeleteString(i);
	}

	m_bDeleted = TRUE;
}

void COPTTextures::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
	if(pScrollBar == (CScrollBar*) &m_cBrightness)
		SetModified();
	
	CPropertyPage::OnHScroll(nSBCode, nPos, pScrollBar);
}

BOOL COPTTextures::OnApply() 
{
	Options.textures.fBrightness = (float)m_cBrightness.GetPos() / 10.0f;

	int iSize = m_TextureFiles.GetCount();
	CString str;
	Options.textures.nTextureFiles = iSize;
	Options.textures.TextureFiles.RemoveAll();
	for(int i = 0; i < iSize; i++)
	{
		m_TextureFiles.GetText(i, str);
		Options.textures.TextureFiles.Add(str);
	}
	
	if(m_bDeleted)
	{
		// inform them that deleted files will only be reapplied after
		// they reload the editor
		MessageBox("You have removed some texture files from the list. "
			"These texture files will continue to be used during this "
			"session, but will not be loaded the next time you run "
			"Hammer.", "A Quick Note");
	}

	Options.PerformChanges(COptions::secTextures);

	return CPropertyPage::OnApply();
}

void GetDirectory(char *pDest, const char *pLongName)
{
	strcpy(pDest, pLongName);
	int i = strlen(pDest);
	while (pLongName[i] != '\\' && pLongName[i] != '/' && i > 0)
		i--;

	if (i <= 0)
		i = 0;
	
	pDest[i] = 0;

	return;
}


void COPTTextures::OnAddtexfile2() 
{
	BROWSEINFO bi;
	char szDisplayName[MAX_PATH];

	bi.hwndOwner = m_hWnd;
	bi.pidlRoot = NULL;
	bi.pszDisplayName = szDisplayName;
	bi.lpszTitle = "Select your Quake II directory.";
	bi.ulFlags = BIF_RETURNONLYFSDIRS;
	bi.lpfn = NULL;
	bi.lParam = 0;
	
	LPITEMIDLIST pidlNew = SHBrowseForFolder(&bi);

	if(pidlNew)
	{

		// get path from the id list
		char szPathName[MAX_PATH];
		SHGetPathFromIDList(pidlNew, szPathName);
		
		
		if (AfxMessageBox("Add all subdirectories as separate Texture Groups?", MB_YESNO) == IDYES)
		//if (!strcmpi("\\textures", &szPathName[strlen(szPathName) - strlen("\\textures")]))
		{
			char szNewPath[MAX_PATH];
			strcpy(szNewPath, szPathName);
			strcat(szNewPath, "\\*.*");
			WIN32_FIND_DATA FindData;
			HANDLE hFile = FindFirstFile(szNewPath, &FindData);

			if (hFile != INVALID_HANDLE_VALUE) do
			{
				if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
						&&(FindData.cFileName[0] != '.'))
				{
					sprintf(szNewPath, "%s\\%s", szPathName, FindData.cFileName);
					strlwr(szNewPath);
					if (m_TextureFiles.FindStringExact(-1, szNewPath) == CB_ERR)
						m_TextureFiles.AddString(szNewPath);
				}
			} while (FindNextFile(hFile, &FindData));

		}
		else
		{
			strlwr(szPathName);
			if (m_TextureFiles.FindStringExact(-1, szPathName) == CB_ERR)
				m_TextureFiles.AddString(strlwr(szPathName));
		}
		SetModified();

		// free the previous return value from SHBrowseForFolder
		CoTaskMemFree(pidlNew);

	}
}


static char s_szStartFolder[MAX_PATH];
static int CALLBACK BrowseCallbackProc( HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData )
{
	switch ( uMsg )
	{
		case BFFM_INITIALIZED:
		{
			if ( lpData )
			{
				SendMessage( hwnd, BFFM_SETSELECTION, TRUE, ( LPARAM ) s_szStartFolder );
			}
			break;
		}

		default:
		{
		   break;
		}
	}
         
	return 0;
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pszTitle - 
//			*pszDirectory - 
// Output : Returns TRUE on success, FALSE on failure.
//-----------------------------------------------------------------------------
BOOL COPTTextures::BrowseForFolder( char *pszTitle, char *pszDirectory )
{
	USES_CONVERSION;

	static bool s_bFirst = true;
	if ( s_bFirst )
	{
		APP()->GetDirectory( DIR_MATERIALS, s_szStartFolder );
		s_bFirst = false;
	}

	LPITEMIDLIST pidlStartFolder = NULL;

	IShellFolder *pshDesktop = NULL;
	SHGetDesktopFolder( &pshDesktop );
	if ( pshDesktop )
	{
		ULONG ulEaten;
		ULONG ulAttributes;
		pshDesktop->ParseDisplayName( NULL, NULL, A2OLE( s_szStartFolder ), &ulEaten, &pidlStartFolder, &ulAttributes );
	}	
	
	char szTmp[MAX_PATH];

	BROWSEINFO bi;
	memset( &bi, 0, sizeof( bi ) );
	bi.hwndOwner = m_hWnd;
	bi.pszDisplayName = szTmp;
	bi.lpszTitle = pszTitle;
	bi.ulFlags = BIF_RETURNONLYFSDIRS /*| BIF_NEWDIALOGSTYLE*/;
	bi.lpfn = BrowseCallbackProc;
	bi.lParam = TRUE;

	LPITEMIDLIST idl = SHBrowseForFolder( &bi );

	if ( idl == NULL )
	{
		return FALSE;
	}

	SHGetPathFromIDList( idl, pszDirectory );

	// Start in this folder next time.	
	Q_strncpy( s_szStartFolder, pszDirectory, sizeof( s_szStartFolder ) ); 

	CoTaskMemFree( pidlStartFolder );
	CoTaskMemFree( idl );

	return TRUE;
}


//-----------------------------------------------------------------------------
// Purpose: intercept this call and update the "material" configuration pointer
//          if it is out of sync with the game configuration "parent" (on the
//          "Options Configs" page)
//  Output: returns TRUE on success, FALSE on failure
//-----------------------------------------------------------------------------
BOOL COPTTextures::OnSetActive( void )
{
	//
	// get the current game configuration from the "Options Configs" page
	//
	COptionProperties *pOptProps = ( COptionProperties* )GetParent();
	if( !pOptProps )
		return FALSE;

	CGameConfig *pConfig = pOptProps->Configs.GetCurrentConfig();
	if( !pConfig )
		return FALSE;

	// compare for a change
	if( m_pMaterialConfig != pConfig )
	{
		// update the material config
		m_pMaterialConfig = pConfig;

		// update the all config specific material data on this page
		MaterialExcludeUpdate();

		// update the last material config
		m_pMaterialConfig = pConfig;
	}

	return CPropertyPage::OnSetActive();
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void COPTTextures::MaterialExcludeUpdate( void )
{
	// remove all of the data in the current "material exclude" list box
	m_MaterialExcludeList.ResetContent();

	//
	// add the data from the current material config
	//
	for( int i = 0; i < m_pMaterialConfig->m_MaterialExcludeCount; i++ )
	{
		int result = m_MaterialExcludeList.AddString( m_pMaterialConfig->m_MaterialExclusions[i].szDirectory );
		m_MaterialExcludeList.SetItemData( result, 1 );
		if( ( result == LB_ERR ) || ( result == LB_ERRSPACE ) )
			return;
	}
	if (pGD != NULL)	
	{
		for( int i = 0; i < pGD->m_FGDMaterialExclusions.Count(); i++ )
		{
			char szFolder[MAX_PATH];
			strcpy( szFolder, pGD->m_FGDMaterialExclusions[i].szDirectory );
			strcat( szFolder, " (default)" );
			int result = m_MaterialExcludeList.AddString( szFolder );
			m_MaterialExcludeList.SetItemData( result, 0 );
			if( ( result == LB_ERR ) || ( result == LB_ERRSPACE ) )
				return;
		}
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void StripOffMaterialDirectory( const char *pszDirectoryName, char *pszName )
{
	// clear name
	pszName[0] = '\0';

	// create a lower case version of the string
	char *pLowerCase = _strlwr( _strdup( pszDirectoryName ) );
	char *pAtMat = strstr( pLowerCase, "materials" );
	if( !pAtMat )
		return;

	// move the pointer ahead 10 spaces = "materials\"
	pAtMat += 10;

	// copy the rest to the name string
	strcpy( pszName, pAtMat );

	// free duplicated string's memory
	free( pLowerCase );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void COPTTextures::OnMaterialExcludeAdd( void )
{
	//
	// get the directory path to exclude
	//
	char szTmp[MAX_PATH];
	if( !BrowseForFolder( "Select Game Executable Directory", szTmp ) )
		return;

	// strip off the material directory
	char szSubDirName[MAX_PATH];
	StripOffMaterialDirectory( szTmp, &szSubDirName[0] );
	if( szSubDirName[0] == '\0' )
		return;

	//
	// add directory to list box
	//
	int result = m_MaterialExcludeList.AddString( szSubDirName );
	m_MaterialExcludeList.SetItemData( result, 1 );
	if( ( result == LB_ERR ) || ( result == LB_ERRSPACE ) )
		return;
	
	//
	// add name of directory to the global exclusion list
	//
	int ndx = m_pMaterialConfig->m_MaterialExcludeCount;
	if( ndx >= MAX_DIRECTORY_SIZE )
		return;
	m_pMaterialConfig->m_MaterialExcludeCount++;

	int index = m_pMaterialConfig->m_MaterialExclusions.AddToTail();
	Q_strncpy( m_pMaterialConfig->m_MaterialExclusions[index].szDirectory, szSubDirName, sizeof ( m_pMaterialConfig->m_MaterialExclusions[index].szDirectory ) );

}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void COPTTextures::OnMaterialExcludeRemove( void )
{
	//
	// get the directory to remove
	//
	int ndxSel = m_MaterialExcludeList.GetCurSel();
	if( ndxSel == LB_ERR )
		return;

	char szTmp[MAX_PATH];
	m_MaterialExcludeList.GetText( ndxSel, &szTmp[0] );

	//
	// remove directory from the list box
	//
	int result = m_MaterialExcludeList.DeleteString( ndxSel );
	if( result == LB_ERR )
		return;

	//
	// remove the name of the directory from the global exclusion list
	//
	for( int i = 0; i < m_pMaterialConfig->m_MaterialExcludeCount; i++ )
	{
		if( !strcmp( szTmp, m_pMaterialConfig->m_MaterialExclusions[i].szDirectory ) )
		{
			// remove the directory
			if( i != ( m_pMaterialConfig->m_MaterialExcludeCount - 1 ) )
			{
				m_pMaterialConfig->m_MaterialExclusions.Remove( i );
			}

			// decrement count
			m_pMaterialConfig->m_MaterialExcludeCount--;
		}
	}
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void COPTTextures::OnMaterialExcludeListSel( void )
{
	int ndxSel = m_MaterialExcludeList.GetCurSel();
	if( ndxSel == LB_ERR )
		return;

	char szTmp[MAX_PATH];
	m_MaterialExcludeList.GetText( ndxSel, &szTmp[0] );

	// Item data of 0 = FGD exclusion, 1 = user-created exclusion
	DWORD dwData = m_MaterialExcludeList.GetItemData( ndxSel );
	GetDlgItem( ID_MATERIALEXCLUDE_REM )->EnableWindow( dwData ? TRUE : FALSE );
}