/*===========================================================================
 *
 * File:	DF3DObj.H
 * Author:	Dave Humphrey (uesp@m0use.net)
 * Created On:	Wednesday, June 20, 2001
 *
 * Defines the CDF3dObject class for handling a 3D object file.
 *
 *=========================================================================*/
#ifndef __DF3DOBJ_H
#define __DF3DOBJ_H


/*===========================================================================
 *
 * Begin Required Include Files
 *
 *=========================================================================*/
  #include "uesp/dagger/common/dfcommon.h"
  #include "common/dl_str.h"
/*===========================================================================
 *		End of Required Include Files
 *=========================================================================*/


/*===========================================================================
 *
 * Begin Definitions
 *
 *=========================================================================*/

	/* Code limited number of points per faces */
  #define DF3D_FACE_MAXPOINTS 32

	/* Record sizes */
  #define DF3D_FACE_RECORDSIZE  8
  #define DF3D_DATA2_RECORDSIZE 18

	/* Version values */
  #define DF3D_VERSION_25 25
  #define DF3D_VERSION_26 26
  #define DF3D_VERSION_27 27
  #define DF3D_VERSION_UNKNOWN 0

/*===========================================================================
 *		End of Definitions
 *=========================================================================*/
#pragma pack(push, 1)

/*===========================================================================
 *
 * Begin Type Definitions
 *
 *=========================================================================*/

	/* Basic Daggerfall point type */
  typedef struct {
    long X;
    long Y;
    long Z;
   } df3dpoint_t;

   	/* Unknown section data1 record type */
  typedef struct {
    unsigned char Unknown[24];
   } df3ddata1_t;

	/* Unknown section data2 record subtype */
  typedef struct {
    byte Unknown[6];
   } df3ddata2_subrecord_t;

	/* Unknown section data2 record type */
  typedef struct {
    long  Unknown1;
    long  Unknown2;
    long  Unknown3;
    long  Unknown4;
    short NumSubRecords;
    df3ddata2_subrecord_t* pSubRecords;
   } df3ddata2_t;
 
/*===========================================================================
 *		End of Type Definitions
 *=========================================================================*/


/*===========================================================================
 *
 * Begin 3D Object Header Structure
 *
 * Each object has this fixed length header.
 *
 *=========================================================================*/
typedef struct {
  char  Version[4];	/* Object version string "v2.7" */
  long  NumPoints;	/* Number of points in object */
  long  NumFaces;	/* Number of face in object */
  long  Unknown1;
  long  NullValue1;	/* Always 0 */
  long  NullValue2;	/* Always 0 */
  long  Data1Offset;	/* Offset to Data1 section from start of file */
  long  Data2Offset;	/* Offset to Data2 section from start of file */
  long  NumData2;	/* Number of Data2 records */
  short Unknown2;
  short Unknown3;
  long  NullValue3;	/* Always 0 */
  long  NullValue4;	/* Always 0 */
  long  PointOffset;	/* Offset to point data from start of file */
  long  NormalOffset;	/* Offset to normal data from start of file */
  long  Unknown4;
  long  FaceOffset;	/* Offset to face data from start of file */
 } df3dheader_t;
/*===========================================================================
 *		End of 3D Object Header Structure
 *=========================================================================*/


/*===========================================================================
 *
 * Begin 3D Object Face Point Data
 *
 * The data structure for each point making up a face/plane.
 *
 *=========================================================================*/
typedef struct {
  long  PointOffset;	/* Indicates point data to use (3D object version dependant) */
  short TextureU;	/* Texture coordinates */
  short TextureV;
 } df3dfacedata_t;
/*===========================================================================
 *		End of 3D Object Face Point DAta
 *=========================================================================*/


/*===========================================================================
 *
 * Begin 3D Object Plane Record Structure
 *
 * Each plane/face in the 3D Object has this data structure.  The structure
 * is a bit field.
 *
 *=========================================================================*/
typedef struct {
  byte   NumPoints;		/* Number of points making up the face */
  byte   Unknown1;
  ushort SubImageIndex : 7;	/* Indicate the texture image to use */
  ushort TextureIndex  : 9;
  long   Unknown2;
    
  		/* The data for each point in the face */
  df3dfacedata_t FaceData[DF3D_FACE_MAXPOINTS];

  short  Tag;			/* Used for custom user data.  Must be after the
				   face data so it doesn't affect the reading
				   and writing of the face. */
 } df3dface_t;
/*===========================================================================
 *		End of 3D Object Plane Record Structure
 *=========================================================================*/


/*===========================================================================
 *
 * Begin Callback Function Types
 *
 *=========================================================================*/

	/* ForEachPoint() callback function type */
  typedef int (* PFNDF3D_FOREACHPOINT)  (      df3dpoint_t& DFPoint, void* pCallbackData);
  typedef int (* PFNDF3D_FOREACHPOINTC) (const df3dpoint_t& DFPoint, void* pCallbackData);

  	/* ForEachFace() callback function type */
  typedef int (* PFNDF3D_FOREACHFACE)  (      df3dface_t& DFFace, void* pCallbackData);
  typedef int (* PFNDF3D_FOREACHFACEC) (const df3dface_t& DFFace, void* pCallbackData);

/*===========================================================================
 *		End of Callback Function Types
 *=========================================================================*/


	/* Re-enable default structure alignment */
#pragma pack(pop)

/*===========================================================================
 *
 * Begin Class CDF3dObject Definition
 *
 * Handles a single Daggerfall 3D object file, either in a single file or
 * contained within a BSA file.
 *
 *=========================================================================*/
class CDF3dObject {

  /*---------- Begin Private Class Members ----------------------*/
private:
  df3dheader_t   m_Header;	/* The fixed length 3D object header */
  df3dpoint_t*   m_pPoints;	/* Array of points */
  df3dface_t*    m_pFaces;	/* Array of faces */
  df3dpoint_t*   m_pNormals;	/* Array of normals? */
  df3ddata1_t*   m_pData1;	/* Array of unknown data1 records */
  df3ddata2_t*   m_pData2;	/* Array of unknown data2 records */

  int		 m_Version;	/* Shortcut to object verison */

  long		 m_FileSize;	/* Used when loading the file */
  long		 m_StartOffset;


  /*---------- Begin Protected Class Methods --------------------*/
protected:

	/* Helper functions to load object sections */
  boolean ReadHeader  (FILE *pFileHandle);
  boolean ReadPoints  (FILE *pFileHandle);
  boolean ReadFaces   (FILE *pFileHandle);
  boolean ReadNormals (FILE *pFileHandle);
  boolean ReadData1   (FILE *pFileHandle);
  boolean ReadData2   (FILE *pFileHandle);


  /*---------- Begin Public Class Methods -----------------------*/
public:

	/* Class Constructors/Destructors */
  CDF3dObject();
  virtual ~CDF3dObject() { Destroy(); }
  virtual void Destroy (void);

	/* Reset the tags for each face */
  void ClearFaceTags (void) const;

	/* Perform a custom operation for each object element */
  int ForEachFace   (PFNDF3D_FOREACHFACE   fnCallBack, void* pCallbackData);
  int ForEachPoint  (PFNDF3D_FOREACHPOINT  fnCallBack, void* pCallbackData);
  int ForEachFaceC  (PFNDF3D_FOREACHFACEC  fnCallBack, void* pCallbackData) const;
  int ForEachPointC (PFNDF3D_FOREACHPOINTC fnCallBack, void* pCallbackData) const;

	/* Get header information */
  long  GetNumPoints    (void) const;
  long  GetNumFaces     (void) const;
  long  GetNumData2     (void) const;
  long  GetPointOffset  (void) const;
  long  GetFaceOffset   (void) const;
  long  GetNormalOffset (void) const;
  long  GetData1Offset  (void) const;
  long  GetData2Offset  (void) const;
  char* GetVersion      (void) const;
  int   GetVersionNum   (void) const;
  long  GetMaxY         (void) const;
  long  GetMinY         (void) const;

	/* Access face array */
  df3dface_t* GetFace (const long FaceIndex);
  
	/* Get a point from a face point offset */
  df3dpoint_t* GetFacePoint      (const long PointOffset);
  long         GetFacePointIndex (const long PointOffset);
  df3dpoint_t* GetPointData	 (void) const;

	/* Check version information */
  boolean IsVersion (const char* pString) const;
  boolean IsVersion (const int   Version) const;

	/* Read object from file stream */
  boolean Read (FILE* pFileHandle);

	/* Set class members */
  void SetFileSize (const long Size);

	/* Tag all faces with the given texture */
  int TagFaceTexture (const int TextureIndex, const int ImageIndex) const;

 };
/*===========================================================================
 *		End of Class CDF3dObject Definition
 *=========================================================================*/


/*===========================================================================
 *
 * Begin CDF3dObject Inline Methods
 *
 *=========================================================================*/

	/* Get header information */
inline long  CDF3dObject::GetNumFaces     (void) const { return (m_Header.NumFaces); }
inline long  CDF3dObject::GetNumPoints    (void) const { return (m_Header.NumPoints); }
inline long  CDF3dObject::GetNumData2     (void) const { return (m_Header.NumData2); }
inline long  CDF3dObject::GetPointOffset  (void) const { return (m_Header.PointOffset); }
inline long  CDF3dObject::GetFaceOffset   (void) const { return (m_Header.FaceOffset); }
inline long  CDF3dObject::GetNormalOffset (void) const { return (m_Header.NormalOffset); }
inline long  CDF3dObject::GetData1Offset  (void) const { return (m_Header.Data1Offset); }
inline long  CDF3dObject::GetData2Offset  (void) const { return (m_Header.Data2Offset); }
inline char* CDF3dObject::GetVersion	  (void) const { return (char*)(m_Header.Version); }
inline int   CDF3dObject::GetVersionNum   (void) const { return (m_Version); }

	/* Return a face from the object */
inline df3dface_t* CDF3dObject::GetFace (const long FaceIndex) {
  IASSERT(FaceIndex >= 0 && FaceIndex < GetNumFaces());
  return &(m_pFaces[FaceIndex]);
 }

	/* Return a point object from a face point offset */
inline df3dpoint_t* CDF3dObject::GetFacePoint (const long PointOffset) {
  return &(m_pPoints[GetFacePointIndex(PointOffset)]);
 }

	/* Return a point index from a face point offset */
inline long CDF3dObject::GetFacePointIndex (const long PointOffset) {
  long PointIndex;

	/* Special case for 2.5 version objects */
  if (m_Version == DF3D_VERSION_25)
    PointIndex = PointOffset / 4;
  else
    PointIndex = PointOffset / 12;

	/* Ensure a valid point index */
  IASSERT(PointIndex >= 0 && PointIndex < GetNumPoints());
  return (PointIndex);
 }

	/* Return a the point data array */
inline df3dpoint_t* CDF3dObject::GetPointData (void) const {
  return (m_pPoints);
 }

	/* Check version information */
inline boolean CDF3dObject::IsVersion (const char* pString) const { return (strnicmp(m_Header.Version, pString, 4) == 0 ? TRUE : FALSE); }
inline boolean CDF3dObject::IsVersion (const int   Version) const { return (m_Version == Version ? TRUE : FALSE); }

	/* Set class members */
inline void CDF3dObject::SetFileSize (const long Size) { m_FileSize = Size; }

/*===========================================================================
 *		End of CDF3dObject Inline Methods
 *=========================================================================*/


/*===========================================================================
 *
 * Begin Inline Functions
 *
 *=========================================================================*/


	/* Returns the divisor needed to convert a face data point offset 
	 * into a point index. */
inline const int GetDF3dFacePointOffsetDivisor (const int& Version) {
 
	/* Special case for 2.5 version objects */
  if (Version == DF3D_VERSION_25) return (4);
  return (12);
 }

	/* Get a point index from an face point offset.  Does not verify the
	 * point index returned or the input data. */
inline const int GetDF3dFacePointIndex (const df3dfacedata_t& FaceData, const int& Version) {
  return (FaceData.PointOffset / GetDF3dFacePointOffsetDivisor(Version));
 }

/*===========================================================================
 *		End of Inline Functions
 *=========================================================================*/



#endif
/*===========================================================================
 *		End of File Df3dobj.H
 *=========================================================================*/