//-----------------------------------------------------------------------------
// Torque Game Engine 
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------

#ifndef SHAPEMIMIC_H_
#define SHAPEMIMIC_H_

#pragma pack(push,8)
#include <max.h>
#include <stdmat.h>
#include <decomp.h>
#pragma pack(pop)

#ifndef _TSSHAPE_H_
#include "ts/tsShape.h"
#endif
#ifndef _TVECTOR_H_
#include "core/tVector.h"
#endif
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif

struct ExporterSequenceData;

struct SkinMimic
{
   typedef Vector<F32> WeightList;

   INode * skinNode;
   TSSkinMesh * skinMesh;
   S32 detailSize;
   S32 skinNum;
   S32 meshNum;

   F32 multiResPercent;

   Vector<TSDrawPrimitive> faces;
   Vector<Point3> verts;
   Vector<Point3F> normals;
   Vector<Point3> tverts;
   Vector<U16> indices;
   Vector<U16> mergeIndices;
   Vector<U32> smoothingGroups;
   Vector<U32> vertId;

   Vector<INode *> bones;
   Vector<WeightList*> weights;
   
   ~SkinMimic() { for (S32 i=0;i<weights.size(); i++) delete weights[i]; }
};

struct IflMimic
{
   char * fileName;
   char * fullFileName;
   S32 materialSlot;
   ~IflMimic() { dFree(fileName); dFree(fullFileName); }
};

struct MeshMimic
{
   INode * pNode;
   TSMesh * tsMesh;
   SkinMimic * skinMimic;
   bool billboard; // i.e., face camera
   bool sortedObject;
   S32 numVerts; // number of unique vertices
   S32 meshNum;
   Vector<U32> smoothingGroups;
   Vector<U32> remap;
   Vector<U32> vertId;

   Matrix3 objectOffset; // NOTE: not valid till late in the game

   F32 multiResPercent;

   MeshMimic(INode * meshNode) { pNode = meshNode; skinMimic = NULL; tsMesh = NULL; }
};

struct MultiResMimic
{
   INode * pNode;
   INode * multiResNode;
   S32 totalVerts;
   // record off order of vertex merging (we'll figure out tverts later)
   Vector<Point3F> mergeFrom;
   Vector<Point3F> mergeTo;
   MultiResMimic(INode * pn, INode * mrn) { pNode = pn; multiResNode = mrn; }
};

struct ObjectMimic
{
   enum { MaxDetails=20 };
   struct Detail
   {
      S32 size;
      MeshMimic * mesh;
   };

   // my object has a name...I'm Jimmy.
   const char * name;
   const char * fullName; // name of object in tree

   // each object has several meshes attached
   S32 numDetails;
   Detail details[MaxDetails];

   // we'll check the object against this list in the end        
   Vector<S32> * pValidDetails;
   INode * inTreeNode; // this is the node that sits in the shape's node hierrarchy

   // The next two items are used for sorting objects
   // objects are sorted by subTreeNum first, and then
   // priority (smallest to highest in both cases).
   S32 subtreeNum;
   U32 priority;

   // all verts in the meshes get multiplied by this matrix
   // before being added to ts shape
   Matrix3 objectOffset;

   // The max node this object hangs on (i.e., the one in the shape not
   // the loose objects that make up the detail levels).
   INode * maxParent;
   // Similar to above: the max node that corresponds to tsNode that will
   // be our parent.  Starts out the same as maxParent, but is revised
   // as we prune unwanted nodes from the tree structure.
   INode * maxTSParent;

   // ts node index we hang off
   S32 tsNodeIndex;
   S32 tsObjectIndex;

   // This is the eventual payoff
   TSObject * tsObject;
    
   //
   bool isBone;
   bool isSkin;

   ~ObjectMimic()
   {
      dFree((char*)name);
      dFree((char*)fullName);
      for (S32 i=0;i<numDetails;i++)
         delete details[i].mesh;
   }
};

struct DecalMeshMimic
{
   MeshMimic * targetMesh;
   TSDecalMesh * tsMesh;

   DecalMeshMimic(MeshMimic * target) { targetMesh = target; tsMesh = NULL; }
};

struct DecalObjectMimic
{
   enum { MaxDetails=20 };
   struct Detail
   {
      DecalMeshMimic * decalMesh;
   };

   // each object has several meshes attached
   S32 numDetails;
   Detail details[MaxDetails];

   // The next two items are used for sorting decal objects.
   // See ObjectMimic for more info.
   S32 subtreeNum;
   U32 priority;

   // The object used to map the decal onto the object
   INode * decalNode;

   // This is the object we decal
   ObjectMimic * targetObject;

   // This is the eventual payoff
   TSDecal * tsDecal;
    
   ~DecalObjectMimic()
   {
      for (S32 i=0;i<numDetails;i++)
         delete details[i].decalMesh;
   }
};

struct NodeMimic
{
   // our twin in the max world:
   INode * maxNode;
    
   // our neighbors in the mimic world:
   NodeMimic * parent;
   NodeMimic * child;
   NodeMimic * sibling;

   // transforms at default time
   AffineParts child0;
   AffineParts parent0;

   // index of our ts version
   S32 number;
    
   // our mimic object
   Vector<ObjectMimic*> objects;
};

struct CropInfo
{
   bool hasTVerts;
   Matrix3 uvTransform;

   bool uWrap;
   bool vWrap;

   bool crop;
   bool place;

   F32 uOffset;
   F32 vOffset;
   F32 uWidth;
   F32 vHeight;

   // only valid if crop==true
   bool cropLeft;
   bool cropRight;
   bool cropTop;
   bool cropBottom;

   bool twoSided;
};

class ShapeMimic
{
   friend class SceneEnumProc;

   struct Subtree
   {
      Vector<S32> validDetails;
      Vector<const char*> detailNames;
      Vector<INode*> detailNodes;
      NodeMimic start;
   };

   Vector<Subtree*> subtrees;
   Vector<ObjectMimic*> objectList;
   Vector<DecalObjectMimic*> decalObjectList;
   Vector<SkinMimic*> skins;
   Vector<MultiResMimic*> multiResList;
   Vector<IflMimic*> iflList;
   Vector<INode*> sequences;
   Vector<char*> materials;
   Vector<U32> materialFlags;
   Vector<U32> materialReflectionMaps;
   Vector<U32> materialBumpMaps;
   Vector<U32> materialDetailMaps;
   Vector<F32> materialDetailScales;
   Vector<F32> materialEmapAmounts;
   INode * boundsNode;

   // this gets filled in late in the game
   // it holds the nodes that actually go into the shape
   // in the order they appear in the shape
   Vector<NodeMimic*> nodes;
    
   // error control
   char * errorStr;
   void setExportError(const char * str);

   // called by generateShape
   void generateBounds(TSShape * pShape);
   void generateDetails(TSShape * pShape);
   void generateSubtrees(TSShape * pShape);
   void generateObjects(TSShape * pShape);
   void generateDecals(TSShape * pShape);
   void generateDefaultStates(TSShape * pShape);
   void generateIflMaterials(TSShape * pShape);
   void generateSequences(TSShape * pShape);
   void generateMaterialList(TSShape * pShape);
   void generateSkins(TSShape * pShape);
   void optimizeMeshes(TSShape * pShape);
   void convertSortObjects(TSShape * pShape);

   void generateObjectState(ObjectMimic*,S32 time,TSShape *, bool addFrame, bool addMatFrame);
   void generateDecalState(DecalObjectMimic*,S32 time,TSShape *, bool multipleFrames);
   void generateFrame(ObjectMimic*,S32 time, bool addFrame, bool addMatFrame);
   void generateFaces(INode * meshNode, Mesh & maxMesh, Matrix3 & objectOffset,
                      Vector<TSDrawPrimitive> & faces, Vector<Point3F> & normals,
                      Vector<Point3> & verts, Vector<Point3> & tverts, Vector<U16> & indices,
                      Vector<U32> & smooth, Vector<U32> * vertId = NULL);
   S32 getMultiResVerts(INode * meshNode, F32 multiResPercent);
   void generateNodeTransform(NodeMimic*,S32 time,TSShape *, bool blend, S32 blendReferenceTime, Quat16 & rot, Point3F & trans, Quat16 & srot, Point3F & scale);

   void addNodeRotation(NodeMimic*,S32 time,TSShape *, bool blend, Quat16 & rot, bool defaultVal=false);
   void addNodeTranslation(NodeMimic*,S32 time,TSShape *, bool blend, Point3F & trans, bool defaultVal=false);
   void addNodeUniformScale(NodeMimic*,S32 time,TSShape *, bool blend, F32 scale);
   void addNodeAlignedScale(NodeMimic*,S32 time,TSShape *, bool blend, Point3F & scale);
   void addNodeArbitraryScale(NodeMimic*,S32 time,TSShape *, bool blend, Quat16 & srot, Point3F & scale);

   void computeNormals(Vector<TSDrawPrimitive> &, Vector<U16> & indices, Vector<Point3F> & verts, Vector<Point3F> & norms, Vector<U32> & smooth, S32 vertsPerFrame, S32 numFrames, Vector<U16> * mergeIndices);
   void computeNormals(Vector<TSDrawPrimitive> &, Vector<U16> & indices, Vector<Point3> & verts, Vector<Point3F> & norms, Vector<U32> & smooth, S32 vertsPerFrame, S32 numFrames);
   
   void generateNodeAnimation(TSShape * pShape, Interval & range, ExporterSequenceData &);
   void generateObjectAnimation(TSShape * pShape, Interval & range, ExporterSequenceData &);
   void generateDecalAnimation(TSShape * pShape, Interval & range, ExporterSequenceData &);
   void generateGroundAnimation(TSShape * pShape, Interval & range, ExporterSequenceData &);
   void generateFrameTriggers(TSShape *, Interval &, ExporterSequenceData &, IParamBlock *);
   void addTriggersInOrder(IKeyControl *, F32 duration, TSShape *, U32 & offTriggers);

   void setNodeMembership(TSShape *, Interval & range, ExporterSequenceData &, S32 &, S32 &, S32 &, S32 &, S32 &);
   S32 setObjectMembership(TSShape::Sequence &, Interval & range, ExporterSequenceData &);
   S32 setDecalMembership(TSShape::Sequence &, Interval & range, ExporterSequenceData &);
   S32 setIflMembership(TSShape::Sequence &, Interval & range, ExporterSequenceData &);

   S32 setRotationMembership(TSShape *, TSSequence &, ExporterSequenceData &, S32 numFrames);
   S32 setTranslationMembership(TSShape *, TSSequence &, ExporterSequenceData &, S32 numFrames);
   S32 setUniformScaleMembership(TSShape *, TSSequence &, ExporterSequenceData &, S32 numFrames);
   S32 setAlignedScaleMembership(TSShape *, TSSequence &, ExporterSequenceData &, S32 numFrames);
   S32 setArbitraryScaleMembership(TSShape *, TSSequence &, ExporterSequenceData &, S32 numFrames);

   bool animatesAlignedScale(ExporterSequenceData &, S32 numFrames);
   bool animatesArbitraryScale(ExporterSequenceData &, S32 numFrames);

   void setObjectPriorities(Vector<ObjectMimic*> &);
   void setDecalObjectPriorities(Vector<DecalObjectMimic*> &);

   bool testCutNodes(Interval & range, ExporterSequenceData &);
   
   // methods used to optimize meshes
   void collapseVertices(TSMesh*, Vector<U32> & smooth, Vector<U32> & remap, Vector<U32> * vertId = NULL);
   void shareVerts(ObjectMimic *, S32 dl, TSShape *);
   void shareVerts(SkinMimic *, Vector<U32> & originalRemap, TSShape *);
   void shareBones(SkinMimic *, TSShape *);
   bool vertexSame(Point3F &, Point3F &, Point2F &, Point2F &, U32 smooth1, U32 smooth2, U32 idx1, U32 idx2, Vector<U32> * vertId);
   void stripify(Vector<TSDrawPrimitive> &, Vector<U16> & indices);

   // add a name to the shape and return the index
   S32 addName(const char *, TSShape *);

   // adds a mesh node to the mesh list and returns the mesh it hangs off
   ObjectMimic * getObject(INode *, char * name, S32 size, S32 * detailNum, F32 multiResPercent = -1.0f, bool matchFullName = true, bool isBone=false, bool isSkin=false);
   ObjectMimic * addObject(INode *, Vector<S32> *pValidDetails);
   ObjectMimic * addObject(INode *, Vector<S32> *pValidDetails, bool multiRes, S32 multiResSize = -1, F32 multiResPercent = -1.0f);
   ObjectMimic * addBoneObject(INode * bone, S32 subtreeNum);
   MeshMimic   * addSkinObject(SkinMimic * skinMimic);
   void addDecalObject(INode *, ObjectMimic *);
   
   // functions for adding decals
   bool decalOn(DecalObjectMimic*,S32 time);
   void interpolateTVert(Point3F v0, Point3F v1, Point3F v2, Point3F planeNormal, Point3F & p, Point3 tv0, Point3 tv1, Point3 tv2, Point3 & tv);
   S32 generateDecalFrame(DecalObjectMimic*,S32 time);
   void findDecalTexGen(MatrixF &, Point4F &, Point4F &);
   S32  findLastDecalFrameNumber(DecalObjectMimic*);
   bool prepareDecal(INode * decalNode, S32 time);
   void  generateDecalFrame(DecalObjectMimic*, S32 time, S32 dl);
   bool getDecalTVert(U32 decalType, Point3 vert, Point3F normal, Point3 & tv);
   bool getPlaneDecalTVSpecial(Point3 & vert, const Point3F & normal, Point3 & tv);
   void findNonOrthogonalComponents(const Point3F & v1, const Point3F & v2, const Point3F & p, F32 & t1, F32 & t2);
   bool getDecalProjPoint(U32 decalType, Point3 vert, Point3F & normal, U32 decalFaceIdx, Point3F & p);
   bool checkDecalFilter(Point3 v0, Point3 v1, Point3 v2);
   bool checkDecalFace(Point3 tv0, Point3 tv1, Point3 tv2);
   
   // utility function for traversing tree in depth first order
   // returns NULL when we get back to the top
   NodeMimic * findNextNode(NodeMimic * cur);

   // utility function called by collapseTransform removes
   // the node from the mimic tree structure, taking care
   // to otherwise maintain the integrity of the shape tree
   void snip(NodeMimic *);

   // should we cut this node?
   bool cut(NodeMimic *);

   // is this node on the never animate list?
   bool neverAnimateNode(NodeMimic *);

   void splitFaceX(TSDrawPrimitive & face, TSDrawPrimitive & faceA, TSDrawPrimitive & faceB,
                   Vector<Point3> & verts, Vector<Point3> & tverts, Vector<U16> & indices,
                   Vector<bool> & flipX, Vector<bool> & flipY,
                   F32 splitAt,
                   Vector<U32> & smooth, Vector<U32> * vertId);
   void splitFaceY(TSDrawPrimitive & face, TSDrawPrimitive & faceA, TSDrawPrimitive & faceB,
                   Vector<Point3> & verts, Vector<Point3> & tverts, Vector<U16> & indices,
                   Vector<bool> & flipX, Vector<bool> & flipY,
                   F32 splitAt,
                   Vector<U32> & smooth, Vector<U32> * vertId);
   void handleCropAndPlace(Point3 &, CropInfo &);

public:
   ShapeMimic();
   ~ShapeMimic();

   // check for errors
   bool isError();
   const char * getError();
   
   //
   void setGlobals(INode * pNode, S32 polyCount, F32 minVolPerPoly, F32 maxVolPerPoly);

   void recordMergeOrder(IParamBlock *, MultiResMimic *);
   void collapseEdge(Vector<Face> & faces, S32 fromA, S32 toB);
   void getMultiResData(INode * pNode, Vector<S32> & multiResSize, Vector<F32> & multiResPercent);
   void addMultiRes(INode * pNode, INode * multiResNode);
   MultiResMimic * getMultiRes(INode * pNode);
   void remapWeights(Vector<SkinMimic::WeightList*> &, INode * skinNode, INode * multiResNode);
   void copyWeightsToVerts(SkinMimic *);

   void fixupT2AutoDetail();
   void applyAutoDetail(Vector<S32> &, Vector<const char*> &, Vector<INode*> &);

   // find which vertices merge with which vertices
   void findMergeIndices(MeshMimic *, Matrix3 & objectOffset, const Vector<TSDrawPrimitive> &,
           Vector<Point3F> &, Vector<Point3F> &, Vector<Point2F> &,
           const Vector<U16> & indices, Vector<U16> & mergeIndices,
           Vector<U32> & smooth, Vector<U32> & vertId, S32 numChildVerts);
   // very specialized function used by above
   void addMergeTargetAndMerge(Vector<TSDrawPrimitive> & faces, S32 & faceNum, S32 & v,
           Vector<U16> & indices, Vector<U16> & mergeIndices,
           Vector<Point3F> & verts, Vector<Point2F> & tverts, Vector<Point3F> & norms,
           Vector<U32> & smooth, Vector<U32> & vertId,
           Vector<S32> & mergeOrder, Vector<S32> & mergeID, Vector<Point3F> & mergeTarget);
   // called in optimizeMeshes
   void fixupMergeIndices(TSShape *);
   // called after adding first frame to non-skin objects
   void generateMergeIndices(ObjectMimic*);
   void testMerge(TSShape *, ObjectMimic *, S32 dl, S32 numChildVerts);

   // parts added to mimic using these methods...order unimportant
   void addBounds(INode * pNode);
   void addSubtree(INode * pNode);
   void addNode(NodeMimic *,INode *, Vector<S32> &,bool);
   void addMesh(INode * pNode) { addObject(pNode,NULL); }
   void addSkin(INode * pNode);
   void addSkin(INode * pNode, S32 multiResSize, F32 multiResPercent);
   void addSequence(INode * pNode);
   S32 addMaterial(INode * pNode, S32 materialIndex, CropInfo *);
   S32 findMaterial(const char * name, const char * fullName, U32 flags, S32,S32,S32,F32 dscale = 1.0f,F32 emapAmount=1.0f); // finds, and adds if not there
   void appendMaterialList(const char * name, U32 flags, S32,S32,S32,F32 dscale = 1.0f,F32 emapAmount=1.0f); // adds to the end
   const char * getBaseTextureName(const char *);

   // can optionally get rid of nodes with no mesh
   void collapseTransforms();
   void clearCollapseTransforms();

   F32 findMaxDistance(TSMesh * loMesh, TSMesh * hiMesh, F32 & total, S32 & count);

   // the payoff...call after adding all of the above
   TSShape * generateShape();
   void initShape(TSShape*);
   void destroyShape(TSShape*);
};

#endif

