/********************************************************************/
/* Copyright (c) 2019 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/********************************************************************/
/********************************************************************/
#include "stdafx.h"
#include "fugen.h"
#include "IO/util.h"
#include "IO/GLNasExporter.h"
#include "mg/Group.h"
#include "mg/MGStl.h"
#include "GLNasSaveOptionDlg.h"

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

// コンストラクター
CGLNasExporter::CGLNasExporter()
	 : CGLFileExporter(IDS_FILTER_NAS),
	   m_type(SMALLFIELD)
{
}

// group に含まれる MGStl オブジェクトのみを保存する。
//
// MGStl ごとにファイルを作成する可能性がある。
// ファイル名には適当に連番が付く。
bool CGLNasExporter::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);
		COUT << (TCAST)str << std::endl;
		return false;
	}

	const FieldType type = m_type;

	const size_t nMesh = stls.size();
	if(nMesh == 1){
		bool bOk = SaveNas(*stls.front(), strFileName, type);
		CString str, strMsg;
		if(bOk){
			strMsg.Format(IDS_SAVE_SUCCESS, strFileName);
			str.Format(IDS_INFORMATION, strMsg);
		}
		else{
			strMsg.Format(IDS_SAVE_FAILURE, strFileName);
			str.Format(IDS_ERROR, strMsg);
		}
		COUT << (TCAST)str << std::endl;
		return bOk;
	}

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

		CString str, strMsg;
		if(SaveNas(*stls[i], strSavePath, type)){
			strMsg.Format(IDS_SAVE_SUCCESS, strFileName);
			str.Format(IDS_INFORMATION, strMsg);
		}
		else{
			strMsg.Format(IDS_SAVE_FAILURE, strFileName);
			str.Format(IDS_ERROR, strMsg);
			bAllOk = false;
		}
		COUT << (TCAST)str << std::endl;
	}

	return bAllOk;
}

/// true を返す。
bool CGLNasExporter::HasOption() const
{
	return true;
}

/// ダイアログを表示して、メンバーデータを設定する。
void CGLNasExporter::DoShowOptionDialog()
{
	CGLNasSaveOptionDlg dlg;
	dlg.SetFieldType(m_type);
	if(dlg.DoModal() != IDOK){
		return;
	}

	m_type = static_cast<FieldType>(dlg.GetFieldType());
}

namespace
{
	// Nastran ファイルの先頭を出力
	void WriteHeader(std::ostream& os)
	{
		os << "$ NASTRAN input file" << std::endl
			<< "BEGIN BULK" << std::endl;
	}

	// 三角形だけを書く
	void WriteCells(std::ostream& os, const MGStl& stl)
	{
		os << "$ Elements and Element Properties" << std::endl;

		// [---1--][---2--][---3--][---4--][---5--][---6--]
		// CTRIA3  EID     PID     G1      G2      G3      
		const size_t nTri = stl.normals().size();
		for(size_t i = 0; i < nTri; ++i){
			int ids[3] = {0};
			stl.GetVertIndices((int)i, ids);

			// [1] CTRIA3
			os << "CTRIA3  ";
			// [2] EID: 1 だけ下駄を履かせる。
			os << std::setw(8) << i + 1;
			// [3] PID (parent id): 特にないので空白のままにしておく。
			os << std::setw(8) << " ";
			// [4-6] GIDs
			for(size_t j = 0; j < 3; ++j){
				os << std::setw(8) << ids[j] + 1; // 1 だけ下駄を履かせる。
			}
			//// [7-10] 空白で埋める。
			//for(size_t j = 0; j < 3; ++j){
			//	os << setw(8) << " ";
			//}
			os << std::endl;
		}
	}

	// 浮動小数点値を 8 桁に収めて出力
	std::ostream& FormatNumericShort(std::ostream& os, double dVal)
	{
		const int nColumn = 8;
		char buf[nColumn + 1] = {0};

		if(dVal < 0){
			sprintf_s(buf, "%-8.3f", dVal);
		}
		else{
			sprintf_s(buf, "%-8.4f", dVal);
		}
		return os << buf;
	}

	//[---1--][---2--][---3--][---4--][---5--][---6--][---7--][---8--][---9--][--10--]
	//GRID    ID      CP      X1      X2      X3      CD      PS      SEID
	void WriteGridsSmall(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& v = vertices[i];

			// GRID
			// ID
			os << "GRID    " << std::setw(8) << i + 1; // 1 だけ下駄を履かせる。

			// CP (blank)
			os << std::setw(8) << " ";

			// X1, X2, X3
			// with offset length
			for(int j = 0; j < 3; ++j){
				FormatNumericShort(os, v[j]);
			}

			// CD, PS, SEID
			//os << std::setw(8 * 4) << " ";
			os << std::endl;
		}
	}

	// 浮動小数点値を 16 桁に収めて出力
	std::ostream& FormatNumericLarge(std::ostream& os, double dVal)
	{
		const int nColumn = 16;
		char buf[nColumn + 1] = {0};

		if(dVal < 0){
			sprintf_s(buf, "%-16.8E", dVal);
		}
		else{
			sprintf_s(buf, "%-16.9E", dVal);
		}
		return os << buf;
	}

	//0       8               24              40              56              72     
	//[---1--][-------2------][-------3------][-------4------][-------5------][---6--]
	//GRID*                123               4            3.25            4.56
	//[---1--][-------2------][-------3------][-------4------][-------5------][---6--]
	//                      2.               8
	void WriteGridsLarge(std::ostream& os, const MGStl& stl)
	{
		os.setf(std::ios_base::showpoint | std::ios_base::scientific | std::ios_base::uppercase);
		const std::vector<MGPosition>& vertices = stl.positions();
		const size_t nVert = vertices.size();
		for(size_t i = 0; i < nVert; ++i){
			const MGPosition& v = vertices[i];

			// GRID*
			// ID
			os << "GRID*   " << std::setw(16) << i + 1; // 1 だけ下駄を履かせる。

			// CP (blank)
			os << std::setw(16) << " ";

			// X1, X2
			// with offset length
			for(int j = 0; j < 2; ++j){
				FormatNumericLarge(os, v[j]);
			}
			// padding
			os << std::setw(8) << " " << std::endl;
			os << std::setw(8) << " ";

			// X3
			// with offset length
			FormatNumericLarge(os, v[2]);

			// CD, PS, SEID
			//os << std::setw(16 * 3 + 8) << " ";
			os << std::endl;
		}
	}

	// 座標データを書く。フォーマットごとに異なる。
	void WriteGrids(
		std::ostream& fout,
		const MGStl& stl,
		FieldType type)
	{
		switch(type){
		case SMALLFIELD:
			WriteGridsSmall(fout, stl);
			break;
		case LARGEFIELD:
			WriteGridsLarge(fout, stl);
			break;
		case FREEFIELD:
			// 対応していない
			break;
		}
	}

	// Nastran ファイルの末尾を出力
	void WriteFooter(std::ostream& os)
	{
		os << "ENDDATA" << std::endl;
	}
}

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

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

	WriteHeader(fout);
	WriteCells(fout, stl);
	WriteGrids(fout, stl, type);
	WriteFooter(fout);

	return true;
}
