#include <stdio.h>
#include <hnd.h>
#include <HND_FileIO.h>
#include <HND_FileIOAttr.h>

// エラーコールバック関数
// HNDがエラーを出力した場合に呼ばれる
void hnd_error_callback(HND_ERROR err, HnmID HandleID, Sint32 num, HnmID id)
{
    printf("HND_ERROR:%s, HandleID=%s, num=%d, id=%s\n",
			hnSysGetErrorString(err), hnSysGetHnmIDString(HandleID), num, hnSysGetHnmIDString(id));
}

void GetPolygonVertex(HNS_HANDLE *hnd_polyvtx)
{
	HND_ERROR   err = HND_SUCCESS;                  // HNDリターン
	Sint32	i, j, polyvtx_cnt, layer_cnt, vtx_id, nor_id, diff_id, spec_id, uv_id;

	// ポリゴン頂点数の取得(大抵の場合は３頂点のトライアングルデータ。SIからのデータは多角形ポリゴンの場合がある)
	// 3DS Maxは1頂点だけのポリゴンがあるようなので注意が必要
	polyvtx_cnt = hnGetHandleDataCount(hnd_polyvtx);

	for (i = 0; i < polyvtx_cnt; i++) {
		HNS_HANDLE hnd_vtx = {0};
		HNS_HANDLE hnd_nor = {0};
		HNS_HANDLE hnd_layer = {0};
		HNS_F3D  hns_f3d = {0};

		// ポリゴン頂点IDの取得
		err = hnGetValueSint32(hnd_polyvtx, i, HNM_VERTEX, &vtx_id);

		// 頂点データハンドルを直接取得（実体がここにあるわけではなく参照）
		err = hnGetValueHandle(hnd_polyvtx, i, HNM_VERTEX, &hnd_vtx);

		// ポリゴン法線IDの取得
		err = hnGetValueSint32(hnd_polyvtx, i, HNM_NORMAL, &nor_id);

		// ポリゴン法線ハンドルを直接取得
		err = hnGetValueHandle(hnd_polyvtx, i, HNM_NORMAL, &hnd_nor);

		// ポリゴン法線データを直接取得
		hnGetValue(&hnd_nor, 0, HNM_VALUE, &hns_f3d);

		// ディフューズ頂点カラーIDの取得
		err = hnGetValueSint32(hnd_polyvtx, i, HNM_DIFFUSE, &diff_id);

		// スペキュラ頂点カラーIDの取得
		err = hnGetValueSint32(hnd_polyvtx, i, HNM_SPECULAR, &spec_id);

		// UVレイヤーハンドルを直接取得
		err = hnGetValueHandle(hnd_polyvtx, i, HNM_UVLAYER, &hnd_layer);

		// マルチUVの場合複数のUVIDが格納されている
		layer_cnt = hnGetHandleDataCount(&hnd_layer);
		for (j = 0; j < layer_cnt; j++) {
			HNS_HANDLE hnd_uv = {0};
			HNS_F2D  hns_f2d = {0};

			// UVIDの取得
			err = hnGetValueSint32(&hnd_layer, j, HNM_UV, &uv_id);

			// 直接UVの値が参照したい場合はhnGetValueHandle関数を使用する
			// UVの実データはHND_MODEL配下にある
			err = hnGetValueHandle(&hnd_layer, j, HNM_UV, &hnd_uv);

			// UVの値を取得する
			hnGetValue(&hnd_uv, 0, HNM_VALUE, &hns_f2d);
		}
	}
}

void GetPolygon(HNS_HANDLE *hnd_mdl)
{
	HNS_HANDLE hnd_poly;		// HND polygon
	HNS_HANDLE hnd_polynormal;	// HND polygon normal
	HNS_HANDLE hnd_polyvtx;		// HND polygon vertex
	HNS_HANDLE hnd_matcls;
	HNS_HANDLE hnd_mat;
	Sint32  mdl_id;				// HND model id
	Sint32  nor_cnt;			// HND polygon normal count
	Sint32  nor_id;				// HND polygon normal id
	Sint32  mat_id;				// HND matrial id
	Sint32  i, poly_cnt;
	HND_ERROR   err = HND_SUCCESS;                  // HNDリターン

	// モデルIDの取得
	err = hnGetValueSint32(hnd_mdl, 0, HNM_ID, &mdl_id);

	// ポリゴンハンドルの取得
	err = hnGetValueDataHandle(hnd_mdl, 0, HNM_POLYGON, &hnd_poly);

	// ポリゴン法線数の取得
	err = hnGetValueSint32(hnd_mdl, 0, HNM_POLYNORMAL, &nor_cnt);

	// ポリゴン法線ハンドルの取得
	err = hnGetValueDataHandle(hnd_mdl, 0, HNM_POLYNORMAL, &hnd_polynormal);

	// ポリゴン数の取得
	poly_cnt = hnGetHandleDataCount(&hnd_poly);

	for (i = 0; i < poly_cnt; i++) {
		HNS_F3D  hns_f3d = {0};

		// マテリアルクラスタIDの取得
		err = hnGetValueSint32(&hnd_poly, i, HNM_MATERIALCLUSTER, &mat_id);

		// ポリゴンから直接マテリアルクラスタハンドルを取得する場合(実体はオブジェクトにある)
		err = hnGetValueDataHandle(&hnd_poly, i, HNM_MATERIALCLUSTER, &hnd_matcls);

		// さらにマテリアルクラスタハンドルからマテリアルを取得する場合
		// (実体はオブジェクトにある。Numに0を指定している点に注意)
		err = hnGetValueDataHandle(&hnd_matcls, 0, HNM_MATERIAL, &hnd_mat);

		// ポリゴン法線IDの取得
		err = hnGetValueSint32(&hnd_poly, i, HNM_NORMAL, &nor_id);

		// ポリゴン法線の取得
		err = hnGetValue(&hnd_poly, i, HNM_NORMAL, &hns_f3d);

		// HNDポリゴン頂点ハンドルの取得
		err = hnGetValueDataHandle(&hnd_poly, i, HNM_POLYVTX, &hnd_polyvtx);

		// ポリゴン頂点データの取得
		GetPolygonVertex(&hnd_polyvtx);
	}
}

void GetVertexList(HNS_HANDLE *hnd_mdl)
{
	HND_ERROR	err = HND_SUCCESS;                  // HNDリターン
	HNS_HANDLE	hnd_vtx;
	Sint32		i, vtx_cnt, vtx_id;

	// 頂点ハンドルの取得
	err = hnGetValueDataHandle(hnd_mdl, 0, HNM_VERTEX, &hnd_vtx);

	// 頂点数の取得
	vtx_cnt = hnGetHandleDataCount(&hnd_vtx);

	for (i = 0; i < vtx_cnt; i++) {
		HNS_F3D hns_f3d = {0};
		HNS_HANDLE	hnd_weight;
		Sint32		j, weight_cnt;

		// 頂点IDの取得（ポリゴン頂点ハンドルから参照されている）
		err = hnGetValueSint32(&hnd_vtx, i, HNM_ID, &vtx_id);

		// 頂点ポジションの取得(複数のデータ取得方法がある。どれも結果は同じなので、使いやすいものを利用すると良い)
	    hnGetValueFloatX3(&hnd_vtx, i, HNM_POSITION, &hns_f3d.part.x,
	                                                 &hns_f3d.part.y,
	                                                 &hns_f3d.part.z);

		// hnGetValueFloatX()の場合、Float[3]の配列渡す
		hnGetValueFloatX(&hnd_vtx, i, HNM_POSITION, hns_f3d.xyz);

		// hnGetValue()の場合、HNS_F3D構造体をそのまま渡す
		hnGetValue(&hnd_vtx, i, HNM_POSITION, &hns_f3d);

		// ウエイトハンドルの取得
		hnGetValueDataHandle(&hnd_vtx, i, HNM_ENVWEIGHT, &hnd_weight);

		// ウエイト数の取得
		hnGetValueSint32(&hnd_vtx, i, HNM_ENVWEIGHT, &weight_cnt);

		for (j = 0; j < weight_cnt; j++) {
			Sint32	node_id;
			Float	weight;

			// ボーンとなるノードIDの取得
		    hnGetValueSint32(&hnd_weight, j, HNM_NODE, &node_id);

			// ウエイト値の取得
		    hnGetValueFloat(&hnd_weight, j, HNM_WEIGHT , &weight);
		}
	}


}

void GetKeyData(HNS_HANDLE *hnd_key)
{
	// キーの数を取得する
	Sint32 key_cnt = hnGetHandleDataCount( hnd_key );
	Sint32 i;

	// 本来はkey_cnt分メモリアロケートする必要がある
	Float Frame, fValue, fValue4[4];
	Sint32 sValue;
	Uint32 uValue;
	Sint32 type;
	Float InParam[3], OutParam[3];

	/*--- キーの種類 ---*/
	switch ( hnGetHandleType(hnd_key) ) {

	/*--- スタンダードキー ---*/
	case HNM_FLOATKEY:
		for (i = 0; i < key_cnt; i++) {
			hnGetValueFloat(hnd_key, i, HNM_FRAME, &Frame );
			hnGetValueFloat(hnd_key, i, HNM_KEY  , &fValue);
		}
		break;

	/*--- ティピカルキー ---*/
	case HNM_TFLOATKEY:
		for (i = 0; i < key_cnt; i++) {
			hnGetValueFloat (hnd_key, i, HNM_FRAME, &Frame );
			hnGetValueFloat (hnd_key, i, HNM_KEY  , &fValue);
			hnGetValueSint32(hnd_key, i, HNM_TYPE , &type  );
			switch ( type ) {
			/*--- SIスプライン ---*/
			case SI_SPLINE :
				break;
			/*--- リニア ---*/
			case SI_LINEAR  : case XSI_LINEAR  : case MAX_LINEAR  : case MAYA_LINEAR  :
				break;

			/*--- ベジェ ---*/
			case SI_BEZIER  : case XSI_BEZIER  : case MAX_BEZIER  : case MAYA_BEZIER  :
				break;

			/*--- コンスタント ---*/
			case SI_CONSTANT: case XSI_CONSTANT: case MAX_CONSTANT: case MAYA_CONSTANT:
				break;

			/*--- 最後のキー ---*/
			case SI_LASTKEY : case XSI_LASTKEY : case MAX_LASTKEY : case MAYA_LASTKEY :
			default:
				break;
			}
			hnGetValueFloatX(hnd_key, i, HNM_INPARAM , InParam );
			hnGetValueFloatX(hnd_key, i, HNM_OUTPARAM, OutParam);
		}
		break;

	/*--- IDキー ---*/
	case HNM_IDKEY:
		for (i = 0; i < key_cnt; i++) {
			hnGetValueFloat (hnd_key, i, HNM_FRAME, &Frame );
			hnGetValueSint32(hnd_key, i, HNM_KEY  , &sValue);
		}
		break;

	/*--- UIDキー ---*/
	case HNM_UIDKEY:
		for (i = 0; i < key_cnt; i++) {
			hnGetValueFloat (hnd_key, i, HNM_FRAME, &Frame );
			hnGetValueUint32(hnd_key, i, HNM_KEY  , &uValue);
		}
		break;

	/*--- ティピカルUIDキー ---*/
	case HNM_TUIDKEY:
		for (i = 0; i < key_cnt; i++) {
			hnGetValueFloat (hnd_key, i, HNM_FRAME, &Frame );
			hnGetValueUint32(hnd_key, i, HNM_KEY  , &uValue);
			hnGetValueSint32(hnd_key, i, HNM_TYPE , &type  );
			switch ( type ) {
			/*--- コンスタント ---*/
			case SI_CONSTANT: case XSI_CONSTANT: case MAX_CONSTANT: case MAYA_CONSTANT:
				break;
			/*--- ONESHOT ---*/
			case SI_ONESHOT: case XSI_ONESHOT: case MAX_ONESHOT: case MAYA_ONESHOT:
				break;
			default:
				break;
			}
		}
		break;

	/*--- クォータニオンキー ---*/
	case HNM_QUATERNIONKEY:
		for (i = 0; i < key_cnt; i++) {
			hnGetValueFloat (hnd_key, i, HNM_FRAME, &Frame );
			hnGetValueFloatX(hnd_key, i, HNM_KEY  , fValue4);
		}
		break;

	default:
		// not support
		break;
	}
}
void GetMotion(HNS_HANDLE *hnd_mot, Sint32 num, HnmID hnd_id)
{
	HND_ERROR  err = HND_SUCCESS;
	HNS_HANDLE hnd_key = {0};
	err = hnGetValueDataHandle(hnd_mot, num, hnd_id, &hnd_key);
	if (err != HND_SUCCESS) return;

	// キーデータの取得
	GetKeyData(&hnd_key);
}

void GetMotionX3(HNS_HANDLE *hnd_mot, Sint32 num, HnmID hnd_id)
{
	HND_ERROR  err = HND_SUCCESS;
	HNS_HANDLE hnd_key3[3] = {0};
	err = hnGetValueDataHandleX3(hnd_mot, num, hnd_id, hnd_key3);
	if (err != HND_SUCCESS) return;

	// キーデータの取得
	GetKeyData(&hnd_key3[0]);
	GetKeyData(&hnd_key3[1]);
	GetKeyData(&hnd_key3[2]);

}

void GetNodeMotion(HNS_HANDLE *hnd_mot)
{
	HNS_HANDLE hnd_trsmot = {0};
	HNS_HANDLE hnd_item = {0};

    // TRSモーションハンドルを取得
    // モーションレイヤーを使用している場合は複数ハンドルを取得する必要があるが、ここでは1つのみ
	hnGetValueDataHandle(hnd_mot, 0, HNM_TRSMOTION, &hnd_trsmot);

	// トランスモーション取得
	GetMotionX3(&hnd_trsmot, 0, HNM_TRANSLATION);

	// ローテートモーション取得
	GetMotionX3(&hnd_trsmot, 0, HNM_ROTATION);

	// スケールモーション取得
	GetMotionX3(&hnd_trsmot, 0, HNM_SCALE);

	// アイテムモーションハンドルの取得(タイプにより取得されるデータは異なる)
	hnGetValueDataHandle(hnd_mot, 0, HNM_ITEM   , &hnd_item   );

	// アイテムモーションのタイプを取得する
	switch ( hnGetHandleType(&hnd_item) ) {
	case HNM_MODELMOT:
		break;
	case HNM_CAMERAMOT:
		break;
	case HNM_LIGHTMOT:
		break;
	}
}

void GetNode(HNS_HANDLE *hnd_node)
{
	HNS_HANDLE hnd_item = {0};
	HNS_HANDLE hnd_motion = {0};
	HNS_HANDLE hnd_child = {0};
	HNS_HANDLE hnd_sibling = {0};
	Sint32     type;
	HND_ERROR  err = HND_SUCCESS;          // HNDリターン
	HNS_F3D    f3d;
	Float      xyz[3];

	// アイテムハンドルの取得
	hnGetValueDataHandle(hnd_node, 0, HNM_ITEM, &hnd_item);

	// アイテムタイプの取得
	type = hnGetHandleType(&hnd_item);

	switch ( type ) {
	// SI チェインルートの場合
	case HNM_SICHAINRT:
		break;

	// SI ボーンの場合
	case HNM_SIBONE:
		break;

	// SI エフェクタの場合
	case HNM_SIEFF:
		break;

	// インスタンスモデルの場合
	case HNM_INSTMDL:
		break;

	// モデルの場合
	case HNM_MODEL:
	{
		HNS_HANDLE hnd_vertex = {0};
		HNS_HANDLE hnd_polygon = {0};

		// 頂点データの取得
		GetVertexList(&hnd_item);

		// ポリゴンの取得
		GetPolygon(&hnd_item);
		break;
	}}

	/* 配列の変数を使用して値を取得する場合 */
	/* Transデータ */
	err = hnGetValueFloatX(hnd_node, 0, HNM_TRANSLATION, xyz);

	/* Rotationデータ */
	err = hnGetValueFloatX(hnd_node, 0, HNM_ROTATION   , xyz);

	/* Scaleデータ */
	err = hnGetValueFloatX(hnd_node, 0, HNM_SCALE      , xyz);

	/* HNS_F3D構造体を使用して値を取得する場合 */
	err = hnGetValue(hnd_node, 0, HNM_TRANSLATION, &f3d);
	err = hnGetValue(hnd_node, 0, HNM_ROTATION   , &f3d);
	err = hnGetValue(hnd_node, 0, HNM_SCALE      , &f3d);

	/* X,Y,Zそれぞれの値を取得する場合 */
	err = hnGetValueFloatX3(hnd_node, 0, HNM_TRANSLATION, &f3d.part.x,
	                                                      &f3d.part.y,
	                                                      &f3d.part.z);
	err = hnGetValueFloatX3(hnd_node, 0, HNM_ROTATION   , &f3d.part.x,
	                                                      &f3d.part.y,
	                                                      &f3d.part.z);
	err = hnGetValueFloatX3(hnd_node, 0, HNM_SCALE      , &f3d.part.x,
	                                                      &f3d.part.y,
	                                                      &f3d.part.z);

	/* モーションデータ */
	err = hnGetValueDataHandle(hnd_node, 0, HNM_MOTION, &hnd_motion);
    if (err == HND_SUCCESS && hnIsHandle(&hnd_motion)) {
		// モーションデータあり
		GetNodeMotion(&hnd_motion);
	}

	// 子ノードの取得
	err = hnGetValueDataHandle(hnd_node, 0, HNM_CHILD  , &hnd_child);
    if (err == HND_SUCCESS && hnIsHandle(&hnd_child)) {
		GetNode(&hnd_child);
	}

	// 弟ノードの取得
	err = hnGetValueDataHandle(hnd_node, 0, HNM_SIBLING, &hnd_sibling);
    if (err == HND_SUCCESS && hnIsHandle(&hnd_sibling)) {
		GetNode(&hnd_sibling);
	}

}

void GetMaterial(HNS_HANDLE *hnd_mat)
{
	HND_ERROR   err = HND_SUCCESS;          // HNDリターン
	HNS_HANDLE	hnd_tex = {0};
	Sint32		i, tex_cnt = 0;
	Sint32		uvlayer_idx;
	Float		RepeatU = 1.f, RepeatV = 1.f;

	// テクスチャハンドルを取得
	hnGetValueDataHandle(hnd_mat, 0, HNM_TEXTURE, &hnd_tex);

	// テクスチャ数の取得
	tex_cnt = hnGetHandleDataCount(&hnd_tex);

	for (i = 0; i < tex_cnt; i++ ) {
		// このテクスチャが使用するUVレイヤーのインデックスを取得
		hnGetValueSint32(&hnd_tex, i, HNM_UVLAYER, &uvlayer_idx);

		// UVリピート
		err = hnGetValueFloat(&hnd_tex, i, HNM_UREPEAT, &RepeatU);
		err = hnGetValueFloat(&hnd_tex, i, HNM_VREPEAT, &RepeatV);
	}
}

void GetObject(HNS_HANDLE *hnd_obj)
{
	Sint32		node_cnt = 0;
	HNS_HANDLE	hnd_node = {0};
	Sint32		clu_idx_cnt = 0;
	HNS_HANDLE	hnd_matcluster = {0};
	HNS_HANDLE	hnd_mat = {0};

	// ノード数を取得
	hnGetValueSint32(hnd_obj, 0, HNM_NODE, &node_cnt);

	// ルートノードデータハンドルを取得
	hnGetValueDataHandle(hnd_obj, 0, HNM_NODE, &hnd_node);

	// ノードの取得
	GetNode(&hnd_node);

	// マテリアルクラスタ数の取得
	hnGetValueSint32(hnd_obj, 0, HNM_MATERIALCLUSTER, &clu_idx_cnt);

	// マテリアルクラスタハンドルを取得
	hnGetValueDataHandle(hnd_obj, 0, HNM_MATERIALCLUSTER, &hnd_matcluster);

	// マテリアルハンドルを取得
	hnGetValueDataHandle(hnd_obj, 0, HNM_MATERIAL, &hnd_mat);
	GetMaterial(&hnd_mat);
}

void GetScene(HNS_HANDLE *hnd_scn)
{
	HND_ERROR   err = HND_SUCCESS;          // HNDリターン
	HNS_HANDLE  hnd_obj  =  {0};
	HNS_HANDLE  hnd_node = {0};
	Sint32		obj_cnt = 0;

	// オブジェクト数を取得
	hnGetValueSint32(hnd_scn, 0, HNM_OBJECT, &obj_cnt);

	// 先頭オブジェクトハンドルを取得
	err = hnGetValueDataHandle(hnd_scn, 0, HNM_OBJECT, &hnd_obj);

	// オブジェクト数分繰返し
	while (err == HND_SUCCESS && hnIsHandle(&hnd_obj)) {

		// ルートノードデータハンドルを取得
		err = hnGetValueDataHandle(&hnd_obj, 0, HNM_NODE, &hnd_node);

        // オブジェクトデータ読込み
		GetObject(&hnd_obj);

        // 次のオブジェクトデータの取得
		err = hnGetValueDataHandle(&hnd_obj, 0, HNM_NEXT, &hnd_obj);
	}
}

int main(int argc, char* argv[])
{
	HNS_HANDLE		hscn_hdl = {0};
    int				att = EIDefault;
    long			err;
	Sint32			ret;
	const char		*hnd_fname   = "C:\\test\\hnd_sample.hna";

	// HNDの初期化
	hnSysInitSystem();

	// HNDのエラーコールバックを設定する
	hnSysSetErrorCallback(hnd_error_callback);

	// HNDファイルの読み込み
	if ((ret = hnuFIOImportHNDFile(&hscn_hdl, hnd_fname, &att, &err)) != HND_IOERR_NOERR) {
		// エラー
		return -1;
	}

	// シーンデータの取得
	GetScene(&hscn_hdl);

	// HNDファイル出力(上書き注意)
	// 読み込んだ時のattをそのまま渡すと同じフォーマットで出力する
	ret = hnuFIOExportHNDFile(&hscn_hdl, 0, att, hnd_fname, &err);

	// バイナリ出力する場合
	att |= EIBinary;
	ret = hnuFIOExportHNDFile(&hscn_hdl, 0, att, hnd_fname, &err);

	// HNDの終了処理（このHNDで確保されたメモリはすべて開放される）
	hnSysExitSystem();
	return 0;
}

