//***************************************************************************************
//
// File supervisor: Softimage Rendering & Pipeline team
//
// (c) Copyright 2001-2005 Avid Technology, Inc. . All rights reserved.
//
//***************************************************************************************

/****************************************************************************************
THIS CODE IS PUBLISHED AS A SAMPLE ONLY AND IS PROVIDED "AS IS".
IN NO EVENT SHALL SOFTIMAGE, AVID TECHNOLOGY, INC. AND/OR THEIR RESPECTIVE
SUPPLIERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
CONNECTION WITH THE USE OR PERFORMANCE OF THIS CODE .

COPYRIGHT NOTICE. Copyright © 1999-2005 Avid Technology Inc. . All rights reserved. 

SOFTIMAGE is a registered trademark of Avid Technology Inc. or its subsidiaries 
or divisions. Windows NT is a registered trademark of Microsoft Corp. All other
trademarks contained herein are the property of their respective owners. 
****************************************************************************************/

/*! \file plugin_stub.cpp   
    XSI plugin stub functions. This is where the plugin glue is implemented
*/

#include "stdafx.h"
#include "dotXSIConverter.h"
#include "plugin_stub.h"
#include <xsi_application.h>
#include <xsi_context.h>
#include <xsi_menu.h>
#include <xsi_property.h>
#include <xsi_model.h>
#include <xsi_command.h>
#include <xsi_argument.h>
#include <xsi_ppglayout.h>
#include <xsi_time.h>
#include <xsi_parameter.h>
#include <xsi_ppgeventcontext.h>
#include <xsi_selection.h>
#include <xsi_uitoolkit.h>
#include <xsi_griddata.h>
#include <xsi_color.h> 

// Modules
#include "cnv_dotXSI_IO.h"
#include "cnv_COLLADA_IO.h"
#include "cnv_info.h"
#include "cnv_environment.h"
#include "cnv_hierarchytraversal.h"
#include "cnv_model.h"
#include "cnv_null.h"
#include "cnv_modelinfo.h"
#include "cnv_camera.h"
#include "cnv_custompset.h"
#include "cnv_material.h"
#include "cnv_image.h"
#include "cnv_instance.h"
#include "cnv_mesh.h"
#include "cnv_mesh_clusterprop.h"
#include "cnv_mesh_cluster.h"
#include "cnv_light.h"
#include "cnv_camera_anim.h"
#include "cnv_custompset_anim.h"
#include "cnv_environment_anim.h"
#include "cnv_material_anim.h"
#include "cnv_light_anim.h"
#include "cnv_modelinfo_anim.h"
#include "cnv_image_anim.h"
#include "cnv_mixer.h"
#include "cnv_userdatablob.h"
#include "cnv_plottedanimation.h"
#include "cnv_envelope.h"
#include "cnv_shapeanimation.h"
#include "cnv_hierarchyelementinfo.h"
#include "cnv_ik.h"

#include <xsi_material.h>
#include <xsi_library.h>
#include <xsi_shader.h>
#include <xsi_materiallibrary.h>
#include <xsi_imageclip.h>

#include <vector>

XSI::CString	gOldPath;

#ifndef unix
BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
    return TRUE;
}
#endif

Selection g_pSelection;
Application g_pApp;
bool g_bMaterialIDError = false;
bool g_bFilenameError = false;
bool g_bNodenameError = false;
char g_pExportName[256];

/****************************************************************************************
XSI SDK registration functions
****************************************************************************************/
_XSI_EXTERN_ CStatus XSILoadPlugin( PluginRegistrar& in_reg )
{
	in_reg.PutAuthor( L"Crytek" );
	in_reg.PutName( L"Crytek Export Plug-In" );
	in_reg.PutVersion( 1, 0 );

	in_reg.RegisterMenu(siMenuMainFileCrosswalkID, L"CrySeparator", false, false);
	in_reg.RegisterMenu(siMenuMainFileCrosswalkID, L"CryExport", false, false);

	in_reg.RegisterCommand( L"CreateCryExportCrosswalkOptions", L"CreateCryExportCrosswalkOptions" );

	in_reg.RegisterProperty( L"CryExportCrosswalkOptions" );

	return CStatus::OK;	
}

void LogMaterials()
{
	g_pApp.LogMessage(L"+++++++++++++");
	CRefArray materialLibraries = g_pApp.GetActiveProject().GetActiveScene().GetMaterialLibraries();
	for( LONG i = 0; i < materialLibraries.GetCount(); i++ )
	{
		MaterialLibrary matLib( materialLibraries[i] );
		CRefArray materials = matLib.GetItems();
		for( LONG j=0; j < materials.GetCount(); j++ )
		{
			Material mat( materials[j] );
			g_pApp.LogMessage( mat.GetFullName() );
		}
	}
	g_pApp.LogMessage(L"+++++++++++++");
}

CString GetMaterialLibraryName( Material material )
{
	CRefArray materialLibraries = g_pApp.GetActiveProject().GetActiveScene().GetMaterialLibraries();
	for( LONG i = 0; i < materialLibraries.GetCount(); i++ )
	{
		MaterialLibrary matLib( materialLibraries[i] );
		CRefArray materials = matLib.GetItems();
		for( LONG j=0; j < materials.GetCount(); j++ )
		{
			Material mat( materials[j] );
			if( mat == material )
			{
				return matLib.GetName();
			}
		}
	}
	return NULL;
}

int GetMaterialLibraryID( Material material )
{
	CRefArray materialLibraries = g_pApp.GetActiveProject().GetActiveScene().GetMaterialLibraries();
	for( LONG i = 0; i < materialLibraries.GetCount(); i++ )
	{
		MaterialLibrary matLib( materialLibraries[i] );
		CRefArray materials = matLib.GetItems();
		for( LONG j=0; j < materials.GetCount(); j++ )
		{
			Material mat( materials[j] );
			if( mat == material )
			{
				return i;
			}
		}
	}
	return -1;
}

int GetMaterialLibraryNumber()
{
	CRefArray materialLibraries = g_pApp.GetActiveProject().GetActiveScene().GetMaterialLibraries();
	return materialLibraries.GetCount();
}

CString GetMaterialNameInMaterialLibrary( Material material )
{
	CRefArray materialLibraries = g_pApp.GetActiveProject().GetActiveScene().GetMaterialLibraries();
	for( LONG i = 0; i < materialLibraries.GetCount(); i++ )
	{
		MaterialLibrary matLib( materialLibraries[i] );
		CRefArray materials = matLib.GetItems();
		for( LONG j=0; j < materials.GetCount(); j++ )
		{
			Material mat( materials[j] );
			if( mat == material )
			{
				return mat.GetFullName();
			}
		}
	}
	return NULL;
}

CString GetImageClipName( ImageClip imageclip )
{
	CRefArray imageClips = g_pApp.GetActiveProject().GetActiveScene().GetImageClips();
	for( LONG i = 0; i < imageClips.GetCount(); i++ )
	{
		ImageClip imageClip( imageClips[i] );
		if( imageClip == imageclip )
		{
			return imageClip.GetFullName();
		}
	}
	return NULL;
}


void SetMaterialLibraryByMaterial( Material material )
{
	g_pApp.LogMessage( material.GetFullName() );

	CRefArray materialLibraries = g_pApp.GetActiveProject().GetActiveScene().GetMaterialLibraries();
	for( LONG i = 0; i < materialLibraries.GetCount(); i++ )
	{
		MaterialLibrary matLib( materialLibraries[i] );
		CRefArray materials = matLib.GetItems();
		for ( LONG j=0; j < materials.GetCount(); j++ )
		{
			Material mat( materials[j] );

			g_pApp.LogMessage( mat.GetFullName() );
			if( mat == material )
			{
				COMMANDS::SetCurrentMaterialLibrary( matLib.GetFullName() );
			}
		}
	}
}

bool DeleteMaterialFromMaterialLibrary( Material material )
{
	CRefArray materialLibraries = g_pApp.GetActiveProject().GetActiveScene().GetMaterialLibraries();
	for( LONG i = 0; i < materialLibraries.GetCount(); i++ )
	{
		MaterialLibrary matLib( materialLibraries[i] );
		CRefArray materials = matLib.GetItems();
		for ( LONG j=0; j < materials.GetCount(); j++ )
		{
			Material mat( materials[j] );
			if( mat == material )
			{
				COMMANDS::DeleteObj(mat.GetFullName());
			}
		}
	}
	return false;
}

typedef struct
{
	long physics;
	float r;
	float g;
	float b;
	float tr;
	float tg;
	float tb;
	float ta;
	bool transparency;
	long materialID;
	long materialLibID;
}sCryShader;

// Material
std::vector<CString> g_pImages;
std::vector<int> g_pImageNumbers;
std::vector<CRef> g_pNewMaterials;
std::vector<CString> g_pOldMaterials;
std::vector<sCryShader> g_pCryShaders;

std::vector<MaterialLibrary> g_pMaterialLibraries;

// ObjectsList (material/objectname)
std::vector<CString> g_pNames;
std::vector<CString> g_pMaterials;

// CryExportNode
std::vector<CString> g_pNodeNames;
std::vector<CRef> g_pNodeObjects;

CString GetObjectsListByMaterialName( CString materialName )
{
	CString objectsList = L"";
	for( DWORD i = 0; i < g_pNames.size(); i++ )
	{
		CString name = g_pNames.at(i);
		CString matName = g_pMaterials.at(i);
		if( matName == materialName )
		{
			objectsList += L",";
			objectsList += name;
		}
	}
	return objectsList;
}

CString GetObjectsListByMaterial( Material material )
{
	CString objectsList = L"";
	for( DWORD i = 0; i < g_pNames.size(); i++ )
	{
		CString name = g_pNames.at(i);
		CString matName = g_pMaterials.at(i);
		if( matName == material.GetName() )
		{
			objectsList += L",";
			objectsList += name;
		}
	}
	return objectsList;
}

void CheckMaterialID( long materialID, long materialLibID, CString name )
{
	for( DWORD i = 0; i < g_pCryShaders.size(); i++ )
	{
		sCryShader cryshader = g_pCryShaders.at(i);
		if( cryshader.materialID == materialID && cryshader.materialLibID == materialLibID && !g_bMaterialIDError )
		{
			CString error = L"MaterialID error (";
			error += CValue( materialID );
			error += L"): ";
			error	+= g_pNames.at(i);
			error += L",";
			error	+= name;
			UIToolkit uitoolkit = g_pApp.GetUIToolkit();
			LONG result;
			uitoolkit.MsgBox( error, siMsgOkOnly, L"CryExport", result );

			g_bMaterialIDError = true;
		}
	}
}

void CheckMaterialID2( long materialID, long materialLibID )
{
	for( DWORD i = 0; i < g_pCryShaders.size(); i++ )
	{
		sCryShader cryshader = g_pCryShaders.at(i);
		if( cryshader.materialID == materialID && cryshader.materialLibID == materialLibID && !g_bMaterialIDError )
		{
			CString error = L"MaterialID error (";
			error += CValue( materialID+1 );
			error += L")";
			UIToolkit uitoolkit = g_pApp.GetUIToolkit();
			LONG result;
			uitoolkit.MsgBox( error, siMsgOkOnly, L"CryExport", result );

			g_bMaterialIDError = true;
		}
	}
}

void CheckFilename( CString fileName )
{
	if( g_bFilenameError )
		return;
	const char* fn = fileName.GetAsciiString();
	if( strstr( fn, "-" ) )
	{
		UIToolkit uitoolkit = g_pApp.GetUIToolkit();
		LONG result;
		CString error = L"Filename error:" + fileName;
		uitoolkit.MsgBox( error, siMsgOkOnly, L"CryExport", result );

		g_bFilenameError = true;
	}
}

void CheckNodename( CString nodeName )
{
	if( g_bNodenameError )
		return;
	const char* nn = nodeName.GetAsciiString();
	if( strstr( nn, "-" ) )
	{
		UIToolkit uitoolkit = g_pApp.GetUIToolkit();
		LONG result;
		CString error = L"Nodename error:" + nodeName;
		uitoolkit.MsgBox( error, siMsgOkOnly, L"CryExport", result );

		g_bNodenameError = true;
	}
}

X3DObject GetParentCryExportNode( X3DObject &pModel )
{
	X3DObject	l_Object = (X3DObject)pModel;
	X3DObject parent = l_Object.GetParent3DObject();
	CString name = parent.GetName();
	const char *asciiname = name.GetAsciiString();
	while( !strstr(asciiname, "CryExportNode") )
	{
			parent = parent.GetParent3DObject();
			name = parent.GetName();
			asciiname = name.GetAsciiString();
	}
	return parent;
}

void Traverse( CRef &pModel, bool bSel )
{
	X3DObject	l_Object = (X3DObject)pModel;
	CString name = l_Object.GetName();
	CString originalName = name;
	const char *asciiName = name.GetAsciiString();
	g_pApp.LogMessage( name );

	if( strstr( asciiName, "CryExportNode" ) )
	{
		CheckNodename( name );

		Property prop = l_Object.GetProperties().GetItem(L"Properties");
		CString filename = prop.GetParameterValue( L"Filename" );

		CheckFilename( filename );

		CString filetype = prop.GetParameterValue( L"Filetype" );
		bool exportable = prop.GetParameterValue( L"Exportable" );

		g_pNodeNames.push_back( l_Object.GetFullName() );
		g_pNodeObjects.push_back( pModel );

		l_Object.PutName( name + L"-" + filename );
		name = l_Object.GetName();
		l_Object.PutName( name + L"-" + filetype );
		name = l_Object.GetName();
		if( exportable )
			l_Object.PutName( name + L"-DoExport" );
		else
			l_Object.PutName( name + L"-NoExport" );
	}

//	if( bSel )
	{
		Material material = l_Object.GetMaterial();
		g_pApp.LogMessage( material.GetName() );
		CRefArray &shaders = material.GetShaders();
		for (int i = 0; i < shaders.GetCount(); i++ )
		{
			CRef ref(shaders[i]);
			Shader shader(ref);
			g_pApp.LogMessage( shader.GetFullName() );
			g_pApp.LogMessage( shader.GetName() );

			if( shader.GetName() == L"CryShader" )
//			if( shader.GetProgID() == L"Softimage.CryShader.1" )
			{
				g_pNames.push_back( l_Object.GetName() );
				g_pMaterials.push_back( material.GetName() );
			}
		}
	}

	CRefArray	l_Children = l_Object.GetChildren();
	for (int i = 0; i < l_Children.GetCount(); i++)
	{
		CRef child = l_Children.GetItem(i);
		if( strstr( asciiName, "CryExportNode" ) )
		{
			Traverse( child, true );
		}
		else
			Traverse( child, bSel );
	}
}

void ConvertMaterials()
{
	CRefArray materialLibraries = g_pApp.GetActiveProject().GetActiveScene().GetMaterialLibraries();
	for( LONG i = 0; i < materialLibraries.GetCount(); i++ )
	{
		MaterialLibrary matLib( materialLibraries[i] );
		CRefArray materials = matLib.GetItems();
		for( LONG j=0; j < materials.GetCount(); j++ )
		{
			Material mat( materials[j] );
			g_pApp.LogMessage( mat.GetFullName() );

			Material newMaterial;

			long physics;
			Parameter colorParam;
			float r;
			float g;
			float b;
			float tr;
			float tg;
			float tb;
			float ta;
			bool transparency;
			long materialID;
			long materialLibID;
			CString image;
			int imageNumber;
			CString matNameDeleted;

			CRefArray &shaders = mat.GetShaders();
			for (int i = 0; i < shaders.GetCount(); i++ )
			{
				Shader shader(shaders[i]);
				if( shader.GetName() == L"CryShader" )
//				if( shader.GetProgID() == L"Softimage.CryShader.1" )
				{
					sCryShader cryShader;

					CRefArray imageClips = shader.GetImageClips();
					imageNumber = imageClips.GetCount();
					g_pImageNumbers.push_back( imageNumber );
					for ( int j = 0; j < imageNumber; j++ )
					{
						if( j != 0 )			//hack
							continue;

						CRef ref(imageClips[j]);
						ImageClip imageClip( ref );

						g_pApp.LogMessage( imageClip.GetFullName() );

						image = GetImageClipName( imageClip );
						g_pImages.push_back( image );
					}
					physics = shader.GetParameterValue(L"Physics");
					colorParam = shader.GetParameter(L"Diffuse");
					r = colorParam.GetParameterValue(L"red");
					g = colorParam.GetParameterValue(L"green");
					b = colorParam.GetParameterValue(L"blue");
					if( imageNumber )
					{
						r = g = b = 0.7f;
					}
					colorParam = shader.GetParameter(L"Transparency");
					tr = colorParam.GetParameterValue(L"red");
					tg = colorParam.GetParameterValue(L"green");
					tb = colorParam.GetParameterValue(L"blue");
					ta = colorParam.GetParameterValue(L"alpha");
					transparency = shader.GetParameterValue(L"refract_inuse");
					materialID = shader.GetParameterValue(L"MaterialID");
					materialLibID = GetMaterialLibraryID( mat );

					CheckMaterialID2( materialID, materialLibID );

					cryShader.physics = physics;
					cryShader.r = r;
					cryShader.g = g;
					cryShader.b = b;
					cryShader.tr = tr;
					cryShader.tg = tg;
					cryShader.tb = tb;
					cryShader.ta = ta;
					cryShader.transparency = transparency;
					cryShader.materialID = materialID;
					cryShader.materialLibID = materialLibID;

					g_pCryShaders.push_back(cryShader);

					CString matName = mat.GetName();
					matNameDeleted = matName;

					g_pOldMaterials.push_back( mat.GetName() );

					SetMaterialLibraryByMaterial( mat );
					DeleteMaterialFromMaterialLibrary( mat );
					MaterialLibrary materialLibrary = g_pApp.GetActiveProject().GetActiveScene().GetActiveMaterialLibrary();

					newMaterial = materialLibrary.CreateMaterial( L"Phong", L"" );

					CString newMatName = L"";
					CString matlib = GetMaterialLibraryName( newMaterial );
					if( !matlib.IsEmpty() )
					{
						newMatName += matlib;
					}
					newMatName += L"-";
					materialID++;
					if( materialID < 10 )
						newMatName += L"0";
					newMatName += CValue(materialID);
					newMatName += L"-";
					newMatName += matName;
					newMatName += L"-phys";
					newMatName += CValue(physics);
					newMaterial.PutName( newMatName );

					g_pNewMaterials.push_back( newMaterial );
				}
			}

			shaders = newMaterial.GetShaders();
			for (int i = 0; i < shaders.GetCount(); i++ )
			{
				CRef ref(shaders[i]);
				Shader shader(ref);
				colorParam = shader.GetParameter(L"Diffuse");
				colorParam.PutParameterValue( CString(L"red"), r );
				colorParam.PutParameterValue( CString(L"green"), g );
				colorParam.PutParameterValue( CString(L"blue"), b );

				if( imageNumber )
				{
					CString mat = GetMaterialNameInMaterialLibrary( newMaterial ) + L".Phong.diffuse";
					XSI::COMMANDS::SIConnectShaderToCnxPoint( image, mat, true );
				}

				colorParam = shader.GetParameter(L"Transparency");
				colorParam.PutParameterValue( CString(L"red"), tr );
				colorParam.PutParameterValue( CString(L"green"), tg );
				colorParam.PutParameterValue( CString(L"blue"), tb );
				colorParam.PutParameterValue( CString(L"alpha"), ta );

				shader.PutParameterValue( CString(L"refract_inuse"), transparency );

				CString objectsList = GetObjectsListByMaterialName( matNameDeleted );
				XSI::COMMANDS::SelectObj( newMaterial.GetFullName(), L"NODE", false );
				XSI::COMMANDS::AssignMaterial( newMaterial.GetFullName() + objectsList );
			}
		}
	}

	for( DWORD i = 0; i < g_pNodeObjects.size(); i++ )
	{
		CRef obj = g_pNodeObjects.at(i);
		g_pSelection.Add( obj, siSelectBranch );
	}
}

void RestoreMaterials()
{
//	LogMaterials();
	for( DWORD i = 0; i < g_pCryShaders.size(); i++ )
	{
		CString oldMat = g_pOldMaterials.at(i);

		Material mat = g_pNewMaterials.at(i);
		g_pApp.LogMessage( mat.GetFullName() );
		SetMaterialLibraryByMaterial( mat );
		DeleteMaterialFromMaterialLibrary( mat );
		MaterialLibrary materialLibrary = g_pApp.GetActiveProject().GetActiveScene().GetActiveMaterialLibrary();

		Material newMaterial;
		newMaterial = materialLibrary.CreateMaterial( L"CryShader", L"" );

		newMaterial.PutName( oldMat );

		sCryShader cryShader = g_pCryShaders.at(i);
		long physics = cryShader.physics;
		float r = cryShader.r;
		float g = cryShader.g;
		float b = cryShader.b;
		float tr = cryShader.tr;
		float tg = cryShader.tg;
		float tb = cryShader.tb;
		float ta = cryShader.ta;
		bool transparency = cryShader.transparency;
		long materialID = cryShader.materialID;
		long materialLibID = cryShader.materialLibID;
		int imagenumber = g_pImageNumbers.at(i);
		CString image;
		if( imagenumber )
			image = g_pImages.at(i);

		CRefArray &shaders = newMaterial.GetShaders();
		for (int j = 0; j < shaders.GetCount(); j++ )
		{
			CRef ref(shaders[j]);
			Shader shader(ref);
			Parameter colorParam;
			colorParam = shader.GetParameter(L"Diffuse");
			colorParam.PutParameterValue( CString(L"red"), r );
			colorParam.PutParameterValue( CString(L"green"), g );
			colorParam.PutParameterValue( CString(L"blue"), b );
			colorParam = shader.GetParameter(L"Transparency");
			colorParam.PutParameterValue( CString(L"red"), tr );
			colorParam.PutParameterValue( CString(L"green"), tg );
			colorParam.PutParameterValue( CString(L"blue"), tb );
			colorParam.PutParameterValue( CString(L"alpha"), ta );
			shader.PutParameterValue( CString(L"refract_inuse"), transparency );
			shader.PutParameterValue( CString(L"Physics"), physics );
			shader.PutParameterValue( CString(L"MaterialID"), materialID );

			if( imagenumber )
			{
				CString mat = GetMaterialNameInMaterialLibrary( newMaterial ) + L".CryShader.diffuse";
				g_pApp.LogMessage( mat );
				XSI::COMMANDS::SIConnectShaderToCnxPoint( image, mat, true );
			}

			CString objectsList = GetObjectsListByMaterialName( oldMat );
			g_pApp.LogMessage( objectsList );
			XSI::COMMANDS::SelectObj( newMaterial.GetFullName(), L"NODE", false );
			g_pApp.LogMessage( newMaterial.GetFullName() + objectsList );
			XSI::COMMANDS::AssignMaterial( newMaterial.GetFullName() + objectsList );
		}
	}

	g_pCryShaders.clear();
	g_pImages.clear();
	g_pImageNumbers.clear();
	g_pOldMaterials.clear();
	g_pNewMaterials.clear();

	g_pNames.clear();
	g_pMaterials.clear();
}

void Convert()
{
	g_bMaterialIDError = false;
	g_bFilenameError = false;
	g_bNodenameError = false;
	strcpy( g_pExportName, "" );

	g_pSelection = g_pApp.GetSelection();
	g_pSelection.Clear();

	CRef root = g_pApp.GetActiveSceneRoot();
	Traverse( root, false );

	g_pApp.LogMessage( L"-------------------" );

	for( LONG i=0; i<g_pSelection.GetCount(); i++ ) 
	{
		SIObject obj( g_pSelection[i] );
		g_pApp.LogMessage( obj.GetName() );
	}

	g_pApp.LogMessage( L"-------------------" );
}

void RestoreNodeNames()
{
	for( DWORD i = 0; i < g_pNodeObjects.size(); i++ )
	{
		CRef obj = g_pNodeObjects.at(i);
		X3DObject	l_Object = (X3DObject)obj;
		l_Object.PutName(g_pNodeNames.at(i));
	}
	g_pNodeNames.clear();
	g_pNodeObjects.clear();
}

std::vector<CString> g_pSelectionNames;
std::vector<XSI::siBranchFlag> g_pSelectionModes;

void SaveSelections()
{
	g_pSelection = g_pApp.GetSelection();
	g_pApp.LogMessage( L"-------------------" );
	g_pApp.LogMessage( L"-------------------" );
	for( LONG i=0; i<g_pSelection.GetCount(); i++ ) 
	{
		SIObject obj( g_pSelection[i] );
		g_pApp.LogMessage( obj.GetName() );
		ProjectItem projectItem = (ProjectItem)obj;

		g_pSelectionNames.push_back( obj.GetFullName() );
		g_pSelectionModes.push_back( projectItem.GetBranchFlag() );

		if( projectItem.GetBranchFlag() == siNode )
		{
			g_pApp.LogMessage( L"Node" );
		}
		else if( projectItem.GetBranchFlag() == siBranch )
		{
			g_pApp.LogMessage( L"Branch" );
		}
		else
		{
			g_pApp.LogMessage( L"Unspec" );
		}
	}
	g_pApp.LogMessage( L"-------------------" );
	g_pApp.LogMessage( L"-------------------" );

	for( DWORD i = 0; i < g_pSelectionNames.size(); i++ )
	{
		g_pApp.LogMessage( g_pSelectionNames.at(i) );
	}
	g_pApp.LogMessage( L"-------------------" );
	g_pApp.LogMessage( L"-------------------" );
}

void RestoreSelections()
{
	g_pSelection.Clear();

	for( DWORD i = 0; i < g_pSelectionNames.size(); i++ )
	{
		if( g_pSelectionModes.at(i) == siNode )
			XSI::COMMANDS::AddToSelection( g_pSelectionNames.at(i), L"NODE", true );
		else if( g_pSelectionModes.at(i) == siBranch )
			XSI::COMMANDS::AddToSelection( g_pSelectionNames.at(i), L"BRANCH", true );

		g_pApp.LogMessage( g_pSelectionNames.at(i) );
	}

	g_pSelectionModes.clear();
	g_pSelectionNames.clear();
}

void RunResourceCompiler()
{
	CString iniFile = g_pApp.GetInstallationPath( siUserAddonPath ) + L"\\Crytek Addon\\XSICryExport.ini";

	char processName[256];
	memset( processName, 0, 256 );
	FILE *f = fopen( iniFile.GetAsciiString(), "rb" );
	fseek( f, 0, SEEK_END );
	DWORD nSize = ftell( f );
	fseek( f, 0, SEEK_SET );
	fread( processName, nSize, 1, f );
	fclose( f );

	STARTUPINFO si;
	PROCESS_INFORMATION pi;

	ZeroMemory( &si, sizeof(si) );
	si.cb = sizeof(si);
	ZeroMemory( &pi, sizeof(pi) );

	WCHAR pn[256];
	MultiByteToWideChar(CP_ACP, 0, processName, -1, pn, 256);
	WCHAR pn2[256];
	MultiByteToWideChar(CP_ACP, 0, g_pExportName, -1, pn2, 256);

	g_pApp.LogMessage( pn );
	g_pApp.LogMessage( pn2 );

	BOOL result;
#ifndef _X64
	result = CreateProcess( pn, pn2, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi );
#else
	result = CreateProcess( processName, g_pExportName, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi );
#endif

	if( !result )
	{
		UIToolkit uitoolkit = g_pApp.GetUIToolkit();
		LONG result;
		uitoolkit.MsgBox( L"Resource Compiler (RC) not found! See XSICryExport.ini!", siMsgOkOnly, L"CryExport", result );
	}

	// Wait until child process exits.
	WaitForSingleObject( pi.hProcess, INFINITE );

	// Close process and thread handles. 
	CloseHandle( pi.hProcess );
	CloseHandle( pi.hThread );
}

/****************************************************************************************

EXPORT SECTION

****************************************************************************************/
_XSI_EXTERN_ CStatus OnCryExportMenu( CRef& in_ref )
{
	CStatus status = CStatus::OK;

	Property prop = g_pApp.GetActiveSceneRoot().GetProperties().GetItem( L"CryExportCrosswalkOptions" ) ;

	if (!prop.IsValid())
	{
		prop = g_pApp.GetActiveSceneRoot().AddProperty( L"CryExportCrosswalkOptions" ) ;
	}

	// Let's build our label for the property page
	wchar_t l_wLabel[1024];
	swprintf(l_wLabel,
#ifdef unix
		sizeof(l_wLabel),
		L"Crosswalk Export v%ls build %s %s",
#else
		L"Crosswalk Export v%s build %S %S",
#endif
		TOOL_VERSION, __DATE__, __TIME__);

	XSI::COMMANDS::InspectObj(L"CryExportCrosswalkOptions",CValue(),l_wLabel,(LONG)siLockAndForceNew ,false);

	return status;
}

_XSI_EXTERN_ CStatus CryExport_Init( CRef& in_ref )
{
	CStatus status = CStatus::OK;

	Context ctxt = in_ref;
	Menu menu = ctxt.GetSource();

	CStatus st;
	MenuItem item;

	// Here we add our menu item in the export menu
	menu.AddCallbackItem(L"CryExport", L"OnCryExportMenu", item);

	return status;
}

_XSI_EXTERN_ CStatus CrySeparator_Init( CRef& in_ref )
{
	CStatus status = CStatus::OK;

	Context ctxt = in_ref;
	Menu menu = ctxt.GetSource();

	CStatus st;
	MenuItem item;

	menu.AddItem(L" ", siMenuItemSeparator , item);

	return status;
}

_XSI_EXTERN_ CStatus CreateCryExportCrosswalkOptions_Init( const CRef& in_context )
{
	CStatus status = CStatus::OK;
	Context ctx(in_context);
	Command cmd(ctx.GetSource());

	cmd.EnableReturnValue( true ) ; 

	// See xsi_command.h and xsi_argument.h for further
	// attributes you may wish to set. For example Command.PutTooltip(),
	// and ArgumentArray.AddWithHandler()	

	Application app;

	// Configure this code based on the number of arguments
	// you want.  You can also specific default values for these arguments.

	ArgumentArray args = cmd.GetArguments();
	args.Add( L"arg0", L"\0" );
	args.Add( L"arg1", L"CryExportCrosswalkOptions\0" );

	return status;
}

_XSI_EXTERN_ CStatus CreateCryExportCrosswalkOptions_Execute( CRef& in_context )
{
	CStatus status = CStatus::OK;
	Application app;
	Context ctxt(in_context);
	Property l_Property;

	// Access the arguments to the command
	CValueArray		args = ctxt.GetAttribute( L"Arguments" );
	CString			argObjectName(args[0]);
	CString			argOptionName(args[1]);

	if (argOptionName.IsEmpty())
	{
		argOptionName = L"CryExportCrosswalkOptions";
	}

	if (!argObjectName.IsEmpty())
	{
		X3DObject  l_XSIObject = app.GetActiveSceneRoot().FindChild(argObjectName, CString(), CStringArray() );

		if(l_XSIObject.IsValid())
		{
			l_Property = l_XSIObject.AddProperty( L"CryExportCrosswalkOptions" ) ;
		}
	}
	else
	{
		l_Property = app.GetActiveSceneRoot().AddProperty( L"CryExportCrosswalkOptions" ) ;
	}

	if (l_Property.IsValid())
	{
		l_Property.PutName(argOptionName);
	}

	CValue l_Value(l_Property);
	ctxt.PutAttribute( L"ReturnValue", l_Value );

	return status;
}

_XSI_EXTERN_ CStatus CryExportCrosswalkOptions_Define( const CRef & in_Ctx )
{
	CStatus status = CStatus::OK;
	Application app ;
	CustomProperty prop = Context(in_Ctx).GetSource() ;
	Parameter param ;

	// Default capabilities for most of these parameters
	int caps = siPersistable;
	CValue dft ;	// Used for arguments we don't want to set


	// ================================================================================
	// File
	// ================================================================================
	prop.AddParameter(	L"Filename",CValue::siString, caps,
		L"Filename", L"",
		dft, param ) ;

	dft = (LONG)3;
	prop.AddParameter(	L"Format",CValue::siString, caps,
		L"Format", L"",
		dft, param ) ;

	prop.AddParameter(	L"Format",CValue::siUInt4 , caps, 
		L"Format", L"", 
		dft, param ) ;	

	param.PutValue(CValue((LONG)0L));



	Project l_XSIProject = app.GetActiveProject();
	CString l_FileName = l_XSIProject.GetPath();

#ifdef unix
	l_FileName += L"/dotXSI/default.xsi";
#else
	l_FileName += L"\\dotXSI\\default.xsi";
#endif

	if ( gOldPath.IsEmpty() )
	{
		CString l_FileName = l_XSIProject.GetPath();
		l_FileName += L"\\dotXSI\\";
		param.PutValue(l_FileName);
	} else {
		param.PutValue(gOldPath);
	}

	prop.AddParameter(	L"Triangulate",CValue::siBool, caps, 
		L"Triangulate", L"", 
		dft, param ) ;	
	param.PutValue(CValue(false));

	// ================================================================================
	// Set default export parameters
	// ================================================================================
	prop.PutParameterValue ( L"Format", CValue((LONG)COLLADA_FORMAT_1_4_1));

	prop.PutParameterValue ( L"Triangulate", CValue(TRUE));
	param = prop.GetParameter( L"Triangulate" );
	param.PutCapabilityFlag( siReadOnly, true );

	return status;
}

CStatus CryExportCrosswalkOptions_RebuildLayout(PPGLayout &in_Layout, CustomProperty &in_CustomProp)
{
	CStatus status = CStatus::OK;

	PPGItem item ;

	in_Layout.Clear();	

	// ================================================================================
	// General 
	// ================================================================================
	CString l_Empty, l_Version(CROSSWALK_VERSION), l_VersionNumber(CROSSWALK_VERSION_NUMBER);
	l_Version += CString(L" ");
	l_Version += l_VersionNumber;

	in_Layout.AddTab( L"Export" ) ;

	// ================================================================================
	// Export settings
	// ================================================================================
	in_Layout.AddRow();
	in_Layout.AddGroup(l_Version, true, 50);
	in_Layout.AddRow();
	item = in_Layout.AddItem( L"Triangulate", L"Convert Geometry To Triangles" ) ;
	in_Layout.EndRow();
	in_Layout.EndGroup();
	in_Layout.EndRow();

	// ================================================================================
	// File group and the export button
	// ================================================================================
	in_Layout.AddRow();
	in_Layout.AddGroup( L"File name", true, 75 ) ;
	item = in_Layout.AddItem( L"Filename",L"File name",siControlFilePath ) ;
	item.PutAttribute( siUINoLabel, true ) ;
	in_Layout.EndGroup();
	in_Layout.AddGroup( L"Crosswalk", true, 25 ) ;
	item = in_Layout.AddButton(L"Export", L"Export");
	in_Layout.EndGroup();
	in_Layout.EndRow();

	return status;
}

_XSI_EXTERN_ CStatus CryExportCrosswalkOptions_DefineLayout( const CRef & in_Ctx )
{
	CStatus status = CStatus::OK;

	// XSI will call this to define the visual appearance of the CustomProperty
	// The layout is shared between all instances of the CustomProperty
	// and is cached.  You can force the code to re-execute by using the 
	// XSIUtils.Refresh feature.

	PPGLayout oLayout = Context( in_Ctx ).GetSource() ;

	CustomProperty cusProp;
	status = CryExportCrosswalkOptions_RebuildLayout(oLayout, cusProp);

	return status;
}

_XSI_EXTERN_ CStatus CryExportCrosswalkOptions_PPGEvent( const CRef& io_Ctx )
{
	CStatus status = CStatus::OK;
	// This callback is called when events happen in the user interface
	// This is where the "logic" code is implemented.

	Application app ;

	PPGEventContext ctx( io_Ctx ) ;

	PPGEventContext::PPGEvent eventID = ctx.GetEventID() ;

	if ( eventID == PPGEventContext::siParameterChange )
	{		
	}
	else if ( eventID == PPGEventContext::siButtonClicked )
	{
		CustomProperty prop = ctx.GetSource();

		CValue l_ButtonName = ctx.GetAttribute( L"Button" );

		if (l_ButtonName.GetAsText() == L"Export")
		{
			SaveSelections();

			Convert();
			ConvertMaterials();

			if( !g_bMaterialIDError && !g_bFilenameError && !g_bNodenameError )
			{
				CValueArray		args(1);
				CValue			retValue;
				args[0] = g_pApp.GetActiveSceneRoot();
				app.ExecuteCommand(L"CreateExportCrosswalkOptions", args, retValue);

				CustomProperty l_CrosswalkOptions(retValue);

				l_CrosswalkOptions.PutParameterValue ( L"Format", prop.GetParameterValue(L"Format"));
				l_CrosswalkOptions.PutParameterValue ( L"ExportXSINormals", CValue(TRUE));
				l_CrosswalkOptions.PutParameterValue ( L"ExportXSIExtra", CValue(FALSE));
				l_CrosswalkOptions.PutParameterValue ( L"ExportSelectionOnly", CValue(TRUE));
				l_CrosswalkOptions.PutParameterValue( L"PlotInterpolation", CValue(1));
				l_CrosswalkOptions.PutParameterValue( L"PlotAnimation", CValue(TRUE));
				l_CrosswalkOptions.PutParameterValue ( L"Triangulate", prop.GetParameterValue(L"Triangulate"));
				l_CrosswalkOptions.PutParameterValue ( L"Filename", prop.GetParameterValue(L"Filename"));

				args[0] = l_CrosswalkOptions;
				CString fn = prop.GetParameterValue ( L"Filename" );
				strcpy( g_pExportName, " " );
				strcat( g_pExportName, fn.GetAsciiString() );
				app.ExecuteCommand(L"ExportCrosswalk", args, retValue);

				app.ExecuteCommand(L"DeleteObj", args, retValue);
			}

			RestoreMaterials();
			RestoreNodeNames();

			RestoreSelections();

			if( !g_bMaterialIDError && !g_bFilenameError && !g_bNodenameError )
			{
				RunResourceCompiler();
			}
		}
	}
	if( eventID == PPGEventContext::siOnInit )
	{
		CustomProperty prop = ctx.GetSource() ;
		PPGLayout oPPGLayout = prop.GetPPGLayout();		
		PPGItem oPPGItem = oPPGLayout.GetItem(L"Filename");

		LONG chosenFormat = prop.GetParameter(L"format").GetValue();
		if (   ( chosenFormat == DOTXSI_FORMAT_5_0 )
			|| ( chosenFormat == DOTXSI_FORMAT_5_0_BINARY )
			|| ( chosenFormat == DOTXSI_FORMAT_6_0 )
			|| ( chosenFormat == DOTXSI_FORMAT_6_0_BINARY )) {
				oPPGItem.PutAttribute( siUIFileFilter, L"dotXSI format (*.xsi)|*.xsi|COLLADA  (*.dae *.xml)|*.dae:*.xml|All Files (*.*)|*.*||" ) ;
		}
		else {
			oPPGItem.PutAttribute( siUIFileFilter, L"COLLADA  (*.dae *.xml)|*.dae:*.xml|dotXSI format (*.xsi)|*.xsi|All Files (*.*)|*.*||" ) ;	
		}

		//Redraw the PPG to show the new combo items
		ctx.PutAttribute(L"Refresh",true);
	}

	return status;
}
