/********************************************************************/
/* Copyright (c) 2019 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/********************************************************************/
/********************************************************************/
#include "stdafx.h"

#include "mg/Group.h"
#include "mg/MGStl.h"
#include "fugen.h"
#include "IO/GLUnvExporter.h"
#include "IO/util.h"

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

// コンストラクター
CGLUnvExporter::CGLUnvExporter()
	 : CGLFileExporter(IDS_FILTER_UNV)
{
}

// group に含まれる MGStl オブジェクトのみを保存する。
//
// MGStl ごとにファイルを作成する可能性がある。
// ファイル名には適当に連番が付く。
bool CGLUnvExporter::Save(const MGGroup& group)
{
	const CString& strFileName = GetFileName();

	// MGStl のみを抽出する。
	// ただし、トップの階層にある要素だけテストする。
	std::vector<const MGStl*> stls;
	MGGroup::const_iterator first = group.begin(), last = group.end();
	for(; first != last; ++first){
		if(const MGStl* pStl = dynamic_cast<const MGStl*>(first->get())){
			stls.push_back(pStl);
		}
	}
	if(stls.empty()){
		CString str, strMsg;
		VERIFY(strMsg.LoadString(IDS_MESH_NOT_EXIST));
		str.Format(IDS_WARNING, strMsg);
		putInOutputWindow(str);
		return false;
	}

	const size_t nMesh = stls.size();
	if(nMesh == 1){
		return SaveUnv(*stls.front(), strFileName);
	}

	// 名前をつけつつ SaveUnv
	bool bAllOk = true;
	for(size_t i = 0; i < nMesh; ++i){
		CString strSavePath = AddNumberToPath(strFileName, _T("-%02d"), i);

		CString str, strMsg;
		if(SaveUnv(*stls[i], strSavePath)){
			strMsg.Format(IDS_SAVE_SUCCESS, strSavePath);
			str.Format(IDS_INFORMATION, strMsg);
		}
		else{
			strMsg.Format(IDS_SAVE_FAILURE, strSavePath);
			str.Format(IDS_ERROR, strMsg);
			bAllOk = false;
		}
	}

	return bAllOk;
}

namespace
{
	const int UNV_POSITION_RECORD = 2411;
	const int UNV_TRIANGLE_RECORD = 2412;

	// 右詰で -1 とだけ書かれたフィールド。
	void WriteBlockType(std::ostream& os, int nType)
	{
		os << std::setw(6) << nType << std::endl;
	}

	// 右詰で -1 とだけ書かれたフィールド。
	void WriteBlockSeparator(std::ostream& os)
	{
		WriteBlockType(os, -1);
	}

	void WriteVertexId(std::ostream& os, size_t nId)
	{
		os << std::setw(10) << nId
			<< std::setw(10) << 1
				<< std::setw(10) << 1
					<< std::setw(10) << 8
						<< std::endl;
	}

	// 成分は 25 桁右詰
	void WriteVertex(std::ostream& os, const MGPosition& pos)
	{
		for(int i = 0; i < 3; ++i){
			os << std::setw(25) << pos[i];
		}
		os << std::endl;
	}

	// 座標情報をすべて出力する。
	void WriteVertices(std::ostream& os, const MGStl& stl)
	{
		const std::vector<MGPosition>& vertices = stl.positions();
		const size_t nVert = vertices.size();
		for(size_t i = 0; i < nVert; ++i){
			const MGPosition& pos = vertices[i];
			WriteVertexId(os, i + 1); // 1 からスタート
			WriteVertex(os, pos);
		}
	}

	// 三角形を一個出力 前半
	void WriteTriangleId(std::ostream& os, size_t nId)
	{
		os << std::setw(10) << nId + 1; // 1 からスタート

		const size_t nMagic[5] = {91, 1, 1, 1, 3};
		for(size_t i = 0; i < 5; ++i){
			os << std::setw(10) << nMagic[i];
		}
		os << std::endl;
	}

	// 三角形を一個出力 後半
	void WriteTriangle(std::ostream& os, int* ids)
	{
		for(size_t i = 0; i < 3; ++i){
			os << std::setw(10) << ids[i] + 1; // 1 からスタート
		}
		os << std::endl;
	}

	// 三角形情報をすべて出力する。
	void WriteTriangles(std::ostream& os, const MGStl& stl)
	{
		int nTri = (int)stl.normals().size();
		for(int i = 0; i < nTri; ++i){
			int ids[3] = {0};
			WriteTriangleId(os, i);
			stl.GetVertIndices(i, ids);
			WriteTriangle(os, ids);
		}
	}
} // namespace

// MGStl オブジェクトを UNV 形式で保存する。
bool SaveUnv(const MGStl& stl, const CString& strFileName)
{
	std::ofstream fout(strFileName);
	if(!fout){
		return false;
	}

	// 以降の処理はまったくの山勘。テスト環境がない。

	WriteBlockSeparator(fout);
	WriteBlockType(fout, UNV_POSITION_RECORD);
	fout << std::setiosflags(std::ios_base::scientific | std::ios_base::uppercase)
		<< std::setprecision(16);
	WriteVertices(fout, stl);
	WriteBlockSeparator(fout);

	WriteBlockSeparator(fout);
	WriteBlockType(fout, UNV_TRIANGLE_RECORD);
	WriteTriangles(fout, stl);
	WriteBlockSeparator(fout);

	return true;
}
