/********************************************************************/
/* Copyright (c) 2019 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/********************************************************************/
/********************************************************************/
// GLCrashHandler.cpp
#include "stdafx.h"
#include <strsafe.h>
#include "Misc/GLCrashHandler.h"
#include "GLVersion.h"
#include "fugenCrashDlg.h"

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

/// The default constructor.
///
/// Save the default unhandled exception handler.
CGLCrashHandler::CGLCrashHandler()
	 : m_pfnHandler(::SetUnhandledExceptionFilter(
		 CGLCrashHandler::CustomUnhandledExceptionFilter))
{
}

/// The destructor.
///
/// Restore the unhandled exception handler.
CGLCrashHandler::~CGLCrashHandler()
{
	::SetUnhandledExceptionFilter(m_pfnHandler);
}

// クラッシュハンドラー
LONG WINAPI CGLCrashHandler::CustomUnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionPointers)
{
	CString strStackTrace(_T("Stack trace:\r\n"));

#if FALSE
	CHAR buffer[MAX_PATH + sizeof IMAGEHLP_SYMBOL] = {0};
	PIMAGEHLP_SYMBOL pSym = reinterpret_cast<PIMAGEHLP_SYMBOL>(&buffer[0]);
	pSym->SizeOfStruct = sizeof IMAGEHLP_SYMBOL;
	pSym->MaxNameLength = MAX_PATH;

	// スタックフレームの初期化
	STACKFRAME sf;
	ZeroMemory(&sf, sizeof(sf));
	sf.AddrPC.Offset = pExceptionPointers->ContextRecord->Eip;
	sf.AddrStack.Offset = pExceptionPointers->ContextRecord->Esp;
	sf.AddrFrame.Offset = pExceptionPointers->ContextRecord->Ebp;
	sf.AddrPC.Mode = AddrModeFlat;
	sf.AddrStack.Mode = AddrModeFlat;
	sf.AddrFrame.Mode = AddrModeFlat;

	// シンボルハンドラの初期化
	SymInitialize(GetCurrentProcess(), NULL, TRUE);

	// スタックフレームを順に表示していく
	for(;;) {
		// 次のスタックフレームの取得
		BOOL bResult = StackWalk(
			IMAGE_FILE_MACHINE_I386,
			GetCurrentProcess(),
			GetCurrentThread(),
			&sf,
			NULL, 
			NULL,
			SymFunctionTableAccess,
			SymGetModuleBase,
			NULL);

		// 失敗ならば、ループを抜ける
		if(!bResult || sf.AddrFrame.Offset == 0) break;

		// プログラムカウンタから関数名を取得
		DWORD Disp;
		bResult = SymGetSymFromAddr(
			GetCurrentProcess(), sf.AddrPC.Offset, &Disp, pSym);
		
		// 取得結果を表示
		CString str;
		if(bResult){
			str.Format(_T("0x%08x %s() + 0x%x\r\n"), sf.AddrPC.Offset, pSym->Name, Disp);
		}
		else{
			str.Format(_T("%08x, ---\r\n"), sf.AddrPC.Offset);
		}
		strStackTrace += str;
	}

	/* 後処理 */
	SymCleanup(GetCurrentProcess());
#endif
#if 0
	LPCTSTR szAppName = _T("Fugen"); // 文字列リソース化しない。
	LPCTSTR szVersion = _T(WM_FUGEN_OUTPUT); // CString は使えない
	const DWORD dwBufferSize = MAX_PATH;

	// クラッシュ時刻を取得
	SYSTEMTIME stLocalTime;
	::GetLocalTime(&stLocalTime);

	// 一時ファイルのパスを取得
	TCHAR szPath[MAX_PATH] = {0};
	::GetTempPath(dwBufferSize, szPath);

	// フォルダを作成
	TCHAR szFileName[MAX_PATH] = {0};
	::StringCchPrintf(szFileName, MAX_PATH, _T("%s%s"), szPath, szAppName);
	::CreateDirectory(szFileName, 0);

	// ダンプファイルのフルパスを決定する。
	::StringCchPrintf(
		szFileName, MAX_PATH, _T("%s%s\\%s-%04d%02d%02d-%02d%02d%02d-%ld-%ld.dmp"),
		szPath, szAppName, szVersion,
		stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
		stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond,
		::GetCurrentProcessId(), ::GetCurrentThreadId());

	HANDLE hDumpFile = ::CreateFile(
		szFileName, GENERIC_READ | GENERIC_WRITE,
		FILE_SHARE_WRITE | FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);

	// ダンプファイルを作成

	MINIDUMP_EXCEPTION_INFORMATION ExpParam;
	ExpParam.ThreadId = ::GetCurrentThreadId();
	ExpParam.ExceptionPointers = pExceptionPointers;
	ExpParam.ClientPointers = TRUE;

	BOOL bMiniDumpSuccessful = MiniDumpWriteDump(
		::GetCurrentProcess(), ::GetCurrentProcessId(),
		hDumpFile, MiniDumpWithDataSegs, &ExpParam, 0, 0);

	// ここも文字列をリソース化しない。

	// 8.3 形式だと読みにくいので、パス文字列を「開く」。
	::GetLongPathName(szFileName, szFileName, MAX_PATH);
	
	// TODO: このままだと、ユーザーに手動でメール送信をやらせることになる。
	CString strMsg;
	strMsg.Format(_T("Application crashed. \r\n"
					 "Please send the dump file below to info@systemfugen.com: \r\n"
					 "\r\n"
					 "%s"), szFileName);
	::AfxMessageBox(strMsg, MB_OK | MB_ICONSTOP);
#else
	CString strMsg;

	CString strVer;
	strVer.Format(_T("Version: %s\r\n"), GetVersionText());

	strMsg += strVer;

	strMsg += strStackTrace;

	fugenCrashDlg dlg;
	dlg.SetStackLog(strMsg);
	dlg.DoModal();

	//::AfxMessageBox(strMsg, MB_OK | MB_ICONSTOP);
#endif

	// EXCEPTION_EXECUTE_HANDLER を返すことで、
	// MS 社にメールを送信しないようにする。
	return EXCEPTION_EXECUTE_HANDLER;
}
