/****************************************************************
 *								*
 *	Copyright 2001, 2012 Fidelity Information Services, Inc	*
 *								*
 *	This source code contains the intellectual property	*
 *	of its copyright holder(s), and is made available	*
 *	under a license.  If you do not know the terms of	*
 *	the license, please stop and do not read further.	*
 *								*
 ****************************************************************/

#include "mdef.h"
#include <stdarg.h>
#include "gtm_string.h"
#include "cmd_qlf.h"
#include "compiler.h"
#include "opcode.h"
#include "cgp.h"
#include "io.h"
#include "list_file.h"
#include "gtmmsg.h"
#include "util_format.h"
#include "show_source_line.h"
#ifdef UNICODE_SUPPORTED
#include "gtm_utf8.h"
#endif

GBLREF char 			source_file_name[];
GBLREF unsigned char 		*source_buffer;
GBLREF short int 		source_name_len, source_line;
GBLREF command_qualifier	cmd_qlf;
GBLREF bool 			dec_nofac;
GBLREF boolean_t		run_time;
GBLREF char			cg_phase;
GBLREF io_pair			io_curr_device, io_std_device;

error_def(ERR_ACTLSTTOOLONG);
error_def(ERR_BADCASECODE);
error_def(ERR_BADCHAR);
error_def(ERR_BADCHSET);
error_def(ERR_CEBIGSKIP);
error_def(ERR_CENOINDIR);
error_def(ERR_CETOOLONG);
error_def(ERR_CETOOMANY);
error_def(ERR_CEUSRERROR);
error_def(ERR_FMLLSTMISSING);
error_def(ERR_FMLLSTPRESENT);
error_def(ERR_FOROFLOW);
error_def(ERR_INVDLRCVAL);
error_def(ERR_LABELMISSING);
error_def(ERR_SRCLIN);
error_def(ERR_SRCLOC);
error_def(ERR_SRCNAM);

void stx_error(int in_error, ...)
{
	va_list		args;
	VA_ARG_TYPE	cnt, arg1, arg2, arg3, arg4;
	bool		list, warn;
	char		msgbuf[MAX_SRCLINE];
	char		buf[MAX_SRCLINE + LISTTAB + SIZEOF(ARROW)];
	char		*c;
	mstr		msg;
	boolean_t	is_stx_warn;	/* current error is actually of type warning and we are in CGP_PARSE phase */
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	va_start(args, in_error);
	/* In case of a IS_STX_WARN type of parsing error, we resume parsing so it is important NOT to reset
	 * the following global variables
	 *	a) saw_side_effect
	 *	b) shift_side_effects
	 *	c) source_error_found
	 */
	is_stx_warn = (CGP_PARSE == cg_phase) && IS_STX_WARN(in_error) GTMTRIG_ONLY( && !TREF(trigger_compile));
	if (!is_stx_warn)
		TREF(saw_side_effect) = TREF(shift_side_effects) = FALSE;
	if (run_time)
	{	/* If the current error is of type STX_WARN then do not issue an error at compile time. Insert
		 * triple to issue the error at runtime. If and when this codepath is reached at runtime (M command
		 * could have a postconditional that bypasses this code) issue the rts_error.
		 * See IS_STX_WARN macro definition for details.
		 */
		if (is_stx_warn)
		{	/* merrors.msg defines INVCMD as a warning but compiler conditions can turn it into an error */
			if (ERR_INVCMD != in_error)	/* if INVCMD has morphed into an error, it won't match here */
				ins_errtriple(in_error);
			return;
		}
		if (TREF(for_stack_ptr) > (oprtype **)TADR(for_stack))
			FOR_POP(BLOWN_FOR);
		if (ERR_BADCHAR == in_error)
		{
			cnt = va_arg(args, VA_ARG_TYPE);
			assert(cnt == 4);
			arg1 = va_arg(args, VA_ARG_TYPE);
			arg2 = va_arg(args, VA_ARG_TYPE);
			arg3 = va_arg(args, VA_ARG_TYPE);
			arg4 = va_arg(args, VA_ARG_TYPE);
			va_end(args);
			rts_error(VARLSTCNT(6) in_error, cnt, arg1, arg2, arg3, arg4);
		} else if ((ERR_LABELMISSING == in_error)
			|| (ERR_FMLLSTMISSING == in_error)
			|| (ERR_ACTLSTTOOLONG == in_error)
			|| (ERR_BADCHSET == in_error)
			|| (ERR_BADCASECODE == in_error))
		{
			cnt = va_arg(args, VA_ARG_TYPE);
			assert(cnt == 2);
			arg1 = va_arg(args, VA_ARG_TYPE);
			arg2 = va_arg(args, VA_ARG_TYPE);
			va_end(args);
			rts_error(VARLSTCNT(4) in_error, cnt, arg1, arg2);
		} else if ((ERR_CEUSRERROR == in_error) || (ERR_INVDLRCVAL == in_error) || (ERR_FOROFLOW == in_error))
		{
			cnt = va_arg(args, VA_ARG_TYPE);
			assert(cnt == 1);
			arg1 = va_arg(args, VA_ARG_TYPE);
			va_end(args);
			rts_error(VARLSTCNT(3) in_error, cnt, arg1);
		} else
		{
			va_end(args);
			rts_error(VARLSTCNT(1) in_error);
		}
	} else if ((CGP_PARSE == cg_phase) && (ERR_INVCMD != in_error))	/* if INVCMD has morphed into an error, won't match here */
		ins_errtriple(in_error);
	assert(!run_time);	/* From here on down, should never go ahead with printing compile-error while in run_time */
	flush_pio();
	if (TREF(source_error_found))
	{
		va_end(args);
		return;
	}
	if (!is_stx_warn
		&& (ERR_CETOOMANY != in_error)	/* compiler escape errors shouldn't hide others */
		&& (ERR_CEUSRERROR!= in_error)
		&& (ERR_CEBIGSKIP != in_error)
		&& (ERR_CETOOLONG != in_error)
		&& (ERR_CENOINDIR != in_error))
	{
		TREF(source_error_found) = (int4)in_error;
	}
	list = (cmd_qlf.qlf & CQ_LIST) != 0;
	warn = (cmd_qlf.qlf & CQ_WARNINGS) != 0;
	if (!warn && !list) /*SHOULD BE MESSAGE TYPE IS WARNING OR LESS*/
	{
		va_end(args);
		return;
	}
	if (list && (io_curr_device.out == io_std_device.out))
		warn = FALSE;		/* if listing is going to $P, don't double output */
	if (ERR_BADCHAR == in_error)
	{
		memset(buf, ' ', LISTTAB);
		show_source_line(&buf[LISTTAB], SIZEOF(buf), warn);
		cnt = va_arg(args, VA_ARG_TYPE);
		assert(cnt == 4);
		arg1 = va_arg(args, VA_ARG_TYPE);
		arg2 = va_arg(args, VA_ARG_TYPE);
		arg3 = va_arg(args, VA_ARG_TYPE);
		arg4 = va_arg(args, VA_ARG_TYPE);
		if (warn)
		{
			dec_err(VARLSTCNT(6) in_error, 4, arg1, arg2, arg3, arg4);
			dec_err(VARLSTCNT(4) ERR_SRCNAM, 2, source_name_len, source_file_name);
		}
		if (list)
			list_line(buf);
		arg1 = arg2 = arg3 = arg4 = 0;
	} else if ((ERR_LABELMISSING == in_error)
		|| (ERR_FMLLSTMISSING == in_error)
		|| (ERR_ACTLSTTOOLONG == in_error)
		|| (ERR_BADCHSET == in_error)
		|| (ERR_BADCASECODE == in_error))
	{
		cnt = va_arg(args, VA_ARG_TYPE);
		assert(cnt == 2);
		arg1 = va_arg(args, VA_ARG_TYPE);
		arg2 = va_arg(args, VA_ARG_TYPE);
		if (warn)
		{
			dec_err(VARLSTCNT(4) in_error, 2, arg1, arg2);
			dec_err(VARLSTCNT(4) ERR_SRCNAM, 2, source_name_len, source_file_name);
		}
	} else
	{
		memset(buf, ' ', LISTTAB);
		show_source_line(&buf[LISTTAB], SIZEOF(buf), warn);
		if (warn)
		{
			if ((ERR_CEUSRERROR != in_error) && (ERR_INVDLRCVAL != in_error) && (ERR_FOROFLOW != in_error))
				dec_err(VARLSTCNT(1) in_error);
			else
			{
				cnt = va_arg(args, VA_ARG_TYPE);
				assert(cnt == 1);
				arg1 = va_arg(args, VA_ARG_TYPE);
				dec_err(VARLSTCNT(3) in_error, 1, arg1);
			}
		}
		if (list)
			list_line(buf);
		arg1 = arg2 = 0;
	}
	va_end(args);
	if (list)
	{
		va_start(args, in_error);
		msg.addr = msgbuf;
		msg.len = SIZEOF(msgbuf);
		gtm_getmsg(in_error, &msg);
		assert(msg.len);
#		ifdef UNIX
		cnt = va_arg(args, VA_ARG_TYPE);
		c = util_format(msgbuf, args, LIT_AND_LEN(buf), (int)cnt);
		va_end(TREF(last_va_list_ptr));	/* set by util_format */
#		else
		c = util_format(msgbuf, args, LIT_AND_LEN(buf));
#		endif
		va_end(args);
		*c = 0;
		list_line(buf);
	}
}
