#include "FactoryC.h"
#include "ImportableI.h"
#include "ClassIdC.h"
#include "DemoInterfaceC.h"
#include "ClassDescC.h"
#include "DeviceFeedbackC.h"
#include "ChooseImporterCommonDialogC.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


using namespace PajaTypes;
using namespace Import;
using namespace PluginClass;
using namespace PajaSystem;


DemoInterfaceC::DemoInterfaceC( DeviceContextC* pContext, TimeContextC* pTimeContext, FileListC* pFileList, FactoryC* pFactory ) : //, FileHandleC* pParent, ChooseImporterDialogI* pChooser ) :
	m_pDevContext( pContext ),
	m_pTimeContext( pTimeContext ),
	m_pFileList( pFileList ),
	m_pFactory( pFactory ),
	m_ui32SessionID( 1 )
{
	// empty
}

DemoInterfaceC::~DemoInterfaceC()
{
	for( uint32 i = 0; i < m_rCommonDialogs.size(); i++ )
		m_rCommonDialogs[i]->release();
}


static
bool
file_exists( const char* szName )
{
	FILE*	pStream;
	pStream = fopen( szName, "rb" );
	if( pStream ) {
		fclose( pStream );
		return true;
	}
	return false;
}


static
void
TRACE( const char* szFormat, ...  )
{
	char	szMsg[256];
	va_list	rList;
	va_start( rList, szFormat );
	_vsnprintf( szMsg, 255, szFormat, rList );
	va_end( rList );
	OutputDebugString( szMsg );
}


// imports requested file matching the class filters. returns 0 (null) in failure.
FileHandleC*
DemoInterfaceC::request_import( const char* szInName, const SuperClassIdC& rSuperFilter, const ClassIdC& rClassFilter )
{
	uint32	i, j;

	char szRelName[_MAX_PATH];
	char szAbsName[_MAX_PATH];
	strcpy( szAbsName, get_absolute_path( szInName ) );
	strcpy( szRelName, get_relative_path( szInName ) );

	// check if this file has already been imported
	FileHandleC*	pHandle = m_pFileList->get_file( szRelName, rSuperFilter, rClassFilter );
	if( pHandle ) {
		TRACE( "file '%s' already exists\n", szAbsName );
		return pHandle;
	}

	// Check to see if the file exists.
	if( !file_exists( szAbsName ) ) {
		TRACE( "file '%s' does not exists\n", szAbsName );
		return 0;
	}

	// get the extension of file
	char szFName[_MAX_FNAME];
	char szExt[_MAX_EXT];
	_splitpath( szInName, 0, 0, szFName, szExt );
	if( strlen( szExt ) ) {
		// cutoff the leading dot
		for( i = 0; i < strlen( szExt ) - 1; i++ )
			szExt[i] = szExt[i + 1];
		szExt[i] = '\0';
		_strupr( szExt );	// convert extension to upper case
	}
	else {
		// if there's no extension in the file there's no way to
		// import it.
		TRACE( "The file has no extension (%s)\n", szAbsName );
		return 0;
	}
	int32	i32ExtLen = strlen( szExt );

	// Collect all possible importers
	std::vector<ClassDescC*>	rImpClasses;

	for( i = 0; i < m_pFactory->get_classdesc_count(); i++ ) {
		ClassDescC*	pDesc = m_pFactory->get_classdesc( i );
		if( pDesc->get_classtype() == CLASS_TYPE_FILEIMPORT ) {
			if( rSuperFilter == NULL_SUPERCLASS || (pDesc->get_super_class_id() == rSuperFilter && (rClassFilter == NULL_CLASSID || rClassFilter == pDesc->get_class_id())) ) {
				// class IDs match... try if extensions match
				for( j = 0; j < pDesc->get_ext_count(); j++ ) {
					if( _strnicmp( szExt, pDesc->get_ext( j ), i32ExtLen ) == 0 ) {
						// found match
						rImpClasses.push_back( pDesc );
					}
				}
			}
		}
	}

	ImportableI*	pImp = 0;

	// Get current session
	ImportSessionS*	pSession = 0;
	if( m_rSessions.size() )
		pSession = &m_rSessions[(int32)m_rSessions.size() - 1];

	// Get Choose importer common dialog
	ChooseImporterCommonDialogC*	pChooseImporterDlg = (ChooseImporterCommonDialogC*)get_common_dialog( CLASS_CHOOSEIMPORTERCOMMONDIALOG );

	if( rImpClasses.size() == 1 ) {
		// Only one match, use it.
		pImp = (ImportableI*)rImpClasses[0]->create();
	}
	else if( (rImpClasses.size() > 1) && pSession && pChooseImporterDlg ) {
		// Check if there's a default importer for the extension.

		std::string	sExt = szExt;

		DefaultImpMapT::iterator rIt;
		rIt = pSession->m_rDefaultImp.find( sExt );

		ClassDescC*	pSelDesc = 0;

		if( rIt != pSession->m_rDefaultImp.end() ) {
			// Found
			pSelDesc = rIt->second;
		}
		else {
			// Fire choose importer dialog
			for( uint32 i = 0; i < rImpClasses.size(); i++ ) {
				ClassDescC*	pDesc = rImpClasses[i];
				std::string	sName;

				// Make name
				sName += pDesc->get_name();
				sName += " (";
				for( uint32 j = 0; j < pDesc->get_ext_count(); j++ ) {
					if( j )
						sName += ", ";
					sName += "*.";
					sName += pDesc->get_ext( j );
				}
				sName += ")";

				pChooseImporterDlg->add_importer( sName.c_str(), i );
			}

			pChooseImporterDlg->set_file_name( szRelName );

			if( pChooseImporterDlg->do_modal() ) {
				
				pSelDesc = rImpClasses[pChooseImporterDlg->get_selected_importer()];

				if( pChooseImporterDlg->get_use_extension_to_all() ) {
					// Associate the selected importer to all it's extensions.
					char	szExtension[_MAX_PATH];
					for( uint32 j = 0; j < pSelDesc->get_ext_count(); j++ ) {
						// Make upper case extension
						strcpy( szExtension, pSelDesc->get_ext( j ) );
						_strupr( szExtension );
						std::string	sExt;
						sExt = szExtension;
						// Associate it
						pSession->m_rDefaultImp[sExt] = pSelDesc;
					}
				}
			}

		}

		if( pSelDesc )
			pImp = (ImportableI*)pSelDesc->create();
	}

	if( !pImp ) {
		// we didnt find proper importable
		TRACE( "Could not create importable\n" );
		return 0;
	}

	// Load file
	if( !pImp->load_file( szRelName, this ) ) {
//	if( !pImp->load_file( szAbsName, this ) ) {
		pImp->release();
		TRACE( "load_file() failed\n" );
		return 0;
	}

	// Initialise file
	pImp->initialize( INIT_INITIAL_UPDATE, this );

	// Add file to file list
	pHandle = m_pFileList->add_file( pImp, pSession ? pSession->m_pFolder : 0 );

	return pHandle;
}

void
DemoInterfaceC::initialize_importable( ImportableI* pImp, uint32 ui32Reason )
{
	pImp->initialize( ui32Reason, this );
}

void
DemoInterfaceC::file_update_notify( Import::FileHandleC* pHandle )
{
	// update all files
	for( uint32 i = 0; i < m_pFileList->get_file_count(); i++ ) {
		ImportableI*	pImp = m_pFileList->get_file( i )->get_importable();

		if( !pImp )
			continue;

		for( uint32 j = 0; j < pImp->get_reference_file_count(); j++ ) {
			FileHandleC*	pRef = pImp->get_reference_file( j );
			if( pRef == pHandle ) {
				pImp->update_notify( pHandle );
				break;
			}
		}
	}
}

void
DemoInterfaceC::add_common_dialog( CommonDialogI* pDlg )
{
	m_rCommonDialogs.push_back( pDlg );
}

CommonDialogI*
DemoInterfaceC::get_common_dialog( const ClassIdC& rClassID )
{
	for( uint32 i = 0; i < m_rCommonDialogs.size(); i++ ) {
		if( m_rCommonDialogs[i]->get_class_id() == rClassID )
			return m_rCommonDialogs[i];
	}

	return 0;
}

FileListC*
DemoInterfaceC::get_filelist()
{
	return m_pFileList;
}

DeviceContextC*
DemoInterfaceC::get_device_context()
{
	return m_pDevContext;
}

TimeContextC*
DemoInterfaceC::get_time_context()
{
	return m_pTimeContext;
}

void
DemoInterfaceC::set_project_path( const char* szDir )
{
	m_sProjectPath = szDir;
	if( m_sProjectPath.size() > 0 ) {
		// Remove the ending slash.
		if( m_sProjectPath[(int32)m_sProjectPath.size() - 1] == '\\' || m_sProjectPath[(int32)m_sProjectPath.size() - 1] == '/' )
			m_sProjectPath.resize( (int32)m_sProjectPath.size() - 1 );
	}
}

const char*
DemoInterfaceC::get_project_path() const
{
	return m_sProjectPath.c_str();
}

bool
DemoInterfaceC::is_relative_path( const char* szDir )
{
	if( szDir && strlen( szDir ) > 2 ) {
		if( (szDir[0] == '\\' && szDir[1] == '\\') || (szDir[0] == '/' && szDir[1] == '/') ||
			(szDir[1] == ':' && szDir[2] == '\\') || (szDir[1] == ':' && szDir[2] == '/') )
			return false;
	}
	return true;
}

const char*
DemoInterfaceC::get_relative_path( const char* szDir )
{
	static char	szRelDir[_MAX_PATH];

	if( is_relative_path( szDir ) ) {
		strcpy( szRelDir, szDir );
	}
	else {
		// Check if the file is relative to the project path.
		if( m_sProjectPath.size() > 2 && strlen( szDir ) > (m_sProjectPath.size() + 1) && _strnicmp( m_sProjectPath.c_str(), szDir, m_sProjectPath.size() ) == 0 ) {
			strcpy( szRelDir, szDir + m_sProjectPath.size() + 1 );
		}
		else {
			// If not, use the whole path.
			strcpy( szRelDir, szDir );
		}
	}

	return szRelDir;
}

const char*
DemoInterfaceC::get_absolute_path( const char* szDir )
{
	static char	szAbsDir[_MAX_PATH];

	if( is_relative_path( szDir ) ) {
		strcpy( szAbsDir, m_sProjectPath.c_str() );
		strcat( szAbsDir, "\\" );
		strcat( szAbsDir, szDir );
	}
	else {
		strcpy( szAbsDir, szDir );
	}

	return szAbsDir;
}

void
DemoInterfaceC::set_parent_folder( FileHandleC* pFolder )
{
	if( m_rSessions.size() ) {
		m_rSessions[(int32)m_rSessions.size() - 1].m_pFolder = pFolder;
	}
}

uint32
DemoInterfaceC::begin_session()
{
	ImportSessionS	rSession;
	rSession.m_pFolder = 0;
	rSession.m_ui32SessionID = m_ui32SessionID++;
	
	m_rSessions.push_back( rSession );

	return rSession.m_ui32SessionID;
}

void
DemoInterfaceC::end_session( uint32 ui32SessionID )
{
	if( !m_rSessions.empty() ) {
		if( m_rSessions[(int32)m_rSessions.size() - 1].m_ui32SessionID != ui32SessionID ) {
			TRACE( "DemoInterfaceC::end_session(): session ID mismatch! (%d != %d)\n", m_rSessions[(int32)m_rSessions.size() - 1].m_ui32SessionID, ui32SessionID );
		}
		else {
			m_rSessions.pop_back();
		}
	}
	else {
		TRACE( "DemoInterfaceC::end_session(): no sessions to end!\n" );
	}
}
