/* 
 * 
 * Confidential Information of Telekinesys Research Limited (t/a Havok).  Not for disclosure or distribution without Havok's
 * prior written consent.This software contains code, techniques and know-how which is confidential and proprietary to Havok.
 * Level 2 and Level 3 source code contains trade secrets of Havok. Havok Software (C) Copyright 1999-2008 Telekinesys Research Limited t/a Havok. All Rights Reserved. Use of this software is subject to the terms of an end user license agreement.
 * 
 */

#include <Demos/demos.h>
#include <Demos/Common/Api/Serialize/Browse/BrowseDemo.h>

#include <Common/Serialize/Packfile/Binary/hkBinaryPackfileReader.h>
#include <Common/Serialize/Packfile/Xml/hkXmlPackfileReader.h>
#include <Common/Serialize/Util/hkStructureLayout.h>
#include <Common/Serialize/Util/hkRootLevelContainer.h>
#include <Common/Base/System/Io/IStream/hkIStream.h>
#include <Physics/Utilities/Serialize/hkpPhysicsData.h>


// We can optionally version the packfile contents on load
// This requires linking with hkcompat
#define SIMPLE_LOAD_WITH_VERSIONING
#include <Common/Serialize/Version/hkVersionUtil.h>
#if defined(SIMPLE_LOAD_WITH_VERSIONING)
#	include <Common/Serialize/Version/hkVersionRegistry.h>
	void versionContents(hkPackfileReader* reader)
	{
		if( hkVersionUtil::updateToCurrentVersion( *reader, hkVersionRegistry::getInstance() ) != HK_SUCCESS )
		{
			HK_WARN_ALWAYS(0, "Couldn't update version, skipping.\n");
		}
	}
#else
	void maybeVersionContents(hkPackfileReader* reader)
	{
		if( hkString::strCmp( reader->getContentsVersion(), hkVersionUtil::getCurrentVersion() ) != 0 )
		{
			HK_WARN_ALWAYS(0, "Versioning is disabled, but file is from an older version.\n");
		}
	}
#endif

// We have a different binary file depending on the compiler and platform
static inline void BrowseDemo_getBinaryFileName(hkString& e)
{
	e.printf("%d%d%d%d.hkx", 
		hkStructureLayout::HostLayoutRules.m_bytesInPointer,
		hkStructureLayout::HostLayoutRules.m_littleEndian? 1 : 0,
		hkStructureLayout::HostLayoutRules.m_reusePaddingOptimization? 1 : 0,
		hkStructureLayout::HostLayoutRules.m_emptyBaseClassOptimization? 1 : 0);
} 

BrowseDemo::BrowseDemo( hkDemoEnvironment* env) 
:hkDefaultPhysicsDemo(env),
m_packfileData(HK_NULL),
m_physicsData(HK_NULL),
m_fileSys( new hkNativeFileSystem() ),
m_directories( new hkFileSystem::DirectoryListing() ),
m_listPos(0),
m_prevListPos(0),
m_startPath("Common/Api/Serialize/SimpleLoad/Resources")
{
	// Disable warning: 'm_contactRestingVelocity not set, setting it to REAL_MAX, so that the new collision restitution code will be disabled'
	hkError::getInstance().setEnabled(0xf03243ed, false);
	{
		getFolderContents( m_startPath.cString(), *m_directories );
	}
	
	// Setup the camera
	{
		hkVector4 from(31, 31, 14);
		hkVector4 to(2.5f, -2, -.3f);
		hkVector4 up(0, 0, 1);
		setupDefaultCameras( env, from, to, up );
	}
}

BrowseDemo::~BrowseDemo()
{
	// Re-enable warning
	hkError::getInstance().setEnabled(0xf03243ed, true);

	m_fileSys->removeReference();

	if(m_world != HK_NULL)
	{
		m_world->markForWrite();
		m_world->removeReference();
		m_world = HK_NULL;
	}
	if(m_packfileData != HK_NULL)
	{
		m_packfileData->removeReference();
	}
	if(m_directories)
	{
		delete m_directories;
	}
}

hkResult BrowseDemo::openDir(const char* dirPath)
{
	hkFileSystem::DirectoryListing* tmpdir = new hkFileSystem::DirectoryListing();
	// browse the folder
	if( getFolderContents( dirPath, *tmpdir ) == HK_SUCCESS )
	{
		// if succeeded replace current contents 
		delete m_directories;
		m_directories = tmpdir;
		m_startPath = dirPath;
		if(!m_startPath.endsWith("/"))
		{
			m_startPath += "/";
		}
		return HK_SUCCESS;
	}
	else
	{
		delete tmpdir;
		return HK_FAILURE;
	}
}
hkResult BrowseDemo::getFolderContents( const char* pathName, hkFileSystem::DirectoryListing& dirsOut )
{	
	// tmp dir
	hkFileSystem::DirectoryListing newDirs;
	
	// get directory contents
	if( m_fileSys->listDirectory( pathName , newDirs ) == HK_SUCCESS )
	{
		// get the expected file extension and format
		hkString expected;
		if(m_variantId == 0)// using binary
		{
			BrowseDemo_getBinaryFileName( expected );
		}
		else
		{
			expected = ".xml";
		}
		// copy all directory names
		const hkArray<char*>& directories = newDirs.getDirectories();
		for(int i=0; i<directories.getSize(); ++i)
		{
			dirsOut.addDirectory( directories[i] );
		}
		// copy only filenames matching the expected format
		const hkArray<char*>& files = newDirs.getFiles();
		for(int i=0; i<files.getSize(); ++i)
		{
			hkString tmp( files[i] );
			if(tmp.endsWith(expected))
			{
				dirsOut.addFile(files[i]);
			}
		}

		return HK_SUCCESS;
	}
	return HK_FAILURE;
}

hkResult BrowseDemo::openFile(const char* filename)
{
	const hkBool usingBinary = (m_variantId == 0);
	// create thew suitable reader
	hkPackfileReader* reader = HK_NULL;
	if (usingBinary)
	{
		reader = new hkBinaryPackfileReader();
	}
	else
	{
		reader = new hkXmlPackfileReader();	
	}

	// Load the entire file
	// Open a stream to read the file
	hkResult res;
	{
		hkIstream infile( filename );
		HK_ASSERT( 0x215d080c, infile.isOk() );
		res = reader->loadEntireFile( infile.getStreamReader() );
	}
	if( res == HK_SUCCESS )
	{
		versionContents(reader);

		// Get a handle to the memory allocated by the reader, and add a reference to it.
		// This allows us to delete the reader.
		m_packfileData = reader->getPackfileData();
		m_packfileData->addReference();

		// Get the top level object in the file
		hkRootLevelContainer* container = static_cast<hkRootLevelContainer*>( reader->getContents( hkRootLevelContainerClass.getName() ) );
		HK_ASSERT2(0xa6451543, container != HK_NULL, "Could not load root level obejct" );

		m_physicsData = static_cast<hkpPhysicsData*>( container->findObjectByType( hkpPhysicsDataClass.getName() ) );
		HK_ASSERT2(0xa6451544, m_physicsData != HK_NULL, "Could not find physics data in root level object" );
	}

	// We can now delete the reader. Note we still keep a reference to the allocated data.
	reader->removeReference(); // same as delete since we have the only reference

	return res;
}

hkResult BrowseDemo::backOneLevel()
{
	hkResult res = HK_FAILURE;
	int ind = m_startPath.lastIndexOf( '/', 0, m_startPath.getLength()-2 ); // don't get the last "/"
	if( ind > 0)
	{
		// update directory contents and startPath
		return openDir( (m_startPath.substr(0, ind+1)).cString() );
	}
	return res;
}



void BrowseDemo::setupWorld()
{
	HK_ASSERT2(0xa6451535, m_physicsData->getWorldCinfo() != HK_NULL, "No physics cinfo in loaded file - cannot create a hkWorld" );

	// Create a world and add the physics systems to it
	m_world = new hkpWorld( *m_physicsData->getWorldCinfo() );
	m_world->lock();

	// Register all collision agents
	hkpAgentRegisterUtil::registerAllAgents( m_world->getCollisionDispatcher() );

	// Add all the physics systems to the world
	for ( int i = 0; i < m_physicsData->getPhysicsSystems().getSize(); ++i )
	{
		m_world->addPhysicsSystem( m_physicsData->getPhysicsSystems()[i] );
	}

	// Setup graphics - this creates graphics objects for all rigid bodies and phantoms in the world
	setupGraphics();

	m_world->unlock();
}

hkDemo::Result BrowseDemo::stepDemo()
{
	// the file list must be displayed only if we have the directory contents and 
	// we have not loaded a file yet
	if( m_world==HK_NULL )
	{
		int startH = 20+m_env->m_window->getTVDeadZoneH();
		int startV = 40+m_env->m_window->getTVDeadZoneV();

		m_env->m_textDisplay->outputText("Browse for file :", startH, startV, 0xffffffff);

		const hkArray<char*>& dirs = m_directories->getDirectories();
		const hkArray<char*>& files = m_directories->getFiles();
		
		// display all directory names  //////////////
		int printPos=0;
		for (int i=0; i<dirs.getSize(); ++i, ++printPos)
		{
			startV += 20;
			hkString tmp( dirs[i] );
			if(!tmp.endsWith("/"))
			{
				tmp += "/";
			}
			int color = (printPos==m_listPos)? 0x0000ffff : 0xffffffff; // change color if selected
			m_env->m_textDisplay->outputText( tmp.cString()/*dirs[i]*/, startH, startV, color );
		}
		// ..and file names
		for (int i=0; i<files.getSize(); ++i, ++printPos)
		{
			startV += 20;
			int color = (printPos==m_listPos)? 0x0000ffff : 0xffffffff; // change color if selected
			m_env->m_textDisplay->outputText(files[i], startH, startV, color);
		}
		/////////
		
		// check pad status /////////////////
		const hkgPad* pad = m_env->m_gamePad;
		if( pad->wasButtonPressed( HKG_PAD_DPAD_DOWN ) ) // scroll down
		{
			// move cursor down if not already at the end
			m_listPos = (m_listPos < dirs.getSize() + files.getSize() - 1)? m_listPos+1 : m_listPos;
		} else if( pad->wasButtonPressed( HKG_PAD_DPAD_UP ) ) // scroll up
		{
			// move cursor up if not already at the top
			m_listPos = (m_listPos > 0)? m_listPos-1 : m_listPos;
		} else if( pad->wasButtonPressed( HKG_PAD_DPAD_RIGHT ) ) // expand current directory
		{
			// is it a directory?
			if( m_listPos < m_directories->getDirectories().getSize() )
			{
				// open directory and update current lists
				if( openDir( ( m_startPath+(m_directories->getDirectories()[m_listPos]) ).cString() ) == HK_SUCCESS )
				{
					m_prevListPos = m_listPos;
					m_listPos = 0;  
				}
			}
		}else if( pad->wasButtonPressed( HKG_PAD_DPAD_LEFT ) ) // go one level up
		{
			// one level up in the fs hierarchy
			if( backOneLevel() == HK_SUCCESS)
			{
				m_listPos = m_prevListPos;
			}
		}else if( pad->wasButtonPressed( HKG_PAD_BUTTON_0 ) ) // open file
		{
			// is it a file?
			int fileInd = m_listPos - m_directories->getDirectories().getSize();
			if( fileInd >= 0 )
			{
				// try to open the file and load contents 
				hkString tmp( m_startPath );
				tmp += "/";
				tmp += m_directories->getFiles()[fileInd];  
				if( openFile( tmp.cString() ) == HK_SUCCESS )
				{
					setupWorld();
					// this is necessary because the first time it was called the world was NULL
					// and setup was not completed
					postConstruct(); 
				}
			}
		}
		//////
	}
	return hkDefaultPhysicsDemo::stepDemo();
}

#if defined(HK_COMPILER_MWERKS)
#	pragma force_active on
#	pragma fullpath_file on
#endif

static const char helpString[] = \
"This demo demonstrates how to browse the filesystem to load a simple physics file.";

HK_DECLARE_DEMO_VARIANT(BrowseDemo, TEST, "Binary",  0 , helpString, helpString);
HK_DECLARE_DEMO_VARIANT(BrowseDemo, TEST, "XML",     1 , helpString, helpString);

/*
* Havok SDK - NO SOURCE PC DOWNLOAD, BUILD(#20080529)
*
* Confidential Information of Havok.  (C) Copyright 1999-2008 
* Telekinesys Research Limited t/a Havok. All Rights Reserved. The Havok
* Logo, and the Havok buzzsaw logo are trademarks of Havok.  Title, ownership
* rights, and intellectual property rights in the Havok software remain in
* Havok and/or its suppliers.
*
* Use of this software for evaluation purposes is subject to and indicates 
* acceptance of the End User licence Agreement for this product. A copy of 
* the license is included with this software and is also available at
* www.havok.com/tryhavok
*
*/
