/*
 * Written by Tomas Dosoudil <tomas.dosoudil@gmail.com>
 * This file is distributed under BSD-style license.
 *
 */

#include	<postgres.h>
#include	<storage/bufpage.h>
#include	<access/htup.h>
#include	<access/transam.h>
#include	"output.h"
#include	"pgdview.h"

/* 
 * print file number 
 */
void
PrintFileName(char *fileName, int numberOfFile) {
		printf("Filename: %s.%d\n", fileName, numberOfFile);
}

/*
 * page info
 * it prints number of page, tuples and free space
 * tuple format is: total(used/delete/unused)
 */
void 
PrintPageInfo(int pageNumber, int itemCount, int itemUsed, int itemDelete, Size freeSpace)
{
	int		unused;
	
	unused = itemCount - itemUsed - itemDelete;
	printf("Page: %4d\tTuples: %3d(%3d/%3d/%3d)\tFree Space: %4ld B\n",
			pageNumber, itemCount, itemUsed, itemDelete, unused, freeSpace);
}

/* 
 * print pageheader 
 */
void
PrintPageHeader(PageHeader pageHeader)
{
    printf("\tpd_lsn: %08X%08X\n", (pageHeader->pd_lsn).xlogid, (pageHeader->pd_lsn).xrecoff);
	printf("\t  xlogid: %08X\n", (pageHeader->pd_lsn).xlogid);
	printf("\t  xrecoff: %08X\n", (pageHeader->pd_lsn).xrecoff);
    printf("\tpd_tli: %08X\n", pageHeader->pd_tli);
    printf("\tpd_lower: %04X\n", pageHeader->pd_lower);
    printf("\tpd_upper: %04X\n", pageHeader->pd_upper);
    printf("\tpd_special: %04X\n", pageHeader->pd_special);
    printf("\tpd_pagesize_version: %04X\n", pageHeader->pd_pagesize_version);
	printf("\t  Page size: %02X\n", (pageHeader->pd_pagesize_version & 0xFF00));
	printf("\t  Layout version: %02X\n", (pageHeader->pd_pagesize_version & 0x00FF));		
}

/* 
 * print itempointer on position
 */
void
PrintPageItemPointer(int index, ItemId *item)
{
	printf("\t\tTuple: %3d\tOffset: %4X\tLength: %4X\tFlags: %02X ", index, 
			ItemIdGetOffset(*item), ItemIdGetLength(*item), ItemIdGetFlags(*item));

	if (ItemIdGetFlags(*item) == LP_USED)
		printf("(Used)\n");
	else if (ItemIdGetFlags(*item) == LP_DELETE)
			printf("(Delete)\n");
	else
		printf("(Unused)\n");
}

/* 
 * print header of tuple 
 */
void
PrintHeapTupleHeader(HeapTupleHeader tupleHeader)
{
	printf("\t\t\tt_xmin: %08X\n", HeapTupleHeaderGetXmin(tupleHeader));
	printf("\t\t\tt_xmax: %08X\n", HeapTupleHeaderGetXmax(tupleHeader));
	printf("\t\t\tt_cmin: %08X\n", HeapTupleHeaderGetCmin(tupleHeader));
	printf("\t\t\tt_xvac: %08X\n", HeapTupleHeaderGetXvac(tupleHeader));
	/* 
 	 * order of structures:
     * HeapTupleHeader -> ItemPointerData -> { BlockIdData -> { bi_hi, bi_lo }, ip_posid } 
     */
	printf("\t\t\tt_ctid: %04X%04X%04X\n", tupleHeader->t_ctid.ip_blkid.bi_hi, 
			tupleHeader->t_ctid.ip_blkid.bi_lo, tupleHeader->t_ctid.ip_posid);
	printf("\t\t\tt_natts: %04X\n", tupleHeader->t_natts);
	printf("\t\t\tt_infomask: %04X\n", tupleHeader->t_infomask);
	
	/* show values in t_infomask */
	if (tupleHeader->t_infomask) {
		if (_HeapTupleHasNulls(tupleHeader)) 
			printf("\t\t\t  HEAP_HASNULL\n");
	
		if (_HeapTupleHasVarWidth(tupleHeader)) 
			printf("\t\t\t  HEAP_HASVARWIDTH\n");
	
		if (_HeapTupleHasExternal(tupleHeader)) 
			printf("\t\t\t  HEAP_HASEXTERNAL\n");
	
		if (_HeapTupleHasCompressed(tupleHeader)) 
			printf("\t\t\t  HEAP_HASCOMPRESSED\n");
	
		if (_HeapTupleHasExtended(tupleHeader)) 
			printf("\t\t\t  HEAP_HASEXTENDED\n");

		if (HeapTupleHasOid(tupleHeader)) 
			printf("\t\t\t  HEAP_HASOID\n");

		if (HeapTupleHasUnlogged(tupleHeader)) 
			printf("\t\t\t  HEAP_XMAX_UNLOGGED\n");

		if (HeapTupleHasXminCommitted(tupleHeader)) 
			printf("\t\t\t  HEAP_XMIN_COMMITTED\n");

		if (HeapTupleHasXminInvalid(tupleHeader)) 
			printf("\t\t\t  HEAP_XMIN_INVALID\n");

		if (HeapTupleHasXmaxCommitted(tupleHeader)) 
			printf("\t\t\t  HEAP_XMAX_COMMITTED\n");

		if (HeapTupleHasXmaxInvalid(tupleHeader)) 
			printf("\t\t\t  HEAP_XMAX_INVALID\n");

		if (HeapTupleHasMarkedUpdate(tupleHeader)) 
			printf("\t\t\t  HEAP_MARKED_FOR_UPDATE\n");

		if (HeapTupleHasUpdated(tupleHeader)) 
			printf("\t\t\t  HEAP_UPDATED\n");

		if (HeapTupleHasMovedOff(tupleHeader)) 
			printf("\t\t\t  HEAP_MOVED_OFF\n");
	
		if (HeapTupleHasMovedIn(tupleHeader)) 
			printf("\t\t\t  HEAP_MOVED_IN\n");

		if (HeapTupleHasMoved(tupleHeader)) 
			printf("\t\t\t  HEAP_MOVED\n");
	}

	printf("\t\t\tt_hoff: %02X\n", tupleHeader->t_hoff);
}

/*
 * print tuple data 
 * format is like in hexdump: offset data(2x8B)
 */
void
PrintHeapTupleData(HeapTupleHeader tupleHeader, uint32 tupleOffset, uint32 tupleLength)
{
	uint8	*userData;		/* point to data */
	uint32	userDataLen;	/* data length */
	uint32	dataOffset;		/* data offset in page */
	uint32	j;				/* counter */

	/* point to tuple data */
	userData = (uint8*) tupleHeader;
	userData += tupleHeader->t_hoff;

	userDataLen = tupleLength - tupleHeader->t_hoff;
	dataOffset = tupleOffset + tupleHeader->t_hoff;

	printf("\t\t\t\t%04X | ", dataOffset);	/* offset */
	for (j = 0; j < userDataLen; j++) {

		if (((j % 8) == 0) && (j != 0))
			printf(" ");

		if (((j % 16) == 0) && (j != 0))
			printf("\n\t\t\t\t%04X | ", dataOffset + j);	/* offset */

		printf("%02X ", *userData);
		userData++;
	}
	printf("\n");
}

void
Usage(void) 
{
	printf("pgdview [-p [num] -i [num] [-tdf]] [-ch] { [-D db_path table_oid] | file_name }\n");
	printf(" -p page\n");
	printf(" -c page header\n");
	printf(" -i item list\n");
	printf(" -t tuple header\n");
	printf(" -d tuple data\n");
	printf(" -f follow ctid\n");
	printf(" -D database path\n");
	printf(" -h help\n");
	printf("\nNumeral range\n");
	printf(" Page starts from 0 and tuples from 1\n");
	printf(" Possible values are num,..num,num..,num..num\n");
	printf(" If parameter does not have argument then show all\n");
	printf(" First page is default if parameter -p does not set\n");
	printf("\nFollow ctid\n");
	printf(" When you want to use follow ctid you must enter the page\n");
	printf(" where item is stored and its number\n");
	printf(" e.g pgdview -p 1 -i 1 -tf -D ./path filename\n");
	printf(" Show first item and its tuple header on page one and make follow\n");
}
