////////////////////////////////////////////////////////
// 
// AVP_Platform_DevelopmentFrontEnd.cpp
//
// Created by 
// From GUN_Platform_DevelopmentFrontEnd.cpp by Anthony Gregson - 13/08/04 
// Modified for PC-CLI by Johan Lestrom - 01/04/09
//
// Development front-end. Supports quick starting of any level\game mode via a 
// text-only interface by-passing the full front-end.
//
/////////////////////////////////////////////////////////

#include "Core/Asura.h"

#ifdef AVP_USE_DEVELOPMENT_FRONT_END

#include "FrontEnd/AVP_DevelopmentFrontEnd.h"

#include "Core/Asura_List.h"
#include "Core/Asura_UserInput.h"

#include "Input/Asura_ControlMap.h"
#include "Input/Asura_ControlMapping_Key.h"
#include "Input/Asura_PlatformInput.h"
#include "Main/Asura_PC-CLI_Manager.h"

// D E B U G V A R S /////////////////////////////////////////////////////////

DEBUGVAR float dbg_fFontSize = 0.001f;
DEBUGVAR float dbg_fYPositionSpacing = 0.1f;

// The PC controls.
static void InitialiseControlsPC(Asura_ControlMap& xMap)
{
	xMap.AddKeyMapping( AVP_DFE_BUTTON_UP,     ASURA_KEYID_UP,        Asura_ControlMapping::ON_DEBOUNCED_PRESS );
	xMap.AddKeyMapping( AVP_DFE_BUTTON_DOWN,   ASURA_KEYID_DOWN,      Asura_ControlMapping::ON_DEBOUNCED_PRESS );
	xMap.AddKeyMapping( AVP_DFE_BUTTON_LEFT,   ASURA_KEYID_LEFT,      Asura_ControlMapping::ON_DEBOUNCED_PRESS );
	xMap.AddKeyMapping( AVP_DFE_BUTTON_RIGHT,  ASURA_KEYID_RIGHT,     Asura_ControlMapping::ON_DEBOUNCED_PRESS );
	xMap.AddKeyMapping( AVP_DFE_BUTTON_SELECT, ASURA_KEYID_ENTER,     Asura_ControlMapping::ON_DEBOUNCED_PRESS );
	xMap.AddKeyMapping( AVP_DFE_BUTTON_CANCEL, ASURA_KEYID_BACKSPACE, Asura_ControlMapping::ON_DEBOUNCED_PRESS );
	xMap.AddCombinationMapping( AVP_DFE_BUTTON_QUIT, new Asura_ControlMapping_Combination( new Asura_ControlMapping_Key( ASURA_KEYID_ALT, Asura_ControlMapping::ON_PRESS, 1.0f ), new Asura_ControlMapping_Key( ASURA_KEYID_F4, Asura_ControlMapping::ON_PRESS, 1.0f ), 1.0f ) );
}

void AVP_DevelopmentFrontEnd::Platform_Initialise()
{
	// Alwayes ensure that we've bound the mouse/keyboard controls...
	InitialiseControlsPC(*s_pxControlMap);

	if ( Asura_PCCLI_Manager::GetHostRequested() )
	{
		s_iCurrentGameMode = AVP_DFE_GAMEMODE_HOST_NETWORK_GAME;
		s_bSetupComplete = true;
	}
	else if ( Asura_PCCLI_Manager::GetJoinRequested() )
	{
		s_iCurrentGameMode = AVP_DFE_GAMEMODE_JOIN_NETWORK_GAME;
		s_bSetupComplete = true;
	}
	else
	{
#if defined(AVP_USE_DEV_LOBBY)
		s_iCurrentGameMode = AVP_DFE_GAMEMODE_DEV_LOBBY;
		OnAccept();
#endif	//defined(AVP_USE_DEV_LOBBY)
	}
}

void AVP_DevelopmentFrontEnd::Platform_Shutdown(void)
{
}

bool AVP_DevelopmentFrontEnd::Platform_NeedToBuildFMVList()
{
	return false;
}
void AVP_DevelopmentFrontEnd::Platform_RenderBuildFMVList() {}
void AVP_DevelopmentFrontEnd::Platform_StartBuildFMVList() {}
bool AVP_DevelopmentFrontEnd::Platform_UpdateBuildFMVList()
{
	return true;
}
void AVP_DevelopmentFrontEnd::Platform_ClearFMVList()
{
}

void AVP_DevelopmentFrontEnd::Platform_PreviewFMV()
{
}

const Asura_UnicodeChar* AVP_DevelopmentFrontEnd::Platform_GetFMVDescription()
{
	return L"Only works on PSP";
}


//#if 0

//defines for FindFiles and FindDirectories
#define AVP_FILEFIND_RECURSIVE	   0x00000001 //Recurse search through subdirectories
#define AVP_FILEFIND_IGNOREBACKUPDIR 0x00000002 //Skip any directory called 'backup'

//function adapted from code in Tools_FileSystem.h/cpp

template <class T>
static int GetNumEntries(Asura_List<T>& xList)
{
	int iNumEntries =0;
	for(Asura_List_It< Asura_String > xIt(&xList); !xIt.Done(); xIt.Next(), ++iNumEntries){}
	return iNumEntries;
}
static void FindDirectories( const char* szSearchString, Asura_List<Asura_String>& xOutputList, unsigned uFlags /*=0*/ )
{
    Asura_Assert( szSearchString && *szSearchString, "" );

    Asura_List_ReverseIt<Asura_String> xIt( &xOutputList );
    //xIt.end();

    char szBuffer[MAX_PATH];
	WIN32_FIND_DATA xFindData;

	bool bRecursive = (uFlags & AVP_FILEFIND_RECURSIVE) ? true : false;
    const int iInitialNumEntries = GetNumEntries( xOutputList );//xOutputList.GetNumEntries();
    for( int iDir = 0; iDir < 1 + (bRecursive ? (GetNumEntries( xOutputList ) - iInitialNumEntries) : 0); ++iDir )
    {
        char* szFilenameStart;

	    //copy the search string into a buffer for buidling the filenames , 
	    //and work out where the filename part should be copied
        {
            if( iDir )
            {
                if(iDir==1 && iInitialNumEntries==0)
				{
					//If there were no entries in the list to start with , then the iterator will currently be
					//on the sentinel , so we need to restart to get to the first of the newly added entries.
					//If not , then the iterator will be on the last of the original entries , so using next()
					//will get us to the first newly added entry.RAR 17/12/03
					xIt.Restart();
				}
				else
				{
					xIt.Next();
				}
				
				const Asura_String& xNext = *xIt.GetCurrent();
                strcpy( szBuffer, xNext.GetString() );
                strcpy( &szBuffer[xNext.GetLength()], "\\*.*" );
                
            }
            else
            {
                strcpy( szBuffer, szSearchString );
            }

            char* szSlash = strrchr( szBuffer, '\\' );
            if( !szSlash )
            {
                szSlash = strrchr( szBuffer, '/' );
            }
            szFilenameStart = ( szSlash ? szSlash + 1 : szBuffer );
        }

   	    HANDLE hFindFile = ::FindFirstFile( szBuffer, &xFindData );
	    if( INVALID_HANDLE_VALUE != hFindFile )
	    {
		    do
		    {	
			    if( xFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
			    {

                    // Ignore the "." and ".." directories.
                    if( '.' == xFindData.cFileName[0] )
                    {
                        if( !xFindData.cFileName[1] || ('.' == xFindData.cFileName[1] && !xFindData.cFileName[2]) )
                        {
                            continue;
                        }
                    }

					if(uFlags & AVP_FILEFIND_IGNOREBACKUPDIR)
					{
						if(!stricmp(xFindData.cFileName,"Backup") || !stricmp(xFindData.cFileName,"Logfiles"))
						{
							continue;
						}
					}
			    
   				    strcpy( szFilenameStart, xFindData.cFileName );
   				    xOutputList.AddToList( new Asura_String(szBuffer) );
			    }
		    }
		    while( ::FindNextFile(hFindFile, &xFindData) );

		    ::FindClose( hFindFile );
        }
    }
}

//function adapted from code in Tools_FileSystem.h/cpp
static void FindFiles( const char* szSearchString, Asura_List< Asura_String >& xOutputList, unsigned uFlags)
{
    Asura_Assert( szSearchString && *szSearchString, "" );

    Asura_List<Asura_String> xDirList;

    const char* szWildcard;

    {
        const char* szSlash = strrchr( szSearchString, '\\' );
        if( !szSlash )
        {
            szSlash = strrchr( szSearchString, '/' );
        }

        char szTempBuffer[MAX_PATH];
        
        if( szSlash && szSlash > szSearchString )
        {
            const int iLen = szSlash - szSearchString;
            strncpy( szTempBuffer, szSearchString, iLen );
            szTempBuffer[iLen] = '\0';
            xDirList.AddToList( new Asura_String(szTempBuffer) );
        }
        else
        {
            xDirList.AddToList( new Asura_String(szSearchString) );
        }

        if( uFlags & AVP_FILEFIND_RECURSIVE )
        {
            const Asura_String& xRoot = *xDirList.GetHeadObject();
            strcpy( szTempBuffer, xRoot.GetString() );
            strcpy( &szTempBuffer[xRoot.GetLength()], "\\*.*" );
            FindDirectories( szTempBuffer, xDirList, uFlags );
        }

        if( szSlash && *(szSlash + 1) )
        {
            szWildcard = szSlash + 1;
        }
        else
        {
            szWildcard = szSearchString;
        }
    }

//    const int iLengthOfWildcard = strlen( szWildcard );

	char szBuffer[MAX_PATH];

    for( Asura_List_It<Asura_String> xIt(&xDirList); !xIt.Done(); xIt.Next() )
    {
        char* szFilenameStart;

        //copy the search string into a buffer for buidling the filenames , 
	    //and work out where the filename part should be copied
        {
            const Asura_String& xNext = *xIt.GetCurrent();
			const int iLen = xNext.GetLength();
            
            strcpy( szBuffer, xNext.GetString() );
            szBuffer[iLen] = '\\';
            strcpy( &szBuffer[iLen + 1], szWildcard );

            szFilenameStart = &szBuffer[iLen + 1];
        }

	    WIN32_FIND_DATA xFindData;
   	    HANDLE hFindFile = ::FindFirstFile( szBuffer, &xFindData );
	    if( INVALID_HANDLE_VALUE != hFindFile )
	    {
		    do
		    {	
			    if( !(xFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
			    {
                    // (MP - 17.03.2006) Make sure this really does match
                    // the wildcard. The file search stuff in Windows does
                    // some slightly odd stuff in specific cases. For example,
                    // *.asr will match *.asrBE, though bizarrely
                    // *.a will not match *.asr. I think it's related to
                    // 3 letter extensions somewhow.
                    if( Asura_StringUtil::CompareAgainstWildcardString(xFindData.cFileName, szWildcard, true) )
                    {
   				        strcpy( szFilenameStart, xFindData.cFileName );
   				        xOutputList.AddToList(new Asura_String(szBuffer) );
                    }
			    }
		    }
		    while( ::FindNextFile(hFindFile, &xFindData) );

		    ::FindClose( hFindFile );
	    }
    }
	//delete strings from xDirList
	for( Asura_List_It<Asura_String> xIt(&xDirList); !xIt.Done(); xIt.Next() )
    {
		delete (xIt.GetCurrent());
	}
}

static void GetLevelFilesWExtension(Asura_Char* szDirName, Asura_Char* szExt, Asura_List< Asura_String >& xFiles)
{
	//we create a list of all files in a given directory (including subdirectories)
	Asura_String* pxCurrentString;
	Asura_String xStrWildcard(szDirName);
	xStrWildcard += "*";
	xStrWildcard += szExt;

	FindFiles( xStrWildcard.GetString(), xFiles, AVP_FILEFIND_RECURSIVE );
	//ensure that all string ends with given extension and if it so we cut off the extension 		
	for(Asura_List_It<Asura_String> it(&xFiles); !it.Done(); it.Next())
	{
		pxCurrentString  = it.GetCurrent();
		if(!pxCurrentString->EndsWith(szExt))
		{
			delete it.GetCurrent();
			it.DeleteCurrent();		
		}
	}
}

/*Platform_GetLevelFiles - this function should allocate an Asura_String instance 
using new for each file found and add the instance to the list which is passed as the parameter. 
The calling code will call delete for each instance when it has finished.*/

void AVP_DevelopmentFrontEnd::Platform_GetLevelFiles(Asura_List< Asura_String >& xFiles)
{	

	#ifdef ASURA_TOOLS
		GetLevelFilesWExtension("Environments/", ".asr", xFiles );
	#else		
		GetLevelFilesWExtension("Envs/", ".pc", xFiles );
	#endif
	//Asura_Assert(xFiles.GetNumEntries()!=0 , "No levels files found");
}

#endif//#ifdef AVP_USE_DEVELOPMENT_FRONT_END



//#endif // 0
