/*
 * mid-level functions for accessing catalogue.
 */
#include <stdio.h>
#include <unixio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "pthread_1c_np.h"

#include "catalogue.h"
#define BITS_PER_LONG 32
#include "cds_volume.h"
#include "cds_bitmap.h"

struct object_ctl {
    volptr vol;			/* volume */
    FILE *f;			/* data file */
    struct cds_header hdr;	/* copy of catalogue header */
};
struct cat_rec {
    struct cds_header hdr;
    char key[224];
};

static int catalogue_key ( char *name, int *context, long *prev, char key[224],
	int *keylen )
{
    int i, has_colon;
    if ( *context < 0 ) return 0;	/* context invalid */
    else if ( *context == 0 ) {
	/*
	 * initial call parse key and verify.
	 */
	has_colon = 0;
	if ( name[0] >= '0' && name[0] <= '9' ) return 0; /* bad key */
	for ( i = 0; name[i]; i++ ) {
	    key[i] = name[i];
	    /* if ( !has_colon ) toupper(key[i]); */
	    if ( key[i] == ':' ) has_colon = 1;

	    if ( i >= 223 ) if ( name[i] ) {
		/* string too long */
		if ( !has_colon ) return 0;		/* bad key */
		*keylen = i+1;
		*context = i+1;
		return 3;
	    }
	}
	/*
	 * No continue, reset context.
	 */
	*context = -1;
	*keylen = i;
	if ( !has_colon ) return 0;
	return 1;
    } else {
	/*
	 * continuation.
	 */
	int j;
	sprintf ( key, "%d:", *prev );
	j = strlen ( key );
	for ( i = *context; name[i]; i++ ) {
	    key[j++] = name[i];
	    if ( j > 223 ) {
		/*
		 * Another continuation.
		 */
		*keylen = j;
		*context = i+1;
		return 3;
	    }
	}
	/*
	 * No continue, reset context.
	 */
	*context = -1;
	*keylen = j;
	return 1;
    }
    return has_colon;
}

objptr cds_open_entry ( volptr volume, char *name,
	int create_if, struct cds_header *header, char errmsg[256] )
{
    struct object_ctl *tmp;		/* temporary pointer */
    struct cat_rec rec;
    int ctx, prev, status;
    unsigned int length;
    char key[224];
    int keylen, cr_count;
    long cont_rec[20];
    struct cds_volume *vol;
    /*
     * Lookup record by key.
     */
    errmsg[0] = '\0';
    vol = (struct cds_volume *) volume;
    pthread_mutex_lock ( &vol->lock );
    for ( cr_count = ctx = 0; ctx >= 0; ) {
	if ( catalogue_key ( name, &ctx, &rec.hdr.number, key, &keylen ) ) {
	    int i;
	    for ( i = keylen; i < sizeof(key); i++ ) key[i] = '\0';
	    status = ifread_rec ( &rec, sizeof(rec), &length, vol->catalogue,
		1, key, sizeof(key) );
	    if ( (status&1) == 1 ) {
		/*
		 * Record found.  Save record number if continuation.
		 */
		if ( ctx > 0 ) {
		    if ( cr_count < 20 ) {
			cont_rec[cr_count++] = rec.hdr.number;
		    } else {
			strcpy ( errmsg, "Error in name, too long" );
			break;
		    }
		} else if ( rec.hdr.state == 1 ) {  /* locked */
		    strcpy ( errmsg, "Entry locked against additional access." );
		    break;
		}
	    } else if ( create_if ) {
		/*
		 * Attempt to write a record, either final or continuation.
		 */
		int new_number, i;
		status = cds_allocate ( vol, &new_number );
		if ( (status&1) == 0 ) {
		    strcpy ( errmsg, "No free entries available" );
		    break;
		}
		if ( ctx < 0 ) {
		    /*
		     * At terminal record, update previously existing
		     * continuation records.
		     */
		    while ( cr_count > 0 ) {
			cr_count--;
			status = ifread_rec ( &rec, sizeof(rec), &length,
				vol->catalogue, 0, &cont_rec[cr_count],
				sizeof(cont_rec[cr_count]) );
			rec.hdr.ref_count++;
			status = ifupdate_rec ( &rec, length, vol->catalogue );
		    }
		    rec.hdr.state = 1; /* mark deaccess locked. */
		    rec.hdr.ref_count = 0;
		} else {
		    rec.hdr.state = 0;		/* continuation record */
		    rec.hdr.ref_count = 1;
		}
		rec.hdr.number = new_number;
		rec.hdr.access_window[0] = rec.hdr.access_window[1] = 0;
		rec.hdr.size = 0;
		rec.hdr.mod_time = 0;
		rec.hdr.reserved = 0;
		strncpy ( rec.key, key, keylen );
		for ( i = keylen; i < sizeof(rec.key); i++ ) rec.key[i]='\0';
		if (1 == 1&ifwrite_rec ( &rec, sizeof(rec), vol->catalogue)) {
		} else {
		    strcpy ( errmsg, "Error updating record" );
		    break;
		}
	    } else {
		strcpy ( errmsg, "expected record not found" );
		break;
	    }
	} else {
	    strcpy ( errmsg, "error parsing name for lookup" );
	    break;
	}
    }
    pthread_mutex_unlock ( &vol->lock );
    if ( errmsg[0] ) return (void *) 0;
    /*
     * Create object pointer and initialize.
     */
    if ( header ) *header = rec.hdr;
    tmp = (struct object_ctl *) malloc ( sizeof(struct object_ctl) );
    if ( !tmp ) {
	strcpy ( errmsg, "error allocating object control structure" );
	return (void *) 0;
    }
    tmp->vol = vol;
    tmp->f = (FILE *) 0;
    tmp->hdr = rec.hdr;

    return (void *) tmp;
}
/***************************************************************************/
int cds_close_entry ( objptr object, int delete_flag, 
	struct cds_header *header, char errmsg[256] )
{
    int status;
    long number;
    struct object_ctl *obj;
    struct cat_rec rec;
    struct cds_volume *vol;
    char fname[272];

    obj = (struct object_ctl *) object;

    if ( obj->f ) fclose ( obj->f );
    vol = obj->vol;
    number = obj->hdr.number;
    free ( obj );
    /*
     * update header if present.
     */
    if ( header || delete_flag ) {
	size_t length;
        pthread_mutex_lock ( &vol->lock );
	status = ifread_rec ( &rec, sizeof(rec), &length, vol->catalogue,
		0, &number, sizeof(number) );
	if ( status&1 == 0 ) {
	    strcpy ( errmsg, "Error reading catalogue record for update" );
    	    pthread_mutex_unlock ( &vol->lock );
	    return status;
	}
	if ( delete_flag ) {
	    /*
	     * Check status of data file and delete.
	     */
	    if ( rec.hdr.state > 0 ) {
		cds_number_to_fname ( vol, number, fname );
		delete ( fname );
	    }
	    while ( delete_flag ) {
	        /*
	         * Remove record from file and clear bitmap bit.
	         */
		delete_flag = 0;
	        rec.hdr.ref_count--;
	        if ( rec.hdr.ref_count <= 0 ) {
	            status = ifdelete_rec ( vol->catalogue );
	            cds_deallocate ( vol, number );
	        } else {
		    /* Preserve for use by other continuation record. */
		    if ( rec.hdr.number == number ) rec.hdr.state = 0;
		    status = ifupdate_rec ( &rec, length, vol->catalogue );
	        }
		if ( rec.key[0] >= '0' && rec.key[0] <= '9' ) {
		    long cont_number;
		    delete_flag = 1;
		    cont_number = atoi ( rec.key );
		    status = ifread_rec ( &rec, sizeof(rec), &length, 
			vol->catalogue,	0, &cont_number, sizeof(cont_number) );
		}
	    }
	} else {
	    /*
	     * rewrite the record.
	     */
	    rec.hdr = *header;
	    rec.hdr.number = number;
	    rec.hdr.state = 2;
	    status = ifupdate_rec ( &rec, length, vol->catalogue );
	}
        pthread_mutex_unlock ( &vol->lock );
	if ( (status&1) == 0 ) {
	    strcpy ( errmsg, "error updating/deleting catalogue record" );
	    return status;
	}
    }
    return 1;
}
/***************************************************************************/
int cds_read ( objptr object, char *buffer, int bufsize, int *bytes )
{
    struct object_ctl *obj;
    obj= (struct object_ctl *) object;

    if ( !obj->f ) {
	/*
	 * First data access, attempt to open file.
	 */
	char fname[272];
        cds_number_to_fname ( obj->vol, obj->hdr.number, fname );
        obj->f = fopen ( fname, "rb+" );
    }

    *bytes = fread ( buffer, 1, bufsize, obj->f );

    return 1;
}
int cds_write ( objptr object, char *buffer, int bufsize )
{
    size_t bytes;
    struct object_ctl *obj;
    obj= (struct object_ctl *) object;

    if ( !obj->f ) {
	/*
	 * First data access, attempt to open file.
	 */
	char fname[272];
        cds_number_to_fname ( obj->vol, obj->hdr.number, fname );
        obj->f = fopen ( fname, "wb+" );
    }

    bytes = fwrite ( buffer, 1, bufsize, obj->f );

    return 1;
}
