//	to xsi (for animanium comverter)
//	（…と思ったけど、オリジナルフォーマットになっちゃった）
//	ポリゴンデータとジョイント（ボーン）を出力。
//	また、(リジッド)バインドされているVertex情報も出力。
//
//	Atsushi Morimoto
//	( a.k.a Mayonnaize Production)
//
//	今のところ、Export All 専用。
//	(not suport Export Selection)
//	# プロトタイプということで…。

#include<stdio.h>
#include<stdlib.h>
#include<iostream.h>
#include<fstream.h>
#include<string.h>
#include<math.h>
#include<sys/types.h>
#include<vector>

#include<maya/MStatus.h>
#include<maya/MPxCommand.h>
#include<maya/MPxFileTranslator.h>
#include<maya/MObject.h>
#include<maya/MFnPlugin.h>
#include<maya/MItDag.h>
#include<maya/MDagPath.h>
#include<maya/MFnDagNode.h>
#include<maya/MFnIkJoint.h>
#include<maya/MEulerRotation.h>
#include<maya/MVector.h>
#include<maya/MObjectArray.h>
#include<maya/MDagPathArray.h>
#include<maya/MFnMesh.h>
#include<maya/MPointArray.h>
#include<maya/MItMeshPolygon.h>
#include<maya/MIntArray.h>
#include<maya/MMatrix.h>
#include<maya/MPlugArray.h>
#include<maya/MFnSkinCluster.h>
#include<maya/MFnSet.h>
#include<maya/MSelectionList.h>
#include<maya/MItMeshVertex.h>
#include<maya/MFnSingleIndexedComponent.h>

#if 0
#include"jointOne.h"
#endif

using namespace std;

const	char	const	*version = "$Id: exportMyz.cpp,v 1.2 2001/06/08 09:16:17 jagajin Exp $";

const	double	TK = 0.01;
const	char * const toxsiOptionScript = "xsiExportOptions";
const	char * const toxsiDefaultOptions = "";
const	char * MODELNAME ="Mayonnaize";

//typedef	vector<JointOne> JOINTONE;

void statusCheck(MStatus status);


void statusCheck(MStatus status)
{
	if ( status == MStatus::kSuccess)
		printf("ok\n");
	else
		printf("ng\n");
}

class	toMyz : public MPxFileTranslator {
	public:
				toMyz() {};
		virtual	~toMyz() {};
		static	void*	creator();
		MStatus	writer( const MFileObject& file,
						const MString& optionString,
						FileAccessMode mode);
		MString	defaultExtension() const { return "myz"; };
		MFileKind	identifyFile ( const MFileObject&,
									const char* buffer,
									short size) const;
		bool haveReadMethod() const { return false; };
		bool haveWriteMethod() const { return true; };
	private:
		MStatus	exportAll(const MFileObject& file);
		void	writeAll(const MFileObject& file);

		// Mayonnaize mesod function
		void	outPutPolygon();
		void	outPutJoint();
		void	outPutJointBindPolygon(MFnIkJoint);

		// Mayonnaize mesod data
		MDagPathArray	jointArray;
		MDagPathArray	polyArray;
		FILE	*ofp;
};

void* toMyz::creator()
{
	return new toMyz();
}


//=> 丸写し objExport
MPxFileTranslator::MFileKind toMyz::identifyFile (
                                        const MFileObject& fileName,
                                        const char* buffer,
                                        short size) const
{
    const char * name = fileName.name().asChar();
    int   nameLength = strlen(name);
    
    //if ((nameLength > 4) && !strcasecmp(name+nameLength-4, ".obj"))
    if ((nameLength > 4) && !strcasecmp(name+nameLength-4, ".myz"))
        return kIsMyFileType;
    else
        return kNotMyFileType;
}
//<= 丸写し objExport

MStatus toMyz::writer( const MFileObject& file,
				const MString& optionString,
				FileAccessMode mode)
{
	return exportAll(file);
}

MStatus toMyz::exportAll(const MFileObject& file)
{
	MStatus	status;

	MItDag dagIterator( MItDag::kBreadthFirst, MFn::kInvalid, &status);
	for ( ; !dagIterator.isDone(); dagIterator.next() ) {
		MDagPath dagPath;
		MObject  component = MObject::kNullObj;
		status = dagIterator.getPath(dagPath);
		MFnDagNode dagNode( dagPath, &status );

		if ( dagPath.hasFn(MFn::kJoint) ) {
			jointArray.append(dagPath);
		}	// end of kJoint

		//printf("test");
		if ( dagPath.hasFn(MFn::kSet) ) {
			printf("kSet hit\n");
		}

		if (( dagPath.hasFn(MFn::kMesh)) &&
			( dagPath.hasFn(MFn::kTransform)))
		{
			//printf("check A\n");	// debugging
			polyArray.append(dagPath);
		}
		else if ( dagPath.hasFn(MFn::kMesh)) {
			//printf("check B\n");	// debugging
			continue;	// pass
		}
	}

	writeAll(file);	// if write all mode

	flushall();
	return MStatus::kSuccess;
}

void toMyz::writeAll(const MFileObject& file)
{
	//printf("**** BEGING of writeAll() ****\n");

	ofp = fopen(file.name().asChar(), "w");

	// file header
	fprintf(ofp, "// Mayonnaize Products\n");

	outPutPolygon();
	outPutJoint();

	fclose(ofp);
	//printf("**** END of writeAll() ****\n");
}

void toMyz::outPutJoint()
{
	int n;
	const double K = M_PI / 180.0;	// for radian

	fprintf(ofp, "jointGroup {\n");
	for ( n = 0 ; n < jointArray.length(); n++) {
		MFnIkJoint		fnIkJoint(jointArray[n]);
		MFnDagNode		parent(fnIkJoint.parent(0));
		MVector			tran(fnIkJoint.translation(MSpace::kWorld));
		MEulerRotation	emuOri;
		MEulerRotation	emuRot;
		double			pref[3];

		fnIkJoint.getOrientation(emuOri);
		fnIkJoint.getRotation(emuRot);
		fnIkJoint.getPreferedAngle(pref);

		//fprintf(ofp, "\tjoint num = %d\n", n);	// debugging
		fprintf(ofp, "\tJoint %s < %s { \n", fnIkJoint.name().asChar(),
				parent.name().asChar() );
		fprintf(ofp, "\t\ttranslate = %f, %f, %f\n",
				tran.x * TK, tran.y*TK, tran.z*TK );
		fprintf(ofp, "\t\trotation = %f, %f, %f\n",
				emuRot.x/K, emuRot.y/K, emuRot.z/K
				);
		fprintf(ofp, "\t\tOrientation = %f, %f, %f\n",
				emuOri.x/K, emuOri.y/K, emuOri.z/K);
		fprintf(ofp, "\t\tPreferedAngle = %f, %f, %f\n",
				pref[0]/K, pref[1]/K, pref[2]/K);

		outPutJointBindPolygon(fnIkJoint);

		fprintf(ofp, "\t}\n");
	}

	fprintf(ofp, "} // jointGroup\n");
}

void toMyz::outPutJointBindPolygon(MFnIkJoint fnIkJoint)
{
	MPlug		wmPlug;
	MStatus		status;

	wmPlug = fnIkJoint.findPlug("worldMatrix", &status);
	if ( status == MStatus::kSuccess) {
		MPlugArray	toConnectArray;
		MPlugArray	toConnectArray2;

		if ( wmPlug[0].connectedTo(toConnectArray, 0, 1) ) {
			int pNum;

			for (pNum = 0; pNum < toConnectArray.length(); pNum++) {
				MObject	jcNode;

				jcNode = toConnectArray[pNum].node();
				if ( jcNode.hasFn(MFn::kJointCluster) ) {
					MPlug		jcMegPlug;
					MFnDependencyNode	fnSCluster(jcNode, &status);

					jcMegPlug = fnSCluster.findPlug("message", &status);
					if ( status != MStatus::kSuccess) {
						// not Success
						continue;
					}
					if ( ! jcMegPlug.connectedTo(toConnectArray2, 0, 1) ) {
						fprintf(stderr, "errer: skinBindSet connect not find\n");
						continue;
					}
					int	nset;
					for ( nset = 0; nset < toConnectArray2.length(); nset++) {
						MObject	setNode(toConnectArray2[nset].node());

						if (!setNode.hasFn(MFn::kSet)) {
							continue;
						}	// ここを抜ければset。
						int				lnum = 0;
						MFnSet			fnSet(setNode);
						MSelectionList	setMemberList;
						MDagPath		lDagPath;
						MObject			comp;
						MString			polyName;

						// 第二引数のflatten flugが謎…。
						fprintf(ofp, "\t\tBindPolygon { \n");
						fnSet.getMembers(setMemberList, 0);
						for (lnum = 0; lnum < setMemberList.length(); lnum++ ) {
							setMemberList.getDagPath(lnum, lDagPath, comp);
							polyName = MFnDagNode(lDagPath.node()).name();
							fprintf(ofp, "\t\t\tpolyShape  %s {\n", polyName.asChar());

							MFnSingleIndexedComponent	fnSIComp(comp, &status);
							MIntArray	sElement;
							int	eCount;

							fnSIComp.getElements(sElement);
							for ( eCount = 0; eCount < sElement.length(); eCount++) {
								fprintf(ofp, "\t\t\t\t%d\n", sElement[eCount]);
							}

							fprintf(ofp, "\t\t\t}\n");
						}
						fprintf(ofp, "\t\t} // BindPolygon\n");
					}
				}
			}	// for(pNum ... )
		}
	}
}

void toMyz::outPutPolygon()
{
	int	n;
	MObject	transObj;

	fprintf(ofp, "Poly_Chunk {\n");
	for ( n = 0; n < polyArray.length(); n++) {
		MFnMesh		fnMesh(polyArray[n]);
		long		vertNum;
		long		polyNum;
		MMatrix		transMatrix;

		transObj = polyArray[n].transform();
		// get transform matrix
		if (fnMesh.parentCount() > 1) {	
			fprintf(stderr, "polygon object with instance copy\n");
		} else {	// only 1 transform (normal)
			transObj = fnMesh.parent(0);
			if ( transObj.hasFn(MFn::kTransform) ) {
				MFnTransform	fnTrans(transObj);
				transMatrix = fnTrans.transformationMatrix();
			} else {
				fprintf(stderr, "not translate ? \n");
				transMatrix.setToIdentity();
			}
		}

		vertNum = fnMesh.numVertices();
		polyNum = fnMesh.numPolygons();
		fprintf(ofp, "\tpolygonShape %s {\n", fnMesh.name().asChar());
		//fprintf(stdout, "find polygon number = %d\n", n);	// debugging
		fprintf(ofp, "\t\tvertNum = %d\n", vertNum);
		fprintf(ofp, "\t\tpolyNum = %d\n", polyNum);

		int m;
		MPointArray		pntArray;
		MPoint			pnt;

		fnMesh.getPoints(pntArray);
		fprintf(ofp, "\t\tvertices {\n");
		for ( m = 0; m < pntArray.length(); m++) {
			pnt = pntArray[m];
			pnt *= transMatrix;
			fprintf(ofp, "\t\t\t%f,%f,%f\n", pnt.x*TK, pnt.y*TK, pnt.z*TK);
		}
		fprintf(ofp, "\t\t}\n");	// end vertices

		MItMeshPolygon	meshIter(polyArray[n], MObject::kNullObj);
		MIntArray		faceIndexArray;
		fprintf(ofp, "\t\tFace {\n");
		for ( m = 0; !meshIter.isDone(); meshIter.next() ) {
			meshIter.getVertices(faceIndexArray);
			int o;
			fprintf(ofp, "\t\t\t");
			for ( o = 0; o < faceIndexArray.length(); o++) {
				fprintf(ofp, "%d ", faceIndexArray[o]);
			}
			fprintf(ofp, "\n");
		}
		fprintf(ofp, "\t\t}\n");
		fprintf(ofp, "\t}\n");
	}
	fprintf(ofp, "} // Poly_Chunk\n");

}


//////////////////////////////////////////////////////////////
MStatus initializePlugin( MObject obj )
{
    MFnPlugin plugin( obj, "Mayannaze Products", "0.0", "Any");

    // Register the translator with the system
    return plugin.registerFileTranslator( "toMyz", "none",
                                          toMyz::creator,
                                          (char *)toxsiOptionScript,
                                          (char *)toxsiDefaultOptions );                                        
}

MStatus uninitializePlugin( MObject obj )
{
        MFnPlugin plugin( obj );
        return plugin.deregisterFileTranslator( "toMyz" );
}

