
#include "stdafx.h"

#ifdef STRICT							// Already defined by stdafx,
#undef STRICT							// so we avoid warning linking msg
#endif
#ifdef _MBCS							// The same as above
#undef _MBCS
#endif

#include <GameOS\GameOS.hpp>

#if !defined(DLLPLATFORM_DLLPLATFORM_HPP)
	#include "DLLPlatform\DLLPlatform.hpp"
#endif

#if !defined(STUFF_STUFF_HPP)
	#include "Stuff\Stuff.hpp"
#endif

#if !defined(MW4ANIMATIONSYSTEM_HPP)
	#include <MW4\MW4AnimationSystem.hpp>
#endif

#undef Min
#undef Max


#pragma pack(push,8)
#include "Max.h"						// Include MAX's main header file
#pragma pack(pop)

#include <Stuff\StuffHeaders.hpp>
#include "MaxProxies\MaxProxyHeaders.hpp"
#include <adept\collisionvolume.hpp>

#include "AnimationSuite.h"
#include "About.h"
#include "JointEditDialog.h"

#include "ArmatureExport.h"
#include "siteobject.h"
#include "cvobject.h"
#include "AnimationExport.h"
#include "ErfExport.h"
#include "MLRExport.h"
#include "EBFExport.h"
#include "BatchExport.h"
#include "CvExport.h"
#include "lightexport.h"
#include "erfimport.h"
#include "missionimp.h"


#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define DLLEXPORT __declspec(dllexport)	// Facilite Function's Exportation Declaration


CAnimExpApp	theApp;					// Global Object representing the application
AnimExpClassDesc animExpDesc;

JointPreferenceClassDesc jointPreferenceDesc;
OptimizeKeyframeClassDesc optimizeKeyframeDesc;
ReplicateClassDesc		 replicateExpDesc;
SiteObjectClassDesc		 siteObjectClassDesc;
LegMirrorClassDesc		legmirrorDesc;
HillAdjustClassDesc		hillAdjustDesc;
ErfExpClassDesc			 erfExpDesc;
MLRExpClassDesc			 MLRExpDesc;
EBFExpClassDesc			 EBFExpDesc;
BatchExpClassDesc		 batchExpDesc;
ArmatureExpClassDesc     armatureExpDesc;
FallToolClassDesc		fallToolDesc;
SkeletonVerifyClassDesc	skeletonVerifyDesc;
CvObjectClassDesc		 cvObjectClassDesc;
CvExpClassDesc			 cvExpDesc;
CvToolClassDesc			 cvToolDesc;
LightExpClassDesc		 lightExpDesc;
ErfImpClassDesc			 erfImpDesc;
MissionImpClassDesc		 missionImpDesc;
//HINSTANCE	hInstance;

/////////////////////////////////////////////////////////////////////////////
// Prototypes
static BOOL CALLBACK QuickFileProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);


/////////////////////////////////////////////////////////////////////////////
// CAnimExpApp

BEGIN_MESSAGE_MAP(CAnimExpApp, CWinApp)
	//{{AFX_MSG_MAP(CAnimExpApp)
		// NOTE - the ClassWizard will add and remove mapping macros here.
		//    DO NOT EDIT what you see in these blocks of generated code!
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CAnimExpApp construction

void TurnOnExceptions()
{
	gos_MathExceptions( 1, 1 );
}

void TurnOffExceptions()
{
	gos_MathExceptions( 0, 0 );
}


CAnimExpApp::CAnimExpApp()
{
}

HGOSHEAP AnimSuiteHeap;

/////////////////////////////////////////////////////////////////////////////
// DLL's InitInstance, called when the DLL is being linked to a client
BOOL CAnimExpApp::InitInstance() 
{
	TurnOnExceptions();
	//_clear87();

	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	//Get the DLL's HINSTANCE
	hInstance = AfxGetResourceHandle();

	//Init 3DS Max Custom Controls
	InitCustomControls(hInstance);

	//Init Win32 Controls
	InitCommonControls();

	InitGameOS( hInstance, NULL, "\0" );


	Stuff::ArmorLevel = 4;


	Stuff::InitializeClasses();
	Proxies::InitializeClasses();
	MAXProxies::InitializeClasses();
	MidLevelRenderer::InitializeClasses();
	gosFX::InitializeClasses();
	ElementRenderer::InitializeClasses();
	ElementProxies::InitializeClasses();
	
//	Initialize_MAXStuff();

	TurnOffExceptions();

	AnimSuiteHeap = gos_CreateMemoryHeap("AnimSuiteHeap");
	Check_Pointer(AnimSuiteHeap);
	gos_PushCurrentHeap(AnimSuiteHeap);


	return TRUE;				//CWinApp::InitInstance();
}

/////////////////////////////////////////////////////////////////////////////
// DLL's ExitInstance, called when the DLL is being unlinked from a client
int CAnimExpApp::ExitInstance() 
{
	gos_PopCurrentHeap();

	TurnOnExceptions();

	//_clear87();
	
	ElementRenderer::TerminateClasses();
	gosFX::TerminateClasses();
	MidLevelRenderer::TerminateClasses();
	ElementProxies::TerminateClasses();
	MAXProxies::TerminateClasses();
	Proxies::TerminateClasses();
	Stuff::TerminateClasses();

	TurnOffExceptions();

	ExitGameOS();

	return CWinApp::ExitInstance();
}




/////////////////////////////////////////////////////////////////////////////
// 3DS Max Identification Callbacks
DLLEXPORT const TCHAR* LibDescription ()
{ 
	return GetString(IDS_LIBDESCRIPTION);
}

/////////////////////////////////////////////////////////////////////////////
// Return the Number of Classes present in the DLLs
DLLEXPORT int LibNumberClasses ()
{ 
	// Return the number of plugins in the DLL
	return 20;					
}

/////////////////////////////////////////////////////////////////////////////
// Return the i'th Class Description Object
DLLEXPORT ClassDesc* LibClassDesc(int i)
{
	switch(i) {
    case  0:
		return &animExpDesc;
	case 1:
		return &jointPreferenceDesc;
	case 2:
		return &optimizeKeyframeDesc;
	case 3:
		return &replicateExpDesc;
	case 4:
		return &siteObjectClassDesc;
	case 5:
 		return &legmirrorDesc;
	case 6:
		return &hillAdjustDesc;
	case 7:
		return &erfExpDesc;
	case 8:
		return &batchExpDesc;
	case 9:
		return &armatureExpDesc;
	case 10:
		return &fallToolDesc;
	case 11:
		return &skeletonVerifyDesc;
	case 12:
		return &cvObjectClassDesc;
	case 13:
		return &cvExpDesc;
	case 14:
		return &cvToolDesc;
	case 15:
		return &MLRExpDesc;
	case 16:
		return &EBFExpDesc;
	case 17:
		return &lightExpDesc;
	case 18:
		return &erfImpDesc;
	case 19:
		return &missionImpDesc;
	default:
		return 0;
        break;
	}
}


/////////////////////////////////////////////////////////////////////////////

void *SiteObjectClassDesc::Create(BOOL loading)	
{
	return new SiteObject();
}

/////////////////////////////////////////////////////////////////////////////

void *AnimExpClassDesc::Create(BOOL loading)	
{
	return new AnimExp();
}

/////////////////////////////////////////////////////////////////////////////

void *ErfExpClassDesc::Create(BOOL loading)	
{
	return new ErfExp();
}

/////////////////////////////////////////////////////////////////////////////

void *MLRExpClassDesc::Create(BOOL loading)	
{
	return new MLRExp();
}

/////////////////////////////////////////////////////////////////////////////

void *EBFExpClassDesc::Create(BOOL loading)	
{
	return new EBFExp();
}

/////////////////////////////////////////////////////////////////////////////

void *ArmatureExpClassDesc::Create(BOOL loading)	
{
	return new ArmatureExp();
}

/////////////////////////////////////////////////////////////////////////////

void *BatchExpClassDesc::Create(BOOL loading)	
{
	return new BatchExp();
}
/////////////////////////////////////////////////////////////////////////////

void *CvObjectClassDesc::Create(BOOL loading)	
{
	return new CvObject();
}

void *CvExpClassDesc::Create(BOOL loading)	
{
	return new CvExp();
}

void *LightExpClassDesc::Create(BOOL loading)	
{
	return new LightExp();
}

void *ErfImpClassDesc::Create(BOOL loading)	
{
	return new ErfImp();
}

void *MissionImpClassDesc::Create(BOOL loading)	
{
	return new MissionImp();
}

/////////////////////////////////////////////////////////////////////////////

TCHAR *GetString(int id)
{
	static TCHAR buf[256];

	if (hInstance)
		return LoadString(hInstance, id, buf, sizeof(buf)) ? buf : NULL;

	return NULL;
}


/////////////////////////////////////////////////////////////////////////////
// MAX API Version
DLLEXPORT ULONG LibVersion ()
{ 
	return (VERSION_3DSMAX);
}

//===========================================================================//

DWORD WINAPI fn(LPVOID arg) {
	return(0);
}


//===========================================================================//

void __stdcall GetGameOSEnvironment(char* CommandLine)
{
		//
	//----------------------------------------------------------
	// This is where to set GOS environment settings.
	//----------------------------------------------------------
	//
	Environment.applicationName			= "AnimationSuite";
	Environment.screenWidth				= 640;
	Environment.screenHeight			= 480;
	Environment.bitDepth				= 16;
}

HWND EnableQuickFile(Interface *ip,HWND hWnd)
{
	//Display the plugin's Rollup Dialog
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	HINSTANCE loc_hInstance = AfxGetResourceHandle ();

	HWND hRet = ip->AddRollupPage(loc_hInstance, MAKEINTRESOURCE(IDD_QUICKFILE_PANEL),
		QuickFileProc, _T("Quick File"),(LPARAM)hWnd);
	

	char szKey[100] = "SOFTWARE\\Autodesk\\3D Studio MAX\\Plugins\\QuickFile";
	HKEY hKey;
	if (ERROR_SUCCESS == RegOpenKey(HKEY_LOCAL_MACHINE,szKey,&hKey))
	{
		DWORD wType = REG_SZ;
		char szValue[100];
		BYTE *pData = (BYTE *)szValue;
		DWORD size = 100;
		RegQueryValueEx(hKey,"Enabled",0,&wType,(BYTE *)szValue,&size);
		
		// Disable the drop down 
		if ('0' == szValue[0])	
		{
			IRollupWindow *pRoll = ip->GetCommandPanelRollup();
			pRoll->SetPanelOpen(pRoll->GetNumPanels()-1,false);
		}
		size = 100;
		RegQueryValueEx(hKey,"NumFiles",0,&wType,(BYTE *)szValue,&size);

		int maxfiles;
		sscanf(szValue,"%d",&maxfiles);
		for (int x=0;x<maxfiles;x++)
		{
			char szKey[100];
			size = 100;
			sprintf(szKey,"%d",x);
			RegQueryValueEx(hKey,szKey,0,&wType,(BYTE *)szValue,&size);

			char fname[20];
			_splitpath((LPCTSTR)szValue,NULL,NULL,fname,NULL);
			if (LB_ERR == SendDlgItemMessage(hRet,IDC_QUICKFILE_LISTBOX,LB_FINDSTRINGEXACT,0,(LPARAM)fname))
			{
				char *psz = strdup((LPCTSTR)szValue);
				int index = SendDlgItemMessage(hRet,IDC_QUICKFILE_LISTBOX,LB_ADDSTRING,0,(LPARAM)fname);
				SendDlgItemMessage(hRet,IDC_QUICKFILE_LISTBOX,LB_SETITEMDATA,index,(LPARAM)psz);
			}
		}
	}
	else
	{
		IRollupWindow *pRoll = ip->GetCommandPanelRollup();
		pRoll->SetPanelOpen(pRoll->GetNumPanels()-1,false);
	}
	return hRet;
}

void DisableQuickFile(Interface * ip,HWND hWnd)
{
	char szKey[100] = "SOFTWARE\\Autodesk\\3D Studio MAX\\Plugins\\QuickFile";
	HKEY hKey;
	RegCreateKey(HKEY_LOCAL_MACHINE,szKey,&hKey);

	IRollupWindow *pRoll = ip->GetCommandPanelRollup();
	char *strOpen = pRoll->IsPanelOpen(pRoll->GetNumPanels()-1)?"1":"0";

	RegSetValueEx(hKey,"Enabled",0,REG_SZ,(BYTE *)strOpen,strlen(strOpen));

	char szValue[MAX_PATH];
	int count = SendDlgItemMessage(hWnd,IDC_QUICKFILE_LISTBOX,LB_GETCOUNT,0,0);
	sprintf(szValue,"%d",count);
	RegSetValueEx(hKey,"NumFiles",0,REG_SZ,(BYTE *)szValue,strlen(szValue));

	
	for (int x = 0;x<count;x++)
	{
		//SendDlgItemMessage(hWnd,IDC_QUICKFILE_LISTBOX,LB_GETTEXT,x,(LPARAM)buf);
		LPARAM lpPath = SendDlgItemMessage(hWnd,IDC_QUICKFILE_LISTBOX,LB_GETITEMDATA,x,0);
		char *pPath = (char *)lpPath;
		char szKey[100];
		sprintf(szKey,"%d",x);
		RegSetValueEx(hKey,szKey,0,REG_SZ,(BYTE *)pPath,strlen(pPath));
	}


	ip->DeleteRollupPage(hWnd);
}

static BOOL CALLBACK QuickFileProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	//static HWND hotherWnd = NULL;
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	switch (msg)
	{
	case WM_INITDIALOG:
		{
			//hotherWnd = (HWND)lParam;
			SetWindowLong(GetDlgItem(hWnd,IDC_QUICKFILE_APPLY),GWL_USERDATA,lParam);
			break;
		}
	case WM_DESTROY:
		{
			//hotherWnd = NULL;
			break;
		}
	case WM_COMMAND:
		{
			switch (LOWORD(wParam)) 		//Handle Panel's controls
			{
			case IDC_QUICKFILE_LISTBOX:
				{
					switch (HIWORD(wParam))
					{
					case LBN_SELCHANGE:
						{
							HWND hListBox = (HWND)lParam;
							HWND hTarget = (HWND)GetWindowLong(GetDlgItem(hWnd,IDC_QUICKFILE_APPLY),GWL_USERDATA);
							SendMessage(hTarget,msg,wParam,lParam);
							break;
						}
					}
					break;
				}
			case IDC_QUICKFILE_ADD:
				{
					HWND hTarget = (HWND)GetWindowLong(GetDlgItem(hWnd,IDC_QUICKFILE_APPLY),GWL_USERDATA);
					CFileDialog fd(TRUE,NULL,NULL,OFN_ALLOWMULTISELECT,"Max Files (*.max)|*.max||",CWnd::FromHandle(hTarget));
					char *filebuf = new char[MAX_PATH*200];
					filebuf[0] = 0;
					fd.m_ofn.lpstrFile = filebuf;
					fd.m_ofn.nMaxFile = MAX_PATH*200;
					if (fd.DoModal()==IDOK)
					{
						POSITION pos = fd.GetStartPosition();
						while (pos)
						{
							CString path = fd.GetNextPathName(pos);
							char fname[20];
							_splitpath((LPCTSTR)path,NULL,NULL,fname,NULL);
							if (LB_ERR == SendDlgItemMessage(hWnd,IDC_QUICKFILE_LISTBOX,LB_FINDSTRINGEXACT,0,(LPARAM)fname))
							{
								char *psz = strdup((LPCTSTR)path);
								int index = SendDlgItemMessage(hWnd,IDC_QUICKFILE_LISTBOX,LB_ADDSTRING,0,(LPARAM)fname);
								SendDlgItemMessage(hWnd,IDC_QUICKFILE_LISTBOX,LB_SETITEMDATA,index,(LPARAM)psz);
							}
						}
					}
					break;
				}
			case IDC_QUICKFILE_REMOVE:
				{
					int count = SendDlgItemMessage(hWnd,IDC_QUICKFILE_LISTBOX,LB_GETSELCOUNT,0,0);
					int *items = new int[count];
					int icount = SendDlgItemMessage(hWnd,IDC_QUICKFILE_LISTBOX,LB_GETSELITEMS,count,(LPARAM)items);
					// Start from highest index and count down because indices may change from deletion
					for (;icount;icount--)
					{
						char *psz = (char *)SendDlgItemMessage(hWnd,IDC_QUICKFILE_LISTBOX,LB_GETITEMDATA,icount-1,0);
						if (psz)
							free(psz);
						SendDlgItemMessage(hWnd,IDC_QUICKFILE_LISTBOX,LB_DELETESTRING,items[icount],0);
					}
					delete items;
					break;
				}
			case IDC_QUICKFILE_APPLY:
				{
					HWND hTarget = (HWND)GetWindowLong(GetDlgItem(hWnd,IDC_QUICKFILE_APPLY),GWL_USERDATA);
	
					SendMessage(hTarget,WM_QUICKFILE_BEGIN,0,0);
					int count = SendDlgItemMessage(hWnd,IDC_QUICKFILE_LISTBOX,LB_GETCOUNT,0,0);
					char buf[100];
					for (;count;count--)
					{
						SendDlgItemMessage(hWnd,IDC_QUICKFILE_LISTBOX,LB_GETTEXT,count-1,(LPARAM)buf);
						LPARAM lpPath = SendDlgItemMessage(hWnd,IDC_QUICKFILE_LISTBOX,LB_GETITEMDATA,count-1,0);
						SendMessage(hTarget,WM_QUICKFILE_APPLYFILE,(WPARAM)buf,lpPath);
					}
					SendMessage(hTarget,WM_QUICKFILE_END,0,0);
					break;
				}
			}
			break;
		}
	default:
		{
			return FALSE;
		}

	}
	return TRUE;
}


#define NUM_STAT_UPDATES	20
static float ErrorAmount(Point3 v1, Point3 v2)
	{
	return LengthSquared(v1-v2);
	}

static float ErrorAmount(float v1, float v2)
	{
	float res = v1-v2;
	return res*res;
	}

static float ErrorAmount(Quat v1, Quat v2)
	{	
	AngAxis res(v2/v1);
	return res.angle * res.angle;
	}

static float ErrorAmount(ScaleValue v1, ScaleValue v2)
	{
	return LengthSquared(v1.s-v2.s);
	}

template <class T> int ReduceKeys_MW4(
		Control *cont,Interval range,float thresh,TimeValue step,
		KeyReduceStatus *status, T xx)
	{
	Interval valid;
	Tab<TimeValue> times;
	int i,j,offset = cont->GetKeyTimes(times,range,0);
	TimeValue t;
	int statUp, res;
	//if (times.Count() <3) return KEYREDUCE_CONTINUE;
	if (!times.Count()) return KEYREDUCE_CONTINUE;

	// Init the status callback if we got one.
	if (status) status->Init(times.Count()-1);

	// The number of keys to process inbetween each update
	statUp = times.Count()/NUM_STAT_UPDATES;
	if (statUp==0) statUp = 1;

	// Square the threshold.
	thresh = thresh*thresh;

	// Build a table of the origanal values
	Tab<T> vals;
	vals.SetCount((range.End()-range.Start())/step + 1);
	for (t=range.Start(),i=0; t<range.End(); t+=step,i++) {
		cont->GetValue(t,&vals[i],valid);
		}

	// Start at the last key and see if each key can be deleted.
	for (i = times.Count()-1; i>=0; i--) {		
		//theHold.Begin();
		Control *old_cont = (Control *)cont->Clone();
		
		// Delete the key..
		cont->DeleteKeyByIndex(i+offset);

		// Check to see if the path is still within threshold.
		BOOL ok = TRUE;
		for (t=range.Start(),j=0; t<range.End(); t+=step,j++) {		
			T val;
			cont->GetValue(t,&val,valid);
			if (ErrorAmount(val,vals[j]) > thresh) {
				ok = FALSE;				
				break;
				}
			}

		// If it was ok to delete this key, great! otherwise put it back
if (ok) {
			//theHold.Accept(0);

		} else {
			//theHold.Cancel();
			cont->Copy(old_cont);
			}
		delete old_cont;

		// Maybe update the status
		if (status && !(i%statUp)) {
			switch (res = status->Progress(times.Count()-1-i)) {
				case KEYREDUCE_ABORT:				
				case KEYREDUCE_STOP:
					return res;
				}
			}
		}
	return TRUE;
	}



int ApplyKeyReduction_MW4(
		Control *cont,Interval range,float thresh,TimeValue step,
		KeyReduceStatus *status)
	{		
	if (!cont->NumKeys()) return TRUE;
	int res = KEYREDUCE_CONTINUE;	

	switch (cont->SuperClassID()) {
		case CTRL_FLOAT_CLASS_ID:
			res = ReduceKeys_MW4(cont,range,thresh,step,status,float(0));
			break;

		case CTRL_POSITION_CLASS_ID:
		case CTRL_POINT3_CLASS_ID:
			res = ReduceKeys_MW4(cont,range,thresh,step,status,Point3(0,0,0));
			break;

		case CTRL_ROTATION_CLASS_ID:
			res = ReduceKeys_MW4(cont,range,thresh,step,status,Quat(0.0f,0.0f,0.0f,1.0f));
			break;

		case CTRL_SCALE_CLASS_ID:
			res = ReduceKeys_MW4(cont,range,thresh,step,status,ScaleValue(Point3(1,1,1)));
			break;
		}
		
	return res;
	}


