#include "CrashHandler.h"
#include <stdio.h>
#include <iostream>
#include <memory>
#include <signal.h>
#include <dbgHelp.h>
#include <new.h>
#include <Windows.h>
#include <tchar.h>
#include <fstream>

//#define GLOG_NO_ABBREVIATED_SEVERITIES
#include "glog/logging.h"
void CCrashHandler::SetProcessExceptionHandlers()
{
	// Install top-level SEH handler
	SetUnhandledExceptionFilter(SehHandler);

	// Catch pure virtual function calls.
	// Because there is one _purecall_handler for the whole process, 
	// calling this function immediately impacts all threads. The last 
	// caller on any thread sets the handler. 
	// http://msdn.microsoft.com/en-us/library/t296ys27.aspx
	_set_purecall_handler(PureCallHandler);

	// Catch new operator memory allocation exceptions
	_set_new_handler(NewHandler);

	// Catch invalid parameter exceptions.
	_set_invalid_parameter_handler(InvalidParameterHandler);

	// Set up C++ signal handlers

	_set_abort_behavior(_CALL_REPORTFAULT, _CALL_REPORTFAULT);

	// Catch an abnormal program termination
	signal(SIGABRT, SigabrtHandler);

	// Catch illegal instruction handler
	signal(SIGINT, SigintHandler);

	// Catch a termination request
	signal(SIGTERM, SigtermHandler);
}

void CCrashHandler::SetThreadExceptionHandlers()
{

	// Catch terminate() calls. 
	// In a multithreaded environment, terminate functions are maintained 
	// separately for each thread. Each new thread needs to install its own 
	// terminate function. Thus, each thread is in charge of its own termination handling.
	// http://msdn.microsoft.com/en-us/library/t6fk7h29.aspx
	set_terminate(TerminateHandler);

	// Catch unexpected() calls.
	// In a multithreaded environment, unexpected functions are maintained 
	// separately for each thread. Each new thread needs to install its own 
	// unexpected function. Thus, each thread is in charge of its own unexpected handling.
	// http://msdn.microsoft.com/en-us/library/h46t5b69.aspx  
	set_unexpected(UnexpectedHandler);

	// Catch a floating point error
	typedef void(*sigh)(int);
	signal(SIGFPE, (sigh)SigfpeHandler);

	// Catch an illegal instruction
	signal(SIGILL, SigillHandler);

	// Catch illegal storage access errors
	signal(SIGSEGV, SigsegvHandler);
}

// This method creates minidump of the process
void CCrashHandler::WriteCrashDumpFile(_EXCEPTION_POINTERS* pExInfo, _MINIDUMP_TYPE WriteFlag)
{
	// WinDbg
	// analyze -v 自动分析
	// .ecxr  转到源码
	if (pExInfo == NULL)
		RaiseException(EXCEPTION_BREAKPOINT, 0, 0, NULL);

    // 创建 dmp 文件件
    char szFileName[MAX_PATH] = { 0 };
    char szVersion[] = _T("dump//DumpFile");
    SYSTEMTIME stLocalTime;
    GetLocalTime(&stLocalTime);
    sprintf(szFileName, _T("%s-%04d%02d%02d-%02d%02d%02d.dmp"),
        szVersion, stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
        stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond);

	MINIDUMP_EXCEPTION_INFORMATION dumpInfo;
	dumpInfo.ThreadId = ::GetCurrentThreadId();
	dumpInfo.ExceptionPointers = pExInfo;
	dumpInfo.ClientPointers = TRUE;
	HANDLE hCrashFile = ::CreateFileA(szFileName, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hCrashFile != INVALID_HANDLE_VALUE)
	{
		BOOL bWrite = ::MiniDumpWriteDump(::GetCurrentProcess(), ::GetCurrentProcessId(), hCrashFile, WriteFlag, &dumpInfo, NULL, NULL);
		if (bWrite)
			::FlushFileBuffers(hCrashFile);
	}

	CloseHandle(hCrashFile);
	hCrashFile = NULL;
}

void CCrashHandler::print_backtrace(const char* strErrInfo)
{
    const int kMaxCallers = 162;
	void* callers[kMaxCallers]={0};
    int count = CaptureStackBackTrace(0, kMaxCallers, callers, nullptr);
    SYMBOL_INFO* symbol = (SYMBOL_INFO*)malloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char));
    symbol->MaxNameLen = 255;
    symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
    HANDLE process = GetCurrentProcess();
    SymInitialize(process, nullptr, TRUE);

    char szFileName[MAX_PATH] = { 0 };
    char szVersion[] = "dump//Crash-";
    SYSTEMTIME stLocalTime;
    GetLocalTime(&stLocalTime);
    sprintf(szFileName, "%s-%04d%02d%02d-%02d%02d%02d.txt",
        szVersion, stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
        stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond);

    std::ofstream fout(szFileName, std::ios::app);
    if (!fout)
    {
        SymCleanup(process);
        free(symbol);
        return;
    }

	fout<<"Error:"<<strErrInfo<<"\n";

    for (int i = 0; i < count; i++)
    {
        SymFromAddr(process, (DWORD64)(callers[i]), nullptr, symbol);
        
        IMAGEHLP_LINE lineInfo = { sizeof(IMAGEHLP_LINE) };
        DWORD dwLineDisplacement;
        if (SymGetLineFromAddr(process, (DWORD64)(callers[i]), &dwLineDisplacement, &lineInfo))
        {
            fout << "#" << i << " " << symbol->Name << "()"
                << " File:" << lineInfo.FileName
                << " Line:" << lineInfo.LineNumber << "\n";
        }
        else
        {
            fout << "#" << i << " " << symbol->Name << "()" << "\n";
        }
    }
    fout.close();

    SymCleanup(process);
    free(symbol);
}

LONG WINAPI CCrashHandler::SehHandler(_EXCEPTION_POINTERS* lpExceptionInfo)
{
	static bool bWriteDump = false;
	if (!bWriteDump)
	{
		bWriteDump = true;
		WriteCrashDumpFile(lpExceptionInfo, MiniDumpWithFullMemory);
	}

	AlarmForAppCrash("SehHandler");
	return EXCEPTION_EXECUTE_HANDLER;
}

void __cdecl CCrashHandler::TerminateHandler()
{
	AlarmForAppCrash("TerminateHandler");
}

void __cdecl CCrashHandler::UnexpectedHandler()
{
	AlarmForAppCrash("UnexpectedHandler");
}

void __cdecl CCrashHandler::PureCallHandler()
{
	AlarmForAppCrash("PureCallHandler");
}

void __cdecl CCrashHandler::InvalidParameterHandler(const wchar_t* expression, const wchar_t* function, const wchar_t* file, unsigned int line, uintptr_t pReserved)
{
	AlarmForAppCrash("InvalidParameterHandler");
}

int __cdecl CCrashHandler::NewHandler(size_t)
{
	AlarmForAppCrash("NewHandler");
	return 0;
}

void CCrashHandler::SigabrtHandler(int)
{
	AlarmForAppCrash("SigabrtHandler");
}

void CCrashHandler::SigfpeHandler(int /*code*/, int subcode)
{
	AlarmForAppCrash("SigfpeHandler");
}

void CCrashHandler::SigintHandler(int)
{
	AlarmForAppCrash("SigintHandler");
}

void CCrashHandler::SigillHandler(int)
{
	AlarmForAppCrash("SigillHandler");
}

void CCrashHandler::SigsegvHandler(int)
{
	AlarmForAppCrash("SigsegvHandler");
}

void CCrashHandler::SigtermHandler(int)
{
	AlarmForAppCrash("SigtermHandler");
}

void CCrashHandler::AlarmForAppCrash(const char* strErrInfo)
{
    print_backtrace(strErrInfo);

	LOG(ERROR)<<"Crash STOP";
    MessageBoxA(NULL, _T("程序异常，请确认工艺完成后点确定，并重启程序,点击确定将尝试关闭激光器"), _T(""), MB_OK);

	//关闭激光器
	LOG(ERROR)<<"Error shut down laser";
	CLaserDriver_SP* pLaserDriver = GET_CARD(LASER_DRIVER, CLaserDriver_SP);
	bool bClosed = false;
	try{
		pLaserDriver->OpenLaserGate(LASERPATH_D_NARROW,FALSE);
	}catch(const QString& ioEx){
		LOG(ERROR)<<"OpenLaserGate FALSE failed"<<ioEx.toStdString().c_str();
	}
	try{
		pLaserDriver->SetInnerGate(LASER, false);
		bClosed = true;
	}
	catch(const QString& laserEx){
		LOG(ERROR)<<"SetInnerGate failed"<<laserEx.toStdString().c_str();
	}
}


int GenerateMiniDump(PEXCEPTION_POINTERS pExceptionPointers)
{
    // 定义函数指针
    typedef BOOL(WINAPI * MiniDumpWriteDumpT)(
        HANDLE,
        DWORD,
        HANDLE,
        MINIDUMP_TYPE,
        PMINIDUMP_EXCEPTION_INFORMATION,
        PMINIDUMP_USER_STREAM_INFORMATION,
        PMINIDUMP_CALLBACK_INFORMATION
        );
    // 从 "DbgHelp.dll" 库中获取 "MiniDumpWriteDump" 函数
    MiniDumpWriteDumpT pfnMiniDumpWriteDump = NULL;
    HMODULE hDbgHelp = LoadLibraryA(_T("DbgHelp.dll"));
    if (NULL == hDbgHelp)
    {
        return EXCEPTION_CONTINUE_EXECUTION;
    }
    pfnMiniDumpWriteDump = (MiniDumpWriteDumpT)GetProcAddress(hDbgHelp, "MiniDumpWriteDump");

    if (NULL == pfnMiniDumpWriteDump)
    {
        FreeLibrary(hDbgHelp);
        return EXCEPTION_CONTINUE_EXECUTION;
    }
    // 创建 dmp 文件件
    char szFileName[MAX_PATH] = { 0 };
    char szVersion[] = _T("DumpFile");
    SYSTEMTIME stLocalTime;
    GetLocalTime(&stLocalTime);
    sprintf(szFileName, _T("%s-%04d%02d%02d-%02d%02d%02d.dmp"),
        szVersion, stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
        stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond);
    HANDLE hDumpFile = CreateFileA(szFileName, GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_WRITE | FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);
    if (INVALID_HANDLE_VALUE == hDumpFile)
    {
        FreeLibrary(hDbgHelp);
        return EXCEPTION_CONTINUE_EXECUTION;
    }
    // 写入 dmp 文件
    MINIDUMP_EXCEPTION_INFORMATION expParam;
    expParam.ThreadId = GetCurrentThreadId();
    expParam.ExceptionPointers = pExceptionPointers;
    expParam.ClientPointers = FALSE;
    pfnMiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
        hDumpFile, MiniDumpWithDataSegs, (pExceptionPointers ? &expParam : NULL), NULL, NULL);
    // 释放文件
    CloseHandle(hDumpFile);
    FreeLibrary(hDbgHelp);
    return EXCEPTION_EXECUTE_HANDLER;
}

LONG WINAPI ExceptionFilter(LPEXCEPTION_POINTERS lpExceptionInfo)
{
    // 这里做一些异常的过滤或提示
    if (IsDebuggerPresent())
    {
        return EXCEPTION_CONTINUE_SEARCH;
    }

    return GenerateMiniDump(lpExceptionInfo);
}
