/****************************************************************
 *								*
 * Copyright (c) 2001-2020 Fidelity National Information	*
 * Services, Inc. and/or its subsidiaries. All rights reserved.	*
 *								*
 *	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 "gdsroot.h"
#include "gdsblk.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsfhead.h"
#include "rc.h"
#include "cdb_sc.h"
#include "rc_oflow.h"
#include "copy.h"
#include "error.h"
#include "gtcm.h"
#include "t_end.h"
#include "t_retry.h"
#include "t_begin.h"
#include "gvcst_protos.h"	/* for gvcst_search,gvcst_rtsib,gvcst_lftsib prototype */
#include "hashtab_mname.h"

GBLREF int		rc_size_return;
GBLREF gd_addr		*gd_header;
GBLREF gv_key 		*gv_currkey;
GBLREF gv_namehead 	*gv_target;
GBLREF sgmnt_data	*cs_data;
GBLREF sgmnt_addrs	*cs_addrs;
GBLREF rc_oflow		*rc_overflow;
GBLREF gd_region	*gv_cur_region;
GBLREF trans_num	rc_read_stamp;
GBLREF int		gv_keysize;

error_def(ERR_GVGETFAIL);

int rc_prc_getr(rc_q_hdr *qhdr)
{
	int		key_size, data_len, i;
	bool		dollar_order, two_histories;
	char		*cp2, *cp1;
	mname_entry	gvname;
	short		rsiz, bsiz, fmode, size_return;
	rec_hdr		*rp;
	blk_hdr		*bp;
	srch_hist	second_history;
	enum cdb_sc	status;
	rc_req_getr	*req;
	rc_rsp_page	*rsp;
	srch_blk_status	*bh;
	gvnh_reg_t	*gvnh_reg;
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	ESTABLISH_RET(rc_dbms_ch,0);
	req = (rc_req_getr *)qhdr;
	rsp = (rc_rsp_page *)qhdr;
	rsp->hdr.a.len.value = (short)((char*)(&rsp->page[0]) - (char*)rsp);

	fmode = qhdr->r.fmd.value;
	if ((qhdr->a.erc.value = rc_fnd_file(&qhdr->r.xdsid)) != RC_SUCCESS)
	{
		REVERT;
		DEBUG_ONLY(gtcm_cpktdmp((char *)qhdr,qhdr->a.len.value,"rc_fnd_file failed.");)
		return -1;
	}
	if (req->key.len.value > cs_data->max_key_size)
	{
		qhdr->a.erc.value = RC_KEYTOOLONG;
	 	rsp->size_return.value = 0;
		rsp->size_remain.value = 0;
		rsp->rstatus.value = 0;
		REVERT;
		return 0;
	}
	cp2 = req->key.key + req->key.len.value;
	for (cp1 = req->key.key; *cp1 && cp1 < cp2; cp1++)
		;
	gvname.var_name.len = INTCAST(cp1 - req->key.key);
	gvname.var_name.addr = req->key.key;
	if (gvname.var_name.len > MAX_MIDENT_LEN)	/* GT.M does not support global variables > MAX_MIDENT_LEN chars */
	{
		if (!(gvname.var_name.len == (MAX_MIDENT_LEN + 1) && gvname.var_name.addr[MAX_MIDENT_LEN] == 0x01))
		{
		    qhdr->a.erc.value = RC_KEYTOOLONG;
		    rsp->size_return.value = 0;
		    rsp->size_remain.value = 0;
		    rsp->rstatus.value = 0;
		    REVERT;
		    DEBUG_ONLY(gtcm_cpktdmp((char *)qhdr,qhdr->a.len.value,"RC_KEYTOOLONG.");)
		    return -1;
		}
	}
	COMPUTE_HASH_MNAME(&gvname);
	GV_BIND_NAME_AND_ROOT_SEARCH(gd_header, &gvname, gvnh_reg);
	assert(NULL == gvnh_reg->gvspan); /* so GV_BIND_SUBSNAME_IF_GVSPAN is not needed */
	memcpy(gv_currkey->base, req->key.key, req->key.len.value);
	gv_currkey->end = req->key.len.value;
	dollar_order = FALSE;
	if (gv_currkey->base[gv_currkey->end - 1] == 0xFF)
	{	TREF(gv_last_subsc_null) = TRUE;			/* Trailing null subscript has NO trailing zero */
		gv_currkey->base[gv_currkey->end] = 0;
		gv_currkey->end += 1;
	}else
	{	TREF(gv_last_subsc_null) = FALSE;
		if (gv_currkey->base[gv_currkey->end - 1] == 0x01)
		{	dollar_order = TRUE;
			gv_currkey->base[gv_currkey->end] = 0;
			gv_currkey->end += 1;
		}
	}
	if (!gv_target->root && (cp1 != cp2))
	{
		qhdr->a.erc.value = RC_MUMERRUNDEFVAR;
 		rsp->size_return.value = 0;
		rsp->size_remain.value = 0;
		rsp->rstatus.value = 0;
		REVERT;
		return 0;
	}
	gv_currkey->base[gv_currkey->end] = 0;
	for (i = gv_currkey->end - 2; i > 0; i--)
		if (!gv_currkey->base[i - 1])
			break;
	gv_currkey->prev = i;

	if (fmode & RC_MODE_NEXT)
	{
		if (TREF(gv_last_subsc_null))
			gv_currkey->base[gv_currkey->prev] = 01;
		else
		{
			if (dollar_order)
			{
				gv_currkey->end++;
				gv_currkey->base[gv_currkey->end] = 0;
			} else
			{
				GVKEY_INCREMENT_QUERY(gv_currkey);
			}
		}
	}
	else if (fmode & RC_MODE_PREV)
	{
		if (TREF(gv_last_subsc_null))
		{
			*(&gv_currkey->base[0] + gv_currkey->prev + 1) = 0xFF;
			*(&gv_currkey->base[0] + gv_currkey->end + 1) = 0;
			gv_currkey->end += 1;
		}
	}
	rsp->size_return.value = 0;
	if (dollar_order && (cp1 == cp2))
	{
		rc_gbl_ord(rsp);
		REVERT;
		return 0;
	}
	if (!gv_target->root)
	{
		qhdr->a.erc.value = RC_BADXBUF;
 		rsp->size_return.value = 0;
		rsp->size_remain.value = 0;
		rsp->rstatus.value = 0;
		REVERT;
		return 0;
	}

	t_begin(ERR_GVGETFAIL, 0);
	for (;;)
	{
		rsp->hdr.a.len.value = (short)((char*)(&rsp->page[0]) - (char*)rsp);
		size_return = rc_size_return;
		two_histories = FALSE;
		if ((status = gvcst_search(gv_currkey, NULL)) == cdb_sc_normal)
		{
			bh = gv_target->hist.h;
			if (fmode & RC_MODE_NEXT)	/* if getnext */
			{
				rp = (rec_hdr *)(bh->buffaddr + bh->curr_rec.offset);
				bp = (blk_hdr *)bh->buffaddr;
				if (rp >= (rec_hdr *)CST_TOB(bp))
				{
					two_histories = TRUE;
					status = gvcst_rtsib(&second_history, 0);
					if (status == cdb_sc_normal)
						bh = second_history.h;
					else
						if (status == cdb_sc_endtree)
							two_histories = FALSE;	/* second history not valid */
						else
							goto restart;
				}
			}
			else
				if (fmode & RC_MODE_PREV)	/* if get previous */
				{
					if (bh->prev_rec.offset == 0)
					{
						two_histories = TRUE;
						status = gvcst_lftsib(&second_history);
						if (status == cdb_sc_normal)
							bh = second_history.h;
						else
							if (status == cdb_sc_endtree)
								two_histories = FALSE;	/* second history not valid */
							else
								goto restart;
					}
				}
			if ((bsiz = ((blk_hdr *)(bh->buffaddr))->bsiz + RC_BLKHD_PAD) > SIZEOF(blk_hdr) + RC_BLKHD_PAD)
			{	/* Non-empty block, global exists */
				if (bsiz > size_return)
					rc_overflow->size = bsiz - size_return;
				else
				{
					rc_overflow->size = 0;
					size_return = bsiz;
				}

				memcpy(rsp->page, bh->buffaddr, SIZEOF(blk_hdr));
				PUT_SHORT(&((blk_hdr*)rsp->page)->bsiz,bsiz);
				memcpy(rsp->page + SIZEOF(blk_hdr) + RC_BLKHD_PAD, bh->buffaddr + SIZEOF(blk_hdr),
					  size_return - (SIZEOF(blk_hdr) + RC_BLKHD_PAD));
				rsp->size_return.value = size_return;
				rsp->hdr.a.len.value += rsp->size_return.value;
				assert(rsp->hdr.a.len.value <= rc_size_return+SIZEOF(rc_rsp_page));
				rsp->zcode.value = (cs_data->blk_size / 512);	/* (2 ** zcode) == blk_size */
				if (rc_overflow->size)
				{
					memcpy(rc_overflow->buff, bh->buffaddr + size_return - RC_BLKHD_PAD, rc_overflow->size);
					rc_overflow->offset = size_return;
					rc_overflow->dsid = qhdr->r.xdsid.dsid.value;
					rc_overflow->page = bh->blk_num;
					rc_overflow->zcode = rsp->zcode.value;
				}
				rsp->size_remain.value = rc_overflow->size;
			}

			if (0 == (rc_read_stamp = t_end(&gv_target->hist, two_histories ? &second_history : NULL,
				TN_NOT_SPECIFIED)))
				continue;
			if (bsiz == SIZEOF(blk_hdr) + RC_BLKHD_PAD)	/* Empty block, global does not exist */
			{
				qhdr->a.erc.value = RC_MUMERRUNDEFVAR;
		 		rsp->size_return.value = 0;
				rsp->size_remain.value = 0;
				rsp->rstatus.value = 0;
				REVERT;
				return 0;
			}
			if (status == cdb_sc_endtree)
			{
				if (fmode & RC_MODE_NEXT)
				{	rsp->after.value = (unsigned short)-1;
					rsp->before.value = 0;
				}else
				{	rsp->after.value = 0;
					rsp->before.value = (unsigned short)-1;
				}
				rsp->xcc.value = 0;
			}else
			{
				if (two_histories)
				{
					rsp->after.value  = 0;
					rsp->before.value = 0;
					rsp->xcc.value	= 0;
				}else
				{
					if (fmode & RC_MODE_PREV)		/* getprev */
					{	rsp->before.value = bh->prev_rec.offset;
						if (rsp->before.value)
							rsp->before.value += RC_BLKHD_PAD;
						rsp->xcc.value	= bh->prev_rec.match;
						rsp->after.value  = bh->curr_rec.offset + RC_BLKHD_PAD;
					}else				/* if getnext or just get */
					{			/* If key not found, use prev and curr, else use curr and next */
						if (gv_currkey->end + 1 != bh->curr_rec.match)	/* not found */
						{	rsp->before.value = bh->prev_rec.offset;
							if (rsp->before.value)
								rsp->before.value += RC_BLKHD_PAD;
							rsp->xcc.value	= bh->prev_rec.match;
							rsp->after.value  = bh->curr_rec.offset + RC_BLKHD_PAD;
						}else	/* found */
						{	rsp->before.value = bh->curr_rec.offset;
							if (rsp->before.value)
								rsp->before.value += RC_BLKHD_PAD;
							rsp->xcc.value	= bh->curr_rec.match;
							rp = (rec_hdr *)(bh->buffaddr + bh->curr_rec.offset);
							GET_SHORT(rsiz, &rp->rsiz);
							rsp->after.value  = bh->curr_rec.offset + rsiz + RC_BLKHD_PAD;
						}
					}
				}
			}
			PUT_LONG(rsp->pageaddr, bh->blk_num);
			rsp->frag_offset.value   = 0;
			rsp->rstatus.value = 0;

			REVERT;
			return 0;
		}
restart:	t_retry(status);
	}

}
