/*
 * Test bookfile_*.c routines, usage:
 *
 *    testbook [-f file] [-d def-file] [-t table] [-n entry] [-i iter]
 *
 * options (all options have mandatory following argument):
 *	-f	Bookreader file to read (def: bookreader.decw$book).
 *	-d	Default file spec for open (def: sys$disk:[].decw$book).
 *	-t	Table (index) to use.  If option specifed as "*", all tables
 *		present in  book will be listed.  If option not present,
 *		bookfile_demo will use first type 5 (TOC) table found.
 *		Note that names are case sensitive.
 *	-n	Entry number within selected table to display.  The section
 *		of the book pointed to by the entry will be displayed.
 *		If option not present, the table entries will be listed.
 *	-i	Iteration count, number of additional sections to display
 *		from file.
 */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "bookreader_recdef.h"
#include "bookfile_io.h"
#include "bookfile_index.h"
#include "bookfile_section.h"
#include "bookfile_text.h"

int LIB$INIT_TIMER(), LIB$SHOW_TIMER();

static void show_section ( void *, void *, char *, int, int, int );
static void show_part ( void * bkf, int part_num );
void show_font ( void *bki, long font_page, long fontcount );

static int convert_text3 ( int length, char *in, char *out )
{
    int i, offset, slen, glue, j;
    for ( j = offset = 0; offset < length; ) {
	bkt_text3_scan ( length, in, &offset, &out[j], &slen, &glue );
	j += slen;
	if ( offset < length ) {
	    sprintf(&out[j], "|%x|", glue );
	    j += strlen ( &out[j] );
	    /* out[j++] = ' '; */
	}
    }
    out[j] = '\0';
    return j;
}

int main ( int argc, char **argv )
{
    int i, bad, status, part_length, length, single_sect;
    int ndx_type, ndx_count, iter_count, font_count;
    char *bookfile, *defdir, *table, *sec_str;
    char ndx_name[256];
    bkrdr_recptr root, subrec;
    bktxt_fntptr fontdef;
    void *bkf, *bki;
    /*
     * Interpret command line arguments.
     */
    bookfile = "bookreader.decw$book";
    defdir = "sys$disk:[].decw$book";
    table = sec_str = (char *) 0;
    single_sect = 0;
    iter_count = 0;
    for ( bad = 0, i = 1; !bad && i < argc; i++ ) {
	if ( argv[i][0] != '-' ) { bad = 1; break;}
	if ( i + 1 >= argc ) { bad = 1; break; }
	switch ( argv[i][1] ) {
	    case 'f':  bookfile = argv[++i]; break;
	    case 'd':  defdir = argv[++i]; break;
	    case 't':  table = argv[++i]; break;
	    case 'n':  sec_str = argv[++i]; break;
	    case 'i':  iter_count =  atoi(argv[++i]); break;
	    case 's':  single_sect =  atoi(argv[++i]); break;
	    default:
		bad = 1;
		break;
	}
    }
    if ( bad ) {
	printf("Usage: 'testbook [-f file] [-d dir] [-t table] [-n entry]'\n");
	exit ( 1 );
    }
    /*
     * Open file, read root page, and display some of it's fields.
     */
LIB$INIT_TIMER();
    status = bkf_open ( bookfile, defdir, &bkf );
    if ( (status&1) == 0 ) {
	printf("error opening bookfile '%s'\n", bookfile ); 
	exit ( status );
    }
LIB$SHOW_TIMER();
    status = bkf_read_page ( bkf, 0, &part_length, &root, &length );
    if ( (status&1) == 0 ) {
	printf("error reading root part: %d\n", status );
	exit ( status );
    }
LIB$SHOW_TIMER();
    printf("Book opened, root info:\n      title: '%s'\n      parts: %d\n",
	root->first.title, root->first.partcount );
    printf("   sections: %d\n     author: '%s'\n\n", 
	root->first.sectioncount, root->first.author );
    /*
     * Read font table.
     */
    status = bkt_read_font_map ( bkf, &fontdef, &font_count );
    printf("Font data status: %d, font count: %d table: %d\n", 
		status, font_count, fontdef );
    if ( table ) for ( i = 0; i < font_count; i++ ) printf (
	"Font[%d] = '%s'\n    pixel_size: %d point: %d enc: '%s'\n", 
		fontdef[i].fontno, fontdef[i].name, fontdef[i].pixel_size,
		fontdef[i].point_size,	fontdef[i].encoding );
LIB$SHOW_TIMER();
    /*
     * Lookup table name.
     */
    status = bki_create_context ( bkf, &bki );
    if ( (status&1) == 0 ) {
	printf("error creating index context\n" ); 
	exit ( status );
    }
    if ( table ) {
	int list_mode;
	list_mode = (strcmp(table,"*") == 0);
	if ( list_mode ) printf ( "Available tables:\n" );

	for ( status = bki_find_index ( bki,table,-1, ndx_name,
		&ndx_type, &ndx_count ); (status&1) == 1 && list_mode;
	    	status = bki_find_index ( bki, table, -1, ndx_name,
	   		&ndx_type, &ndx_count ) ) {
	    printf("    '%s', type = %d, size = %d entries\n", ndx_name,
			ndx_type, ndx_count );
	}
	if ( list_mode ) exit ( 1 );
    } else {
	/* 
	 * No table given on command line, find name of first type 5 
	 * table and use it.
	 */
	status = bki_find_index ( bki, "*", 5, 
		ndx_name, &ndx_type, &ndx_count );
    }
    if ( (status&1) == 0 ) {
	printf("Unable to find index '%s'\n", table ? table : "{contents}" );
	exit ( status );
    }
    bki_find_index_end ( bki );

     /* show_font ( bkf, root->first.font_page, root->first.fontcount ); */
    /*
     * Open table for section access using matched name returned by find_index.
     */
    status = bki_open_index ( bki, ndx_name );
    if ( (status&1) == 0 ) {
	printf("Unable to open index '%s'\n", ndx_name );
	exit ( status );
    }
    printf("Table '%s' opened, entries: %d\n", ndx_name, ndx_count );
    /*
     * Now display section corresponding to matching
     */

    show_section ( bkf, bki, sec_str, single_sect, ndx_count, iter_count );
    /*
     * Cleanup.
     */
    status = bki_close_index ( bki );
    status = bki_delete_context ( bki );
    status = bkf_close ( bkf );
    return status;
}

static char *rtype ( short type )
{
    static char *desc[26] = { "unknown", "first", "Unknown2",
	"bodypart", "index", "last", "secmap", "fdx",
	"unknown8", "font", "cont_mid", "cont_end",
	"sb_tblhdr", "ascz_32", "sb_font", "sb_fdesc", "sb_ixtxt",
	"unknown11", "text", "figure", "hotspot", "sb_unk15",
	"sb_extension", "sb_unk17", "sb_license", ">24" };
    static char out_of_range[20];
    if ( (type < 0) || (type > 24) ) {
	sprintf(out_of_range, "unknown%d", type );
	return out_of_range;
    } else {
	return desc[type];
    }
}
/**************************************************************************/
/* Retreive and display contents section specified by table entry number.
 */
static void show_section ( void *bkf, void *bki, char *entry_num_str,
	int single_sect, int ndx_size, int iter_count )
{
    char *desc; void *cursor;
    long ndx_value;
    int status, count, part_num, match, iter;
    short ndx_hdr[9];
    unsigned char attr[4];
    char name[256];
    /*
     * Convert entry number string to integer (match), if string null,
     * set to last table entry.
     */
    match = ndx_size;
    if ( entry_num_str ) match = atoi ( entry_num_str );
    if ( match < 1 ) match = 1;
    if ( match > ndx_size ) match = ndx_size;
    /*
     * Iterate through sub-records in selected table (bki) to retrieve the
     * desired table entry information.
     */
    if ( single_sect ) {
        ndx_value = single_sect;
	if ( iter_count == 0 ) iter_count = 1;
	desc = "";
    } else for ( count = 0; count < match; count++ ) {
	status = bki_read_index( bki, ndx_hdr, attr, name, &desc, &ndx_value);
	if ( (status&1) == 0 ) {
	    printf("Error reading index entry %d\n", count );
	    exit ( status );
	}
        /*
         * If null entry specified, list the table members.
	 */
	if ( !entry_num_str ) printf ( "[%d] %s, (sect=%d)\n", 
		count+1, desc, ndx_value );
    }
    if ( !entry_num_str && !single_sect ) return;  /* Done with list */
    /*
     * Create data structures used by bookfile_section.c
     */
    status = bks_create_section_cursor ( bkf, &cursor );
    if ( (status&1) == 0 ) {
        printf("Error creating cursor: %d\n", status );
	exit ( status );
    } else {
	int first, type, length; long hdr[9];
	/*
	 * Report entry information and lookup part number containing section.
	 */
	printf("Entry %d found in table:\n     title: '%s'\n    Sect #: %d\n",
		 count, desc, ndx_value );
	if ( iter_count <= 0 ) {
	    status = bkf_lookup_first_section ( bkf, ndx_value, 
			&part_num, &first );
	    if ( (status &1) == 1 ) 
		status = bks_seek_part ( cursor, part_num, 0, &iter_count );
	    else iter_count = 0;
	    printf ( "    Part #: %d first sect: %d, sub-records: %d (lookup status: %d)\n\n", 
			part_num, first, iter_count, status );
	    ndx_value = first;		/* reset to start of page */
	} else {
	    status = bkf_lookup_section ( bkf, ndx_value, &part_num );
	    printf ( "    Part #: %d (lookup status: %d)\n\n", part_num, status );
	    first = -1;
	}
	/*
	 * Show the next iter_count sections.
	 */
	for ( iter = 0; (iter < iter_count); iter++ ) {
	    int is_last, t_type, t_len; 
	    short h_v[2]; unsigned char attr[4]; char *data; char buffer[2560];
	    /*
	     * seek.  Aboslute on first read.
	     */
	    if ( ndx_value == first ) {
		first = -1;
		status = bks_seek_section ( cursor, 0, 1, &type, &length, hdr );
	    }
	    else if ( iter == 0  )
	        status = bks_seek_section ( cursor, ndx_value+iter, 0, &type, 
			&length, hdr );
	    else
		status = bks_seek_section ( cursor, 1, 1, &type, &length, hdr );

	    if ( (status&1) == 0 ) printf("Error in seek: %d\n", status);
	    if ( (status&1) == 0 ) break;
	    /*
	     * Now call read_section in loop to extract the contents
	     * of the current section.  is_last will be set true when
	     * sub-record returned is last in section.
	     */
	    is_last = 0;
	    if ( type == BKSBREC_FIGURE ) {
	 	unsigned char *ub;
		ndx_value = hdr[2];
	        printf("[sect=%d,type='%s',len=%d]\n", 
			ndx_value, rtype(type), length);
		printf(" fig: X=%d Y=%d Width=%d Height=%d picbytes: %d\n", 
			hdr[3], hdr[4], hdr[5], hdr[6], hdr[7] );
		ub = (unsigned char *) &hdr[8];
		printf(" fig: primitive: %d ???: %d %d %d\n", ub[0],
			ub[1], ub[2], ub[3] );

	    } else if ( type != BKSBREC_BODYTEXT ) {
		int j;
	        printf("[sect=(%d),type='%s', h_v=%d/%d, len=%d, args: ", 
			ndx_value, rtype(type), h_v[0], h_v[1], length);
		for(j=0;j<9;j++) printf("%d%s",hdr[j],j<8?" ":"]\n");
		status = bks_read_section ( cursor, &t_type, h_v, attr,
			&t_len, &data, &is_last );
	    } else {
		ndx_value = hdr[1];
	        printf("[sect=%d,type='%s'(%d), h_v=%d/%d, len=%d]\n", 
			ndx_value, rtype(type), type, h_v[0], h_v[1], length);
	    }
	    while ( !is_last ) {
		status = bks_read_section ( cursor, &t_type, h_v, attr,
			&t_len, &data, &is_last );
		if ( (status&1) == 0 ) 
		    printf(" xxx: {read error, status: %d, is_last: %d}\n",
			status, is_last );
		if ( (status&1) == 0 ) break;
		/*
		 * Convert type 3 data into single string.
		 */
		if ( t_type == 1 ) {
		    printf ( " ---: {type %d}\n", t_type );	/* can't handle */
		} else if ( t_len > 0 && t_len < 256 ) {
		    if ( (t_type == 2) || (t_type == 3) )
			t_len = convert_text3 ( t_len, data, buffer );
		    buffer[t_len] = '\0';
		    printf("%4d: %s {%d}\n", t_len, buffer, attr[0] );
		} else if ( t_len > 255 ) {
		    int j;
		    printf(" ***: {overflow, %d bytes}\n", t_len);
		    printf("subrec data:");
	            for ( j = 0; j < t_len; j++ ) printf("%d%s",
			data[j], ((j<t_len) && j%20>0) ? " " : "\n" );
		}
	    }
	    if ( is_last == 2 ) printf("////: {part boundary}\n");
	}
    }
    bks_delete_section_cursor ( cursor );
}

void show_font ( void *bkf, long font_page, long fontcount )
{
    bkrdr_recptr font, sub;
    int status, length, i, part_length, j, sublen;
    
    status = bkf_read_page ( bkf, font_page, &part_length, &font, &length );
    printf("Status of reading font page (%d): %d, length: %d/%d\n", 
		font_page, status, length, part_length );
    if ( (status&1) == 0 ) return;

    printf("Font record type: %s, size: %d, count: %d\n", rtype(font->gen.type),
	font->gen.length, fontcount );
    if ( font->gen.type != BKREC_FONT ) return;

    for ( i=sizeof(font->font); i < font->font.length; i+=sub->gen.length) {
	sub = (bkrdr_recptr) &font->reloff[i];

	if ( sub->gen.type == BKSBREC_FONT ) {
	    printf ( "Font #%d is '%s' l=%d\n", sub->fontdef.fontno,
		sub->fontdef.name, sub->gen.length );
	} else {
	    printf("%s subrec at offset %d, size: %d\n",
		rtype(sub->gen.type), i, sub->fontdef.length);
	    printf("subrec data: \n");
      sublen = sub->gen.length - sizeof(sub->gen);
      for ( j = 1; j <= sublen; j++ ) printf("%d%s",
	sub->raw[j+5], ((j<sublen) || j%10) ? " " : "\n" );
	printf("   '%s'\n", &sub->raw[8] );
	}
    }
}
