/**************************************************************
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 *************************************************************/




/* system headers */
#include "system.h"

#include <osl/diagnose.h>
#include <osl/mutex.h>
#include <osl/signal.h>

typedef struct _oslSignalHandlerImpl
{
	oslSignalHandlerFunction      Handler;
	void*			        	  pData;
	struct _oslSignalHandlerImpl* pNext;
} oslSignalHandlerImpl;

static sal_Bool				  bErrorReportingEnabled = sal_True;
static sal_Bool  			  bInitSignal = sal_False;
static oslMutex 			  SignalListMutex;
static oslSignalHandlerImpl*  SignalList;

/*static*//* ULONG _Export APIENTRY SignalHandlerFunction(PEXCEPTIONREPORTRECORD pERepRec,
                                            PEXCEPTIONREGISTRATIONRECORD,
                                            PCONTEXTRECORD, PVOID);
*/
/*static*/ ULONG __declspec(dllexport) APIENTRY SignalHandlerFunction(PEXCEPTIONREPORTRECORD pERepRec,
                                            PEXCEPTIONREGISTRATIONRECORD,
                                            PCONTEXTRECORD, PVOID);
static EXCEPTIONREGISTRATIONRECORD ExcptHandler = { 0, SignalHandlerFunction };

static sal_Bool InitSignal( void )
{
	SignalListMutex = osl_createMutex();

	ExcptHandler.ExceptionHandler = (_ERR *) &SignalHandlerFunction;
	/* DosSetExceptionHandler(&ExcptHandler); */

	return sal_True;
}

static sal_Bool DeInitSignal( void )
{
	/* DosUnsetExceptionHandler(&ExcptHandler); */

	osl_destroyMutex(SignalListMutex);

	return sal_False;
}

static oslSignalAction CallSignalHandler(oslSignalInfo *pInfo)
{
	oslSignalHandlerImpl* pHandler = SignalList;
	oslSignalAction Action = osl_Signal_ActCallNextHdl;

	while (pHandler != NULL)
	{
		if ((Action = pHandler->Handler(pHandler->pData, pInfo)) != osl_Signal_ActCallNextHdl)
			break;

		pHandler = pHandler->pNext;
	}

	return Action;
}

/*****************************************************************************/
/* SignalHandlerFunction	*/
/*****************************************************************************/
/*static*/ ULONG __declspec(dllexport) APIENTRY SignalHandlerFunction(PEXCEPTIONREPORTRECORD pERepRec,
                                            PEXCEPTIONREGISTRATIONRECORD pERegRec,
                                            PCONTEXTRECORD pConRec, PVOID pReserved)
{
	oslSignalInfo	 Info;

	Info.UserSignal = pERepRec->ExceptionNum;
	Info.UserData   = NULL;

	switch (pERepRec->ExceptionNum)
	{
		case XCPT_ACCESS_VIOLATION:
			Info.Signal = osl_Signal_AccessViolation;
			break;

		case XCPT_INTEGER_DIVIDE_BY_ZERO:
			Info.Signal = osl_Signal_IntegerDivideByZero;
			break;

		case XCPT_BREAKPOINT:
			Info.Signal = osl_Signal_DebugBreak;
			break;

		default:
			Info.Signal = osl_Signal_System;
			break;
	}

	switch (CallSignalHandler(&Info))
	{
		case osl_Signal_ActCallNextHdl:
			return (XCPT_CONTINUE_SEARCH);

		case osl_Signal_ActAbortApp:
			return (XCPT_CONTINUE_SEARCH);

		case osl_Signal_ActKillApp:
			exit(255);
			break;
	}

	return (XCPT_CONTINUE_SEARCH);
}

/*****************************************************************************/
/* osl_addSignalHandler */
/*****************************************************************************/
oslSignalHandler SAL_CALL osl_addSignalHandler(oslSignalHandlerFunction Handler, void* pData)
{
	oslSignalHandlerImpl* pHandler;

	OSL_ASSERT(Handler != NULL);

	if (! bInitSignal)
		bInitSignal = InitSignal();

	pHandler = (oslSignalHandlerImpl*) calloc(1, sizeof(oslSignalHandlerImpl));

	if (pHandler != NULL)
	{
		pHandler->Handler = Handler;
		pHandler->pData   = pData;

		osl_acquireMutex(SignalListMutex);

		pHandler->pNext = SignalList;
		SignalList      = pHandler;

		osl_releaseMutex(SignalListMutex);

		return (pHandler);
	}

	return (NULL);
}

/*****************************************************************************/
/* osl_removeSignalHandler */
/*****************************************************************************/
sal_Bool SAL_CALL osl_removeSignalHandler(oslSignalHandler Handler)
{
	oslSignalHandlerImpl *pHandler, *pPrevious = NULL;

	OSL_ASSERT(Handler != NULL);

	if (! bInitSignal)
		bInitSignal = InitSignal();

	osl_acquireMutex(SignalListMutex);

	pHandler = SignalList;

	while (pHandler != NULL)
	{
		if (pHandler == Handler)
		{
			if (pPrevious)
				pPrevious->pNext = pHandler->pNext;
			else
				SignalList = pHandler->pNext;

			osl_releaseMutex(SignalListMutex);

			if (SignalList == NULL )
				bInitSignal = DeInitSignal();

			free(pHandler);

			return (sal_True);
		}

		pPrevious = pHandler;
		pHandler  = pHandler->pNext;
	}

	osl_releaseMutex(SignalListMutex);

	return (sal_False);
}

/*****************************************************************************/
/* osl_raiseSignal */
/*****************************************************************************/
oslSignalAction SAL_CALL osl_raiseSignal(sal_Int32 UserSignal, void* UserData)
{
	oslSignalInfo   Info;
	oslSignalAction Action;

	if (! bInitSignal)
		bInitSignal = InitSignal();

	osl_acquireMutex(SignalListMutex);

	Info.Signal     = osl_Signal_User;
	Info.UserSignal = UserSignal;
	Info.UserData   = UserData;

	Action = CallSignalHandler(&Info);

	osl_releaseMutex(SignalListMutex);

	return (Action);
}

/*****************************************************************************/
/* osl_setErrorReporting */
/*****************************************************************************/
sal_Bool SAL_CALL osl_setErrorReporting( sal_Bool bEnable )
{
	sal_Bool bOld = bErrorReportingEnabled;
	bErrorReportingEnabled = bEnable;

	return bOld;
}
