/****************************************************************
 *								*
 *	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 "gtm_string.h"

#include "stringpool.h"
#include "stp_parms.h"
#include "gdsroot.h"
#include "gdskill.h"
#include "gdsblk.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsfhead.h"
#include "error.h"
#include "muextr.h"
#include "util.h"
#include "mupip_exit.h"
#include "mlkdef.h"
#include "zshow.h"
#include "file_input.h"
#include "load.h"
#include "mu_gvis.h"
#include "mupip_put_gvdata.h"
#include "mupip_put_gvn_fragment.h"
#include "str2gvkey.h"
#include "gtmmsg.h"
#include "gtm_utf8.h"
#include <rtnhdr.h>
#include "gv_trigger.h"

GBLREF bool	mupip_error_occurred;
GBLREF bool	mu_ctrly_occurred;
GBLREF bool	mu_ctrlc_occurred;
GBLREF spdesc	stringpool;
GBLREF gv_key	*gv_currkey;

error_def(ERR_LOADCTRLY);
error_def(ERR_LOADEOF);
error_def(ERR_LOADFILERR);
error_def(ERR_LOADINVCHSET);
error_def(ERR_MUNOFINISH);
error_def(ERR_TRIGDATAIGNORE);

#define GO_PUT_SUB		0
#define GO_PUT_DATA		1
#define GO_SET_EXTRACT		2
#define DEFAULT_MAX_REC_SIZE 3096
#define ISSUE_TRIGDATAIGNORE_IF_NEEDED(KEYLENGTH, PTR, HASHT_GBL)								\
{																\
	/* The ordering of the && below is important as the caller uses HASHT_GBL to be set to TRUE if the global pointed to 	\
	 * by PTR is ^#t. 													\
	 */															\
	if ((HASHT_GBL = IS_GVKEY_HASHT_FULL_GBLNAME(KEYLENGTH, PTR)) && !hasht_ignored)					\
	{															\
		gtm_putmsg(VARLSTCNT(4) ERR_TRIGDATAIGNORE, 2, KEYLENGTH, PTR);							\
		hasht_ignored = TRUE;												\
	}															\
}

static readonly unsigned char gt_lit[] = "LOAD TOTAL";
void go_call_db(int routine, char *parm1, int parm2, int val_off1, int val_len1);

void go_load(uint4 begin, uint4 end)
{
	char		*ptr;
	int		len, fmt, keylength, keystate;
	uint4	        iter, max_data_len, max_subsc_len, key_count, max_rec_size;
	mstr            src, des;
	unsigned char   *rec_buff, ch;
	boolean_t	utf8_extract, format_error = FALSE, hasht_ignored = FALSE, hasht_gbl = FALSE;
	char		*val_off;
	int 		val_len, val_off1, val_len1;
	boolean_t	is_setextract;

	gvinit();

	max_rec_size = DEFAULT_MAX_REC_SIZE;
	rec_buff = (unsigned char *)malloc(max_rec_size);

	fmt = MU_FMT_ZWR;	/* by default, the extract format is ZWR (not GO) */
	len = file_input_get(&ptr);
	if (mupip_error_occurred)
	{
		free(rec_buff);
		return;
	}
	if (len >= 0)
	{
		util_out_print("!AD", TRUE, len, ptr);
		utf8_extract = ((len >= STR_LIT_LEN(UTF8_NAME)) &&
				(0 == MEMCMP_LIT(ptr + len - STR_LIT_LEN("UTF-8"), "UTF-8"))) ? TRUE : FALSE;
		if ((utf8_extract && !gtm_utf8_mode) || (!utf8_extract && gtm_utf8_mode))
		{ /* extract CHSET doesn't match $ZCHSET */
			if (utf8_extract)
				gtm_putmsg(VARLSTCNT(4) ERR_LOADINVCHSET, 2, LEN_AND_LIT("UTF-8"));
			else
				gtm_putmsg(VARLSTCNT(4) ERR_LOADINVCHSET, 2, LEN_AND_LIT("M"));
			mupip_error_occurred = TRUE;
			free(rec_buff);
			return;
		}
	} else
		mupip_exit(ERR_LOADFILERR);
	len = file_input_get(&ptr);
	if (mupip_error_occurred)
	{
		free(rec_buff);
		return;
	}
	if (len >= 0)
	{
		util_out_print("!AD", TRUE, len, ptr);
		fmt = (0 == memcmp(ptr + len - STR_LIT_LEN("ZWR"), "ZWR", STR_LIT_LEN("ZWR"))) ? MU_FMT_ZWR : MU_FMT_GO;
	} else
		mupip_exit(ERR_LOADFILERR);
	if (begin < 3)
		begin = 3;
	for (iter = 3; iter < begin; iter++)
	{
		len = file_input_get(&ptr);
		if (len < 0)	/* The IO device has signalled an end of file */
		{
			gtm_putmsg(VARLSTCNT(3) ERR_LOADEOF, 1, begin);
			mupip_error_occurred = TRUE;
		}
		if (mupip_error_occurred)
		{
			util_out_print("Error reading record number: !UL\n", TRUE, iter);
			free(rec_buff);
			return;
		}
	}
	assert(iter == begin);
	util_out_print("Beginning LOAD at record number: !UL\n", TRUE, begin);
	max_data_len = 0;
	max_subsc_len = 0;
	key_count = 0;
	for (iter = begin - 1; ; )
	{
		if (++iter > end)
			break;
		if (mu_ctrly_occurred)
			break;
		if (mu_ctrlc_occurred)
		{
			util_out_print("!AD:!_  Key cnt: !UL  max subsc len: !UL  max data len: !UL", TRUE,
				LEN_AND_LIT(gt_lit), key_count, max_subsc_len, max_data_len);
			util_out_print("Last LOAD record number: !UL", TRUE, key_count ? iter : 0);
			mu_gvis();
			util_out_print(0, TRUE);
			mu_ctrlc_occurred = FALSE;
		}
		if (0 > (len = file_input_get(&ptr)))
			break;
		if (mupip_error_occurred)
		{
		        mu_gvis();
			break;
		}
		if ('\n' == *ptr)
		{
			if ('\n' == *(ptr+1))
				break;
			ptr++;
		}
		if (0 == len)
			continue;
		if (MU_FMT_GO != fmt)
		{
			/* Determine if the ZWR has $extract format */
			if ('$' == *ptr)
			{
				keylength = zwrkeyvallen(ptr, len, &val_off, &val_len, &val_off1, &val_len1);
				ptr = ptr + 4; /* Skip first 4 character '$','z','e','(' */
				is_setextract = TRUE;
			} else
			{
				/* Determine the ZWR key length. -1 (SIZEOF(=)) is needed since ZWR allows '^x(1,2)='*/
				keylength = zwrkeyvallen(ptr, len, &val_off, &val_len, NULL, NULL);
				is_setextract = FALSE;
			}
			ISSUE_TRIGDATAIGNORE_IF_NEEDED(keylength, ptr, hasht_gbl);
			if (hasht_gbl)
			{
				hasht_gbl = FALSE;
				continue;
			}
			go_call_db(GO_PUT_SUB, ptr, keylength, 0, 0);
			if (mupip_error_occurred)
			{
			        mu_gvis();
				util_out_print("Error loading record number: !UL\n", TRUE, iter);
				mupip_error_occurred = FALSE;
				continue;
			}
			assert(keylength < len - 1);
			if (max_subsc_len < (gv_currkey->end + 1))
				max_subsc_len = gv_currkey->end + 1;
			src.len = val_len;
			src.addr = val_off;
			des.len = 0;
			if (src.len > max_rec_size)
			{
			        max_rec_size = src.len;
				free(rec_buff);
				rec_buff = (unsigned char *)malloc(max_rec_size);
			}
			des.addr = (char *)rec_buff;
			if (FALSE == zwr2format(&src, &des))
			{
				util_out_print("Format error in record number !8UL: !/!AD", TRUE, iter, src.len, src.addr);
				format_error = TRUE;
				continue;
			}
			if (max_data_len < des.len)
			        max_data_len = des.len;
			(is_setextract) ? go_call_db(GO_SET_EXTRACT, des.addr, des.len, val_off1, val_len1)
					: go_call_db(GO_PUT_DATA, (char *)rec_buff, des.len, 0, 0);
			if (mupip_error_occurred)
			{
			        mu_gvis();
				util_out_print("Error loading record number: !UL\n", TRUE, iter);
				mupip_error_occurred = FALSE;
				continue;
			}
			key_count++;
		} else
		{
			ISSUE_TRIGDATAIGNORE_IF_NEEDED(len, ptr, hasht_gbl);
			if (hasht_gbl)
			{
				if (0 > (len = file_input_get(&ptr)))
					break;
				iter++;
				hasht_gbl = FALSE;
				continue;
			}
		        go_call_db(GO_PUT_SUB, ptr, len, 0, 0);
			if (mupip_error_occurred)
			{
			        mu_gvis();
				util_out_print("Error loading record number: !UL\n", TRUE, iter);
				mupip_error_occurred = FALSE;
				continue;
			}
			if (max_subsc_len < (gv_currkey->end + 1))
				max_subsc_len = gv_currkey->end + 1;
			if (++iter > end)
			{
			        iter--;	/* Decrement as didn't load key */
				break;
			}
			if ((len = file_input_get(&ptr)) < 0)
			        break;
			if (mupip_error_occurred)
			{
			        mu_gvis();
				util_out_print("Error loading record number: !UL\n", TRUE, iter);
				break;
			}
			stringpool.free = stringpool.base;
			if (max_data_len < len)
			        max_data_len = len;
			go_call_db(GO_PUT_DATA, ptr, len, 0, 0);
			if (mupip_error_occurred)
			{
			        mu_gvis();
				util_out_print("Error loading record number: !UL\n", TRUE, iter);
				mupip_error_occurred = FALSE;
				continue;
			}
			key_count++;
		}
	}
	free(rec_buff);
	file_input_close();
	if (mu_ctrly_occurred)
	{
		gtm_putmsg(VARLSTCNT(1) ERR_LOADCTRLY);
		mupip_exit(ERR_MUNOFINISH);
	}
	util_out_print("LOAD TOTAL!_!_Key Cnt: !UL  Max Subsc Len: !UL  Max Data Len: !UL",TRUE,key_count,max_subsc_len,
			max_data_len);
	util_out_print("Last LOAD record number: !UL\n", TRUE, key_count ? (iter - 1) : 0);
	if (format_error)
		mupip_exit(ERR_LOADFILERR);
}

void go_call_db(int routine, char *parm1, int parm2, int val_off1, int val_len1)
{
	/* In order to duplicate the VMS functionality, which is to trap all errors in mupip_load_ch
	 * and continue in go_load after they occur, it is necessary to call these routines from a
	 * subroutine due to the limitations of condition handlers and unwinding on UNIX.
	 */
	ESTABLISH(mupip_load_ch);
	switch(routine)
	{	case GO_PUT_SUB:
			gv_currkey->end = 0;
			str2gvkey_gvfunc(parm1, parm2);
			break;
		case GO_PUT_DATA:
			mupip_put_gvdata(parm1, parm2);
			break;
		case GO_SET_EXTRACT:
			mupip_put_gvn_fragment(parm1, parm2, val_off1, val_len1);
			break;
	}
	REVERT;
}
