/* Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. */

#include "dicom_storage.h"

/*
 * List all DICOM attributes' name.
 */
char atts_name[ATTRIBUTENUM][40] = {
    "Accession Number",
    "Acquisition Date",
    "Acquisition Time", 
    "Body Part Examined",
    "Data Collection Diameter",
    "Device Serial Number",
    "Distance Source to Detector",
    "Distance Source to Patient",
    "Image Comments",
    "Institution Address",
    "Institution Name",
    "Institutional Department Name",
    "KVP",
    "Manufacturer",
    "Manufacturer's Model Name",
    "Modality", 
    "Name of Physician(s) Reading Study",
    "Operators' Name",
    "Patient's Age",
    "Patient ID",
    "Patient's Birth Date",
    "Patient's Name",
    "Patient's Sex",
    "Referring Physician's Name",
    "Performing Physician's Name",
    "Series Date",
    "Series Instance UID",
    "Slice Thickness",
    "Software Version(s)",
    "SOP Class UID",
    "SOP Instance UID",
    "Source Type",
    "Spatial Resolution",
    "Station Name",
    "Study Date",
    "Study Description"
};

/*
 * Close all databases, secondary indexes and environment. All secondary 
 * indexes should be closed before closing their primary database. All 
 * databases should be closed before closing their environment.
 */
int
close_dbs(dm_dbs, env)
	DICOM_DBS *dm_dbs;
	DB_ENV *env;
{
        int ret = 0;
        
        /* Close patient's name secondary index. */
	if (dm_dbs->pn_sdbp != NULL) {
		ret = dm_dbs->pn_sdbp->close(dm_dbs->pn_sdbp, 0);
		if (ret != 0)
                        return EXIT_FAILURE;
	}

	/* Close modality secondary index. */
	if (dm_dbs->md_sdbp != NULL) {
                ret = dm_dbs->md_sdbp->close(dm_dbs->md_sdbp, 0);
		if (ret != 0) 
                        return EXIT_FAILURE;
	}

	/* Close acquisition date secondary index. */
	if (dm_dbs->ad_sdbp != NULL) {
		ret = dm_dbs->ad_sdbp->close(dm_dbs->ad_sdbp, 0);
		if (ret != 0)
                        return EXIT_FAILURE;
	}
	
	/* Close the primary database. */
	if (dm_dbs->dicom_dbp != NULL) {
		ret = dm_dbs->dicom_dbp->close(dm_dbs->dicom_dbp, 0);
		if (ret != 0) 
			return EXIT_FAILURE;
	}
        
       /* Close environment. */
	if (env != NULL) {
		ret = env->close(env, 0);
                if (ret != 0)
                        return EXIT_FAILURE;
	}

	return EXIT_SUCCESS; 
}

void
copy_dicom_image(src_dicom_image, des_dicom_image)
	DICOM_IMAGE *src_dicom_image;
	DICOM_IMAGE *des_dicom_image;
{
        /* Clean the struct des_dicom_image. Copy struct src_dicom_image 
         * to des_dicom_image. All integer fields are copied thereafter.
         */  
        memset(des_dicom_image, 0, sizeof(DICOM_IMAGE));
	memcpy(des_dicom_image, src_dicom_image, sizeof(DICOM_IMAGE));

        /* Go through all pointers in DICOM_IMAGE struct and copy all valid
         * content.
         */          
	copy_memory(src_dicom_image->accession_number, 
	    &(des_dicom_image->accession_number), 
	    src_dicom_image->accession_number_length);    
	copy_memory(src_dicom_image->acquisition_date, 
	    &(des_dicom_image->acquisition_date),
	    src_dicom_image->acquisition_date_length);    
	copy_memory(src_dicom_image->acquisition_time, 
	    &(des_dicom_image->acquisition_time),
	    src_dicom_image->acquisition_time_length);    
	copy_memory(src_dicom_image->body_part_examined, 
	    &(des_dicom_image->body_part_examined),
	    src_dicom_image->body_part_examined_length);   
	copy_memory(src_dicom_image->data_collection_diameter, 
	    &(des_dicom_image->data_collection_diameter),
	    src_dicom_image->data_collection_diameter_length);   
	copy_memory(src_dicom_image->device_serial_number, 
	    &(des_dicom_image->device_serial_number),
	    src_dicom_image->device_serial_number_length);   
	copy_memory(src_dicom_image->distance_source_to_detector, 
	    &(des_dicom_image->distance_source_to_detector),
	    src_dicom_image->distance_source_to_detector_length);   
	copy_memory(src_dicom_image->distance_source_to_patient, 
	    &(des_dicom_image->distance_source_to_patient),
	    src_dicom_image->distance_source_to_patient_length); 
	copy_memory(src_dicom_image->image_comments, 
	    &(des_dicom_image->image_comments),
	    src_dicom_image->image_comments_length);    
	copy_memory(src_dicom_image->institution_address, 
	    &(des_dicom_image->institution_address),
	    src_dicom_image->institution_address_length);    
	copy_memory(src_dicom_image->institution_name, 
	    &(des_dicom_image->institution_name),
	    src_dicom_image->institution_name_length);    
	copy_memory(src_dicom_image->institutional_department_name, 
	    &(des_dicom_image->institutional_department_name),
	    src_dicom_image->institutional_department_name_length);    
	copy_memory(src_dicom_image->kvp, 
	    &(des_dicom_image->kvp),
	    src_dicom_image->kvp_length);    
	copy_memory(src_dicom_image->manufacturer, 
	    &(des_dicom_image->manufacturer),
	    src_dicom_image->manufacturer_length);    
	copy_memory(src_dicom_image->manufacturer_model_name, 
	    &(des_dicom_image->manufacturer_model_name),
	    src_dicom_image->manufacturer_model_name_length);    
	copy_memory(src_dicom_image->modality, 
	    &(des_dicom_image->modality),
	    src_dicom_image->modality_length);    
	copy_memory(src_dicom_image->name_of_physician_reading_study, 
	    &(des_dicom_image->name_of_physician_reading_study),
	    src_dicom_image->name_of_physician_reading_study_length);    
	copy_memory(src_dicom_image->operator_name, 
	    &(des_dicom_image->operator_name),
	    src_dicom_image->operator_name_length);   
	copy_memory(src_dicom_image->patient_age, 
	    &(des_dicom_image->patient_age),
	    src_dicom_image->patient_age_length);
	copy_memory(src_dicom_image->patient_birth_date, 
	    &(des_dicom_image->patient_birth_date),
	    src_dicom_image->patient_birth_date_length);   
	copy_memory(src_dicom_image->patient_id, 
	    &(des_dicom_image->patient_id),
	    src_dicom_image->patient_id_length);
	copy_memory(src_dicom_image->patient_name, 
	    &(des_dicom_image->patient_name),
	    src_dicom_image->patient_name_length);   
	copy_memory(src_dicom_image->patient_sex, 
	    &(des_dicom_image->patient_sex),
	    src_dicom_image->patient_sex_length);    
	copy_memory(src_dicom_image->performing_physician_name, 
	    &(des_dicom_image->performing_physician_name),
	    src_dicom_image->performing_physician_name_length);   
	copy_memory(src_dicom_image->referring_physician_name, 
	    &(des_dicom_image->referring_physician_name),
	    src_dicom_image->referring_physician_name_length);     
	copy_memory(src_dicom_image->series_date, 
	    &(des_dicom_image->series_date),
	    src_dicom_image->series_date_length);   
	copy_memory(src_dicom_image->series_instance_uid, 
	    &(des_dicom_image->series_instance_uid),
	    src_dicom_image->series_instance_uid_length);   
	copy_memory(src_dicom_image->slice_thickness, 
	    &(des_dicom_image->slice_thickness),
	    src_dicom_image->slice_thickness_length);
	copy_memory(src_dicom_image->software_version, 
	    &(des_dicom_image->software_version),
	    src_dicom_image->software_version_length); 
	copy_memory(src_dicom_image->sop_class_uid, 
	    &(des_dicom_image->sop_class_uid),
	    src_dicom_image->sop_class_uid_length);   
	copy_memory(src_dicom_image->sop_instance_uid, 
	    &(des_dicom_image->sop_instance_uid),
	    src_dicom_image->sop_instance_uid_length);   
	copy_memory(src_dicom_image->source_type, 
	    &(des_dicom_image->source_type),
	    src_dicom_image->source_type_length);   
	copy_memory(src_dicom_image->spatial_resolution, 
	    &(des_dicom_image->spatial_resolution),
	    src_dicom_image->spatial_resolution_length);   
	copy_memory(src_dicom_image->station_name, 
	    &(des_dicom_image->station_name),
	    src_dicom_image->station_name_length);   
	copy_memory(src_dicom_image->study_date, 
	    &(des_dicom_image->study_date),
	    src_dicom_image->study_date_length);  
	copy_memory(src_dicom_image->study_description, 
	    &(des_dicom_image->study_description),
	    src_dicom_image->study_description_length);   
	copy_memory(src_dicom_image->thumbnail_format, 
	    &(des_dicom_image->thumbnail_format),
	    src_dicom_image->thumbnail_format_length);          	
	copy_memory(src_dicom_image->thumbnail_content, 
	    &(des_dicom_image->thumbnail_content),
	    src_dicom_image->thumbnail_content_length);
}

void 
copy_memory(src_data, des_data, length)
        void *src_data;
        void **des_data;
        int length;
{
        /* Copy the content of src_data to des_data. */
	if (length > 0) {
		*des_data = malloc(length * sizeof(char));
		memset(*des_data, 0, length * sizeof(char));  
		memcpy(*des_data, src_data, length);      
	}
}

/*
 * Convert the first 4 bytes from the stream to an integer and return the 
 * integer. 
 */ 
int 
convert_bytes_to_int(bytes)
        char *bytes;
{
        /* Initialize the integer to be 1. */
        int value = 1;
        
        /* Initialize the number of read bytes to be 0. */
        int i = 0;
        
        /* Every time when reading one byte, plus the current integer with
         * 256 to left shift for 8 bites and add the read byte to get the
         * new integer. After reading 4 bytes, return the integer.
         */               
        while (i < 4) {
                value *= 256;
                value += (unsigned char)bytes[i];
                i++;
        }
        
        return value;
}

/*
 * Convert an integer to byte array and return the byte size of integer.
 * The function is used only by host machine so there is no concern 
 * about endian. If the byte array from integer is used as HTTP content,
 * it should take care of endian here.
 */ 
int 
convert_int_to_bytes(number, bytes)
        int number;
        char *bytes;
{  
        int i = 0;
        int intsize = sizeof(int);
        int mask = 0xff000000;
        int shift = 24;
        
        /* Get one byte each time from the integer from the highest byte 
         * to lowest byte.
         */
        if (intsize <= 8) {
                while (i < intsize) {
                        bytes[i++] = (number >> shift) & 0xff;
                        shift -= 8;
                }
        }
        
        return intsize;
}

/*
 * Convert a char string to a WCHAR string and return its address. 
 */ 
WCHAR **
convertCharToWchar(chars)
	char *chars;
{
        int size;
	LPWSTR wchars;
	
	/* Get the size of WCHAR string converted from a char string. */
	size = MultiByteToWideChar(CP_ACP, 0, chars, -1, NULL, 0);
	
	/* Initialize a buffer with the counted size. */
	wchars = malloc(size * sizeof(WCHAR));
	memset(wchars, 0, size * sizeof(WCHAR));
	
        /* Get WCHAR string from the char string to the buffer. */
        MultiByteToWideChar(CP_ACP, 0, chars, -1, wchars, size);
	
	/* Return the address of the buffer. */
        return &wchars;
}

/*
 * Convert a WCHAR string to a char string and return its address.
 */ 
char **
convertWcharToChar(wchars)
	WCHAR *wchars;
{
	LPSTR chars;
	int size;
	
	/* Get the size of char string from the WCHAR string. */
	size = WideCharToMultiByte(CP_ACP, 0, wchars, -1, NULL, 0, NULL,
	    NULL);
	/* Initialize a buffer with the counted size. */
	chars = malloc(size * sizeof(char));
	memset(chars, 0, size);
	
	/* Get char string from the WCHAR string to the buffer. */
	WideCharToMultiByte(CP_ACP, 0, wchars, -1, chars, size, NULL, 
	    NULL);
	
        /* Return the address of the buffer. */
        return &chars;
}

/*
 * Delete all DICOM in the database. Attributes of all deleted DICOM (not 
 * including image data) are stored in the given DICOM list and the deleted
 * DICOM number are stored in the given integer. If all DICOM have been 
 * deleted, return 0. Unless, return the error code.
 */ 
int
delete_all_dicom(dbs, env, txn, dicom_list, number)
	DICOM_DBS *dbs;
	DB_ENV *env;
	DB_TXN *txn;
	DICOM_IMAGE *dicom_list;
	int *number;
{
        DBC *cursor;
	DBT bdb_key, bdb_data;
	int ret, cret;
	
	/*Initialize all. */
	*number = 0;
	memset(&bdb_key, 0, sizeof(DBT));
	memset(&bdb_data, 0, sizeof(DBT));
	memset(dicom_list, 0, sizeof(DBT));	

        /* Create a cursor in the primary database. */
        ret = dbs->dicom_dbp->cursor(dbs->dicom_dbp, txn, &cursor, 0);
        if (ret != 0) 
		return EXIT_FAILURE;

        /* Traverse bdb to get all DICOM stored in bdb, delete each of 
         * them and return its attributes in a DICOM list.          
         */
        for (*number = 0;;(*number)++) {
                /* Get the next DICOM. If the returning value from get 
                 * operation is not 0, quit the loop. DB_NOTFOUND is 
                 * expected correct returned value. 
                 */                 
                cret = cursor->get(cursor, &bdb_key, &bdb_data, DB_NEXT);
                if (cret == DB_NOTFOUND) {
                        cret = 0;
                        break;
                } else if (cret != 0) {
                        cret = EXIT_FAILURE;
                        break;
                }
  
                /* Get DICOM attributes from the current DICOM to be 
                 * deleted and put them to the DICOM list.                 
                 */
                get_dicom_image_from_dbt(&bdb_data, &dicom_list[*number]);
		dicom_list[*number].id = *((int *)bdb_key.data);
                
                /* Delete the current existing DICOM. All non-zero returned
                 * values are considered incorrect, including DB_NOTFOUND.
                 */                                   
                if ((cret = cursor->del(cursor, 0)) != 0) {
                        cret = EXIT_FAILURE;
                        break;
                }
        }
            
        /* Close the cursor. */            
        ret = cursor->close(cursor);
        if (ret != 0)
                ret = EXIT_FAILURE;
        
        /* If fail to delete all DICOM, return the error code. Unless, 
         * return 0.
         */          
        if (cret != 0)
                return cret;
        else 
                return ret;
}

/*
 * Delete the DICOM with given ID. The deleted DICOM's attributes (not 
 * including the image data) are returned in the given DICOM_IMAGE struct. 
 */ 
int
delete_dicom_with_id(dbs, env, txn, id, dicom)
	DICOM_DBS *dbs;
	DB_ENV *env;
	int id;
	DICOM_IMAGE *dicom;
{
        DBC *cursor;
	DBT bdb_key, bdb_data;
        int ret, cret, number;
	
        /* Zero out DBTs. */
        memset(&bdb_key, 0, sizeof(DBT));
        memset(&bdb_data, 0, sizeof(DBT));
	
        /* Initialize key with the specified ID. */
        bdb_key.data = &id;
        bdb_key.size = (u_int32_t)sizeof(int);

        /* Create cursor for primary database. */
        ret = dbs->dicom_dbp->cursor(dbs->dicom_dbp, txn, &cursor, 0);
        if (ret != 0) 
                return EXIT_FAILURE;

        /* Move cursor to record with the specified ID and delete the 
         * record. The ID should be unique that more than one records 
         * are deleted should be regarded as a failure.           
         */
        for (number = 0; ; number++) {
                /* Get next DICOM record in bdb with the specified key. 
                 * Both zero and DB_NOTFOUND are correct returned value. 
                 */       
                cret = cursor->get(cursor, &bdb_key, &bdb_data, DB_SET);
                if (cret != 0) {
                        if (cret == DB_NOTFOUND && number == 1)
                                cret = 0;
                        else 
                                cret = EXIT_FAILURE;
                        
                        break;
                }
        
                /* Get DICOM attributes (not including image data) from the
                 * deleted record and store it in the given DICOM_IMAGE 
                 * struct.                  
                 */
                get_dicom_image_from_dbt(&bdb_data, dicom);
		dicom->id = *((int *)bdb_key.data);
                
                /* Delete the current existing DICOM. All non-zero returned
                 * values are considered incorrect, including DB_NOTFOUND.
                 */                                   
                if ((cret = cursor->del(cursor, 0)) != 0) {
                       cret = EXIT_FAILURE;
                       break;
                }     
        }

        /* Close the cursor. */
        ret = cursor->close(cursor);
        if (ret != 0)
                ret = EXIT_FAILURE;

        /* If fail to delete DICOM with the given ID, return the error 
         * code. Unless, return 0.
         */          
        if (cret != 0)
                return cret;
        else 
                return ret;
}

/*
 * Find the attribute's index in all attributes listed in atts_name with
 * its name. If the attribute exists in the atts_name, return its index 
 * (from 0). Unless return -1. 
 */ 
int 
find_attributes_index(name)
	char *name;
{
	int idx;

        /* Compare the given name with the all strings listed in the 
         * atts_name. Quit the loop when find the matching one.          
         */
	for (idx = 0; idx < ATTRIBUTENUM;idx++) {
		if ((strcmp(atts_name[idx], name)) == 0)
			break;
	}

        /* If the current index is smaller than the amount, return
         * it. Unless, return -1.
         */          
	return (idx < ATTRIBUTENUM) ? idx : -1;
}

/*
 * Free malloced-memory in the DICOM_IMAGE struct.
 */ 
void 
free_dicom_image(dicom_image)
	DICOM_IMAGE *dicom_image;
{
        /* Go through all pointers in DICOM_IMAGE struct and free any valid
           memory that they points to. */
        free_pointer(dicom_image->accession_number);    
        free_pointer(dicom_image->acquisition_date);    
	free_pointer(dicom_image->acquisition_time);    
	free_pointer(dicom_image->body_part_examined);   
	free_pointer(dicom_image->data_collection_diameter);   
	free_pointer(dicom_image->device_serial_number);   
	free_pointer(dicom_image->distance_source_to_detector);   
	free_pointer(dicom_image->distance_source_to_patient); 
	free_pointer(dicom_image->image_comments);    
	free_pointer(dicom_image->institution_address);    
	free_pointer(dicom_image->institution_name);    
	free_pointer(dicom_image->institutional_department_name);    
	free_pointer(dicom_image->kvp);    
	free_pointer(dicom_image->manufacturer);    
	free_pointer(dicom_image->manufacturer_model_name);    
	free_pointer(dicom_image->modality);    
	free_pointer(dicom_image->name_of_physician_reading_study);    
	free_pointer(dicom_image->operator_name);   
	free_pointer(dicom_image->patient_age);
	free_pointer(dicom_image->patient_birth_date);   
	free_pointer(dicom_image->patient_id);
	free_pointer(dicom_image->patient_name);   
	free_pointer(dicom_image->patient_sex);    
	free_pointer(dicom_image->performing_physician_name);   
	free_pointer(dicom_image->referring_physician_name);     
	free_pointer(dicom_image->series_date);   
	free_pointer(dicom_image->series_instance_uid);   
	free_pointer(dicom_image->slice_thickness);
	free_pointer(dicom_image->software_version); 
	free_pointer(dicom_image->sop_class_uid);   
	free_pointer(dicom_image->sop_instance_uid);   
	free_pointer(dicom_image->source_type);   
	free_pointer(dicom_image->spatial_resolution);   
	free_pointer(dicom_image->station_name);   
	free_pointer(dicom_image->study_date);  
	free_pointer(dicom_image->study_description);   
	free_pointer(dicom_image->thumbnail_format);          	
	free_pointer(dicom_image->thumbnail_content);
}

/*
 * If the pointer points to valid memory, free it.
 */ 
void 
free_pointer(p)
	void *p;
{
	if (p != NULL) {
		free(p);
		p = NULL;
	}
}

/*
 * Get DICOM_IMAGE struct from DBT.
 */ 
void 
get_dicom_image_from_dbt(bdb_data, dicom_image)
	DBT *bdb_data;
	DICOM_IMAGE *dicom_image;
{
	char **att_value;
			/* A pointer to the current attribute's address. */
	unsigned int *att_length;     /* The current attribute's length. */
	int offset;     /* The current number of bytes read in DBT data. */
	int i;     /* The current number of attributes read in DBT data. */
        
	/* Iteratively get all DICOM attributes and thumbnail from DBT. */ 
	offset = 0;
	for (i = 0; i < ATTRIBUTENUM + 2; i++) {
		switch (i) {
		case ACCESSIONNUMBER: 
			att_value = &(dicom_image->accession_number); 
			att_length = 
			    &(dicom_image->accession_number_length);
                        break;
		case ACQUISITIONDATE: 
			att_value = &(dicom_image->acquisition_date); 
			att_length = 
                            &(dicom_image->acquisition_date_length);
			break;
		case ACQUISITIONTIME: 
			att_value = &(dicom_image->acquisition_time); 
			att_length = 
			    &(dicom_image->acquisition_time_length);
			break;
		case BODYPARTEXAMINED: 
			att_value = &(dicom_image->body_part_examined); 
			att_length = 
			    &(dicom_image->body_part_examined_length);
			break;
                case DATACOLLECTIONDIAMETER:
			att_value = 
			    &(dicom_image->data_collection_diameter); 
			att_length = &(dicom_image->
			    data_collection_diameter_length);
				break;
                case DEVICESERIALNUMBER:
			att_value = &(dicom_image->device_serial_number); 
			att_length =
			    &(dicom_image->device_serial_number_length);
			break;
                case DISTANCESOURCETODETECTOR:
			att_value = 
			    &(dicom_image->distance_source_to_detector); 
			att_length = &(dicom_image->
			    distance_source_to_detector_length);
			break;
                case DISTANCESOURCETOPATIENT:
			att_value = 
			    &(dicom_image->distance_source_to_patient); 
			att_length = &(dicom_image->
			    distance_source_to_patient_length);
			break;
                case IMAGECOMMENTS: 
			att_value = &(dicom_image->image_comments); 
			att_length = &(dicom_image->image_comments_length);
			break;
		case INSTITUTIONADDRESS: 
			att_value = &(dicom_image->institution_address); 
			att_length = &(dicom_image->
			    institution_address_length);
			break;
		case INSTITUTIONNAME: 
			att_value = &(dicom_image->institution_name); 
			att_length = &(dicom_image->
			    institution_name_length);
			break;
		case INSTITUTIONALDEPARTMENTNAME: 
			att_value = &(dicom_image->
			    institutional_department_name); 
			att_length = &(dicom_image->
			    institutional_department_name_length);
			break;
		case KVP: 
			att_value = &(dicom_image->kvp); 
			att_length = &(dicom_image->kvp_length);
			break;
		case MANUFACTURER: 
			att_value = &(dicom_image->manufacturer); 
			att_length = &(dicom_image->manufacturer_length);
			break;
		case MANUFACTURERMODELNAME:
			att_value = &(dicom_image->
			    manufacturer_model_name); 
			att_length = &(dicom_image->
			    manufacturer_model_name_length);
			break; 
		case MODALITY: 
			att_value = &(dicom_image->modality); 
			att_length = &(dicom_image->modality_length);
			break;
		case NAMEOFPHYSICIANREADINGSTUDY: 
			att_value = &(dicom_image->
			    name_of_physician_reading_study); 
			att_length = &(dicom_image->
			    name_of_physician_reading_study_length);
			break;
		case OPERATORNAME:
			att_value = &(dicom_image->operator_name); 
			att_length = &(dicom_image->operator_name_length);
			break;
		case PATIENTAGE:
			att_value = &(dicom_image->patient_age); 
			att_length = &(dicom_image->patient_age_length);
			break;
		case PATIENTBIRTHDATE:
			att_value = &(dicom_image->patient_birth_date); 
			att_length = &(dicom_image->
			    patient_birth_date_length);
			break;
		case PATIENTID:
			att_value = &(dicom_image->patient_id); 
			att_length = &(dicom_image->patient_id_length);
			break;
		case PATIENTNAME:
			att_value = &(dicom_image->patient_name); 
			att_length = &(dicom_image->patient_name_length);
			break;
		case PATIENTSEX:
			att_value = &(dicom_image->patient_sex); 
			att_length = &(dicom_image->patient_sex_length);
			break;
		case PERFORMINGPHYSICIANNAME:
			att_value = &(dicom_image->
			    performing_physician_name); 
			att_length = &(dicom_image->
			    performing_physician_name_length);
			break;
		case REFERRINGPHYSICIANNAME:
			att_value = &(dicom_image->
			    referring_physician_name); 
			att_length = &(dicom_image->
			    referring_physician_name_length);
			break;
		case SERIESDATE:
			att_value = &(dicom_image->series_date); 
			att_length = &(dicom_image->series_date_length);
			break;
		case SERIESINSTANCEUID:
			att_value = &(dicom_image->series_instance_uid); 
			att_length = &(dicom_image->
			    series_instance_uid_length);
			break;
		case SLICETHICKNESS:
			att_value = &(dicom_image->slice_thickness); 
			att_length = &(dicom_image->
			    slice_thickness_length);
			break;
		case SOFTWAREVERSION:
			att_value = &(dicom_image->software_version); 
                	att_length = &(dicom_image->
			    software_version_length);
			break;
		case SOPCLASSUID:
			att_value = &(dicom_image->sop_class_uid); 
                        att_length = &(dicom_image->sop_class_uid_length);
			break;
		case SOPINSTANCEUID:
			att_value = &(dicom_image->sop_instance_uid); 
			att_length = &(dicom_image->
			    sop_instance_uid_length);
			break;
		case SOURCETYPE:
			att_value = &(dicom_image->source_type); 
			att_length = &(dicom_image->source_type_length);
			break;
		case SPATIALRESOLUTION:
			att_value = &(dicom_image->spatial_resolution); 
                        att_length = &(dicom_image->
			    spatial_resolution_length);
			break;
		case STATIONNAME:
			att_value = &(dicom_image->station_name); 
                        att_length = &(dicom_image->station_name_length);
			break;
		case STUDYDESCRIPTION:
			att_value = &(dicom_image->study_description); 
                        att_length = &(dicom_image->
			    study_description_length);
			break;
		case STUDYDATE:
			att_value = &(dicom_image->study_date); 
                        att_length = &(dicom_image->study_date_length);
			break;
		case ATTRIBUTENUM:
			att_value = &(dicom_image->thumbnail_format); 
                        att_length = &(dicom_image->
			    thumbnail_format_length);
			break;
		case (ATTRIBUTENUM + 1):
			att_value = &(dicom_image->thumbnail_content); 
                        att_length =
			    &(dicom_image->thumbnail_content_length);
			break;
                }
                
                /* Get length of current attribute. */      	
		*att_length = convert_bytes_to_int(
		    (char *)bdb_data->data + offset);
		offset += sizeof(int);    
		
                /* Get content of current attribute or thumbnail. If it is 
                 * not image data, that is content of thumbnail, append 
                 * '\0' at the end of the string.
                 */                  	
                *att_value = malloc((*att_length) * sizeof(char) + 1);  
                memset(*att_value, 0, *att_length); 
                memcpy(*att_value, (char *)bdb_data->data + offset, 
                    *att_length);                      
                if (i != 4)                            
                        *(*att_value + *att_length) = '\0';  
                
                /* Move to the next attribute. */
                offset += *att_length;    
        }                      
}

/*
 * Callback function to create acquisition date secondary index. 
 */ 
int
get_acquisition_date(sdbp, pkey, pdata, skey)
	DB *sdbp;
	const DBT *pkey;
	const DBT *pdata;
	DBT *skey;
{
	return secondary_call_back(ACQUISITIONDATE, sdbp, pkey, pdata, 
	    skey);
}

/*
 * Callback function to create modality secondary index. 
 */ 
int
get_modality(sdbp, pkey, pdata, skey)
	DB *sdbp;
	const DBT *pkey;
	const DBT *pdata;
	DBT *skey;
{
	return secondary_call_back(MODALITY, sdbp, pkey, pdata, skey);
}

/*
 * Callback function to create patient name secondary index.
 */ 
int
get_patient_name(sdbp, pkey, pdata, skey)
	DB *sdbp;
	const DBT *pkey;
	const DBT *pdata;
	DBT *skey;
{
	return secondary_call_back(PATIENTNAME, sdbp, pkey, pdata, skey);
}

/*
 * Callback function to create secondary index, whose key is from  
 * att_offset in primary data.
 */ 
int 
secondary_call_back(att_offset, sdbp, pkey, pdata, skey)
	int att_offset;
	DB *sdbp;
	const DBT *pkey;
	const DBT *pdata;
	DBT *skey;
{
	u_int32_t length = 0;
			  /* The current attribute/image content length. */
	u_int32_t offset = 0;                 /* The current read bytes. */
	int i;			   /* The current read attributes/image. */

        /* If primary data doesn't exist, this record is not indexed. */
	if (pdata->size == 0)
		return (DB_DONOTINDEX);
	
	/* Move to the attribute with the number att_offset in all     
         * attributes and get the offset of its content. */
        for (i = 0;i <= att_offset;i++) {
                length = convert_bytes_to_int((char *)(pdata->data) + 
		    offset);
                if (i != att_offset)
                        offset += sizeof(u_int32_t) + length;
                else 
                        offset += sizeof(u_int32_t);
        }
        
        /* If there is no value of this attribute, nothing will be 
	 * indexed. */	          
        if (length == 0)
                return (DB_DONOTINDEX);

	/* Create secondary index from the offset in primary data. */
	memset(skey, 0, sizeof(DBT));
	skey->data = (u_int8_t *)pdata->data + offset;
	skey->size = length;

	return (EXIT_SUCCESS);
}

/*
 * Initialize all pointers and strings in struct DICOM_DBS. 
 */ 
void
initialize_dbs(dm_dbs)
	DICOM_DBS *dm_dbs;
{
        /* Initialize databases' handles. */
        dm_dbs->ad_sdbp = NULL;
	dm_dbs->dicom_dbp = NULL;
	dm_dbs->md_sdbp = NULL;
	dm_dbs->pn_sdbp = NULL;
        
        /* Initialize databases' name, including dicom primary database, 
           acquisition date, modality and patient name secondary index. */
	dm_dbs->ad_sdb_name = ACQUISITION_DATE_SECONDARY_DB_NAME;
	dm_dbs->dicom_db_name = DICOM_DB_NAME;
	dm_dbs->md_sdb_name = MODALITY_SECONDARY_DB_NAME;
	dm_dbs->pn_sdb_name = PATIENT_NAME_SECONDARY_DB_NAME;
	
	/* Initialize environment home directory. */
	dm_dbs->home_dir = DICOM_HOME;
	
	/* Initialize database file name. */
	dm_dbs->db_file_name = DB_FILE_NAME;
}

/*
 * Open database or secondary index. If it is not opened successfully, 
 * return EXIT_FAILURE. Unless return EXIT_SUCCESS. 
 */ 
int
open_db(dbpp, file_name, db_name, env, access_mode, is_secondary, txn)
	DB **dbpp;
	const char *file_name;
	const char *db_name;
	DB_ENV *env;
	u_int32_t access_mode;
	int is_secondary;
	DB_TXN *txn;
{
        DB *dbp;                 
        u_int32_t open_flags;
	int ret;
	
	open_flags = DB_CREATE;

	/* Create the database */
	ret = db_create(&dbp, env, 0);
	if (ret != 0) 
                return (EXIT_FAILURE);

	/* If it is secondary index, keys should be duplicated. */
	if (is_secondary == SEC) {
		ret = dbp->set_flags(dbp, DB_DUPSORT);
		if (ret != 0) 
                        return (EXIT_FAILURE);
	}

	/* Open database. */
	ret = dbp->open(dbp, txn, file_name, db_name, access_mode, 
            open_flags, 0);
	if (ret != 0)  
                return (EXIT_FAILURE);

        /* Point to created database. */
	*dbpp = dbp;

	return (EXIT_SUCCESS);
}

/*
 * Open primary database and all secondary indexes, acquisition date,  
 * modality and patient name secondary index.     
 */ 
int
open_dbs(dm_dbs, envp, access_mode, cache_size, log_size)
	DICOM_DBS *dm_dbs;
	DB_ENV **envp;
	u_int32_t access_mode;
	u_int32_t cache_size;
	u_int32_t log_size;
{
        DB_ENV *env;
        DB_TXN *txn;
	int ret;
	
        /* Create environment. */
        ret = open_env(&env, cache_size, log_size, dm_dbs->home_dir);
        if (ret == EXIT_FAILURE)
                return ret;
        
        /* Begin transaction. */
	ret = env->txn_begin(env, NULL, &txn, 0);
	if (ret != 0)  
		return EXIT_FAILURE; 
        
        /* Open primary database. */
        ret = open_db(&dm_dbs->dicom_dbp, dm_dbs->db_file_name, 
            dm_dbs->dicom_db_name, env, DB_BTREE, PRI, txn);
        if (ret == EXIT_FAILURE) {
                txn->abort(txn);
                return EXIT_FAILURE;
        }
        
        /* Open secondary database on patient's name and associate it with 
	 * primary database. 
	 */
        ret = open_db(&dm_dbs->pn_sdbp, dm_dbs->db_file_name, 
            dm_dbs->pn_sdb_name, env, DB_BTREE, SEC, txn);
        if (ret == EXIT_FAILURE) {
                txn->abort(txn);
                return EXIT_FAILURE;
        }
        
        ret = dm_dbs->dicom_dbp->associate(dm_dbs->dicom_dbp, txn,
            dm_dbs->pn_sdbp, get_patient_name, 0);
	if (ret != 0) {
		txn->abort(txn);
		return EXIT_FAILURE;
	}

        /* Open secondary database on modality and associate it with 
	 * primary database. 
	 */
        ret = open_db(&dm_dbs->md_sdbp, dm_dbs->db_file_name, 
            dm_dbs->md_sdb_name, env, DB_BTREE, SEC, txn);
        if (ret == EXIT_FAILURE) {
                txn->abort(txn);
                return EXIT_FAILURE;
        }
        
        ret = dm_dbs->dicom_dbp->associate(dm_dbs->dicom_dbp, txn,
            dm_dbs->md_sdbp, get_modality, 0);
	if (ret != 0) {
                txn->abort(txn);
                return EXIT_FAILURE;
        }

        /* Open secondary database on acquisition date and associate it  
	 * with primary database. 
	 */
        ret = open_db(&dm_dbs->ad_sdbp, dm_dbs->db_file_name, 
            dm_dbs->ad_sdb_name, env, DB_BTREE, SEC, txn);
        if (ret == EXIT_FAILURE) {
                txn->abort(txn);
                return EXIT_FAILURE;
        }
        
        ret = dm_dbs->dicom_dbp->associate(dm_dbs->dicom_dbp, txn,
            dm_dbs->ad_sdbp, get_acquisition_date, 0);
	if (ret != 0) {
                txn->abort(txn);
                return EXIT_FAILURE;
        }
        
        /* Commit transaction. */
        ret = txn->commit(txn, 0);
	if (ret != 0) 
		return EXIT_FAILURE;
	
	*envp = env;
        
        return ret;    
}

/*
 * Open an environment in the home directory.
 */ 
int
open_env(envp, cache_size, log_size, home_dir)
	DB_ENV **envp;
	u_int32_t cache_size;
	u_int32_t log_size;
	const char *home_dir;
{
	DB_ENV *env;
        u_int32_t env_flags;
	int ret;

        /* Open flag for environment. */
        env_flags = DB_CREATE | DB_INIT_LOG | DB_INIT_TXN | DB_INIT_MPOOL;

	/* Create the environment. */
	ret = db_env_create(&env, 0);
	if (ret != 0)
		return EXIT_FAILURE;

	/* Set the cache size of the environment. */
	ret = env->set_cachesize(env, 0, cache_size, 0);
	if (ret != 0) 
                return EXIT_FAILURE;

	/* Set the log size of the environment. */
	ret = env->set_lg_bsize(env, log_size);
	if (ret != 0) 
		return EXIT_FAILURE;

	/* Open new environment with recovery, log, transaction, memory
	 * pool subsystem in homr directory.
	 */
	ret = env->open(env, home_dir, env_flags, 0755);
	if (ret != 0) 
		return EXIT_FAILURE;

	/* Point to created environment. */
	*envp = env;     	

	return EXIT_SUCCESS;
}

/*
 * Partially update the specified record by id in bdb. Start from doff, 
 * overwrite the old record for oringinal_length bytes with the stream 
 * for new_length bytes.  
 */ 
int 
partial_write(id, stream, doff, original_length, new_length, dbp, txn)
        int id;
	char *stream;
	int doff;
	int original_length;
	int new_length;
        DB *dbp;
        DB_TXN *txn;
{
        DBT bdb_key, bdb_data;
        int ret; 
        
        /* Zero out DBTs. */
        memset(&bdb_key, 0, sizeof(DBT));
	memset(&bdb_data, 0, sizeof(DBT));

        /* Initialize key DBT with id. */
	bdb_key.data = &id;
	bdb_key.size = (u_int32_t) sizeof(int);

	bdb_data.data = stream;                            /* New bytes. */
	bdb_data.size = new_length;          /* Length of the new bytes. */
	bdb_data.doff = doff;
	                  /* Offset of the partial record being written. */
	bdb_data.dlen = original_length;      /* Length of the old bytes. */
	bdb_data.flags = DB_DBT_PARTIAL;          /* Do partial storage. */
	ret = dbp->put(dbp, txn, &bdb_key, &bdb_data, 0);
	
	return ret;
}

/*
 * Put the cursor on the matching record that is equal to the value. If 
 * there is no matching record, return NULL. 
 */ 
DBC *
position_cursor(att_index, value, cursorpp)
	int att_index;
	char **value;
	DBC **cursorpp;
{
	DBT bdb_key, bdb_data; 
	int ret, s_att_index; 
        
	memset(&bdb_key, 0, sizeof(DBT));
        memset(&bdb_data, 0, sizeof(DBT));

        /* Get the index of current attribute in indexed attributes, 
         * patient's name, modality and acquisition date.
         */          
	if (att_index == PATIENTNAME)
		s_att_index = S_PNOFFSET;

	if (att_index == MODALITY)
		s_att_index = S_MDOFFSET;
	
	if (att_index == ACQUISITIONDATE)
	       s_att_index = S_ADOFFSET;

        /* Set DBT to be the requested value. */
	bdb_key.data = value[att_index];
	bdb_key.size =  (u_int32_t)strlen(value[att_index]);

        /* Position the corresponding cursor to record that has the same 
         * key as requested value. */
	ret = cursorpp[s_att_index]->get(cursorpp[s_att_index], &bdb_key, 
            &bdb_data, DB_SET);
	if (ret != 0)
        	return NULL;

	return cursorpp[s_att_index];
}

/*
 * Get content of attribute on the position of att_index in all attributes 
 * and return its offset and length in DICOM_IMAGE. if the pointer offset, 
 * is not NULL, return the offset. If the pointer length is not NULL,  
 * return its length.
 */ 
char *
position_dicom_field(att_index, image, offset, length)
	int att_index;
	DICOM_IMAGE *image;
	int *offset;
	int *length;
{
	char *p;
	int idx, count, loffset, llength;

	count = (offset == NULL) ? att_index: 0; /* The end of the loop. */
	loffset = 0; 
	           /* Offset from attribute on att_index to current one. */
	llength = 0;
	p = NULL;          /* The content of the attribute on att_index. */
	for (idx = att_index;idx >= count;idx--) {
        	switch (idx) {
        	case ACCESSIONNUMBER: 
                        if (idx < att_index)  
                                loffset += 
				    image->accession_number_length + 4;
                        if (idx == att_index) {
                                p = image->accession_number; 
                                llength = image->accession_number_length;
                        }
                        break;
        	case ACQUISITIONDATE: 
        		if (idx < att_index) 
                                loffset += 
				    image->acquisition_date_length + 4;
                        if (idx == att_index) {
                                p = image->acquisition_date;
                                llength = image->acquisition_date_length; 
                        }
                        break;
        	case ACQUISITIONTIME: 
        		if (idx < att_index) 
                                loffset += 
				    image->acquisition_time_length + 4;
                        if (idx == att_index) {
                                p = image->acquisition_time; 
                                llength = image->acquisition_time_length; 
                        }
                        break;
        	case BODYPARTEXAMINED: 
        		if (idx < att_index) 
                                loffset += 
				    image->body_part_examined_length + 4;
                        if (idx == att_index) {
                                p = image->body_part_examined; 
                                llength = image->body_part_examined_length;
			}
                        break;
        	case DATACOLLECTIONDIAMETER:
                        if (idx < att_index) 
                                loffset += image->
                                    data_collection_diameter_length + 4;
                        if (idx == att_index) {
                                p = image->data_collection_diameter; 
                                llength = image->
                                    data_collection_diameter_length;
                        }
                        break;
        	case DEVICESERIALNUMBER:
                        if (idx < att_index) 
                                loffset += image->
                                    device_serial_number_length + 4;
                        if (idx == att_index) {
                                p = image->device_serial_number; 
                                llength = 
				    image->device_serial_number_length;
                        }
                        break;
        	case DISTANCESOURCETODETECTOR:
                        if (idx < att_index) 
                                loffset += image->
                                    distance_source_to_detector_length + 4;
                        if (idx == att_index) {
                                p = image->distance_source_to_detector;
                                llength = image->
                                    distance_source_to_detector_length;
                        } 
                        break;
        	case DISTANCESOURCETOPATIENT:
                        if (idx < att_index) 
                                loffset += image->
                                    distance_source_to_patient_length + 4;
                        if (idx == att_index) {
                                p = image->distance_source_to_patient;
                                llength = image->
                                    distance_source_to_patient_length;
                        }  
                        break;
        	case IMAGECOMMENTS: 
                        if (idx < att_index) 
                                loffset += 
				    image->image_comments_length + 4;
                        if (idx == att_index) {
                                p = image->image_comments; 
                                llength = image->image_comments_length;
                        } 
                        break;
		case INSTITUTIONADDRESS: 
                        if (idx < att_index) 
                                loffset += image->
                                    institution_address_length + 4;
                        if (idx == att_index) { 
                                p = image->institution_address; 
                                llength = 
				    image->institution_address_length;
                        } 
                        break;
        	case INSTITUTIONNAME: 
                        if (idx < att_index)
                                loffset += 
				    image->institution_name_length + 4;
                        if (idx == att_index) {
                                p = image->institution_name; 
                                llength = image->institution_name_length;
                        } 
                        break;
        	case INSTITUTIONALDEPARTMENTNAME: 
                        if (idx < att_index) 
                                loffset += image->
				    institutional_department_name_length +
				    4;
                        if (idx == att_index) {
                                p = image->institutional_department_name; 
                                llength = image->
                                    institutional_department_name_length;
                        } 
                        break;
        	case KVP: 
                        if (idx < att_index) 
                                loffset += image->kvp_length + 4;
                        if (idx == att_index) {
                                p = image->kvp;  
                                llength = image->kvp_length;
                        }
                        break;
        	case MANUFACTURER: 
                        if (idx < att_index) 
                                loffset += image->manufacturer_length + 4;
                        if (idx == att_index) {
                                p = image->manufacturer;  
                                llength = image->manufacturer_length;
                        }
                        break;
        	case MANUFACTURERMODELNAME:
                        if (idx < att_index) 
                                loffset += image->
                                    manufacturer_model_name_length + 4;
                        if (idx == att_index) { 
                                p = image->manufacturer_model_name; 
                                llength = 
				    image->manufacturer_model_name_length;
                        } 
                        break; 
        	case MODALITY: 
                        if (idx < att_index) 
                                loffset += image->modality_length + 4;
                        if (idx == att_index) { 
                                p = image->modality; 
                                llength = image->modality_length;
                        } 
                        break;
        	case NAMEOFPHYSICIANREADINGSTUDY: 
                        if (idx < att_index) 
                                loffset += image->
                                    name_of_physician_reading_study_length
				     + 4;
                        if (idx == att_index) { 
                                p = image->name_of_physician_reading_study;
				llength = image->
                                    name_of_physician_reading_study_length;
                        } 
                        break;
        	case OPERATORNAME:
                        if (idx < att_index) 
                                loffset += image->operator_name_length + 4;
                        if (idx == att_index) { 
                                p = image->operator_name; 
                                llength = image->operator_name_length;
                        } 
                        break;
        	case PATIENTAGE:
                        if (idx < att_index) 
                                loffset += image->patient_age_length + 4;
                        if (idx == att_index) { 
                                p = image->patient_age;  
                                llength = image->patient_age_length;
                        }
                        break;
        	case PATIENTBIRTHDATE:
                        if (idx < att_index) 
                                loffset += 
				    image->patient_birth_date_length + 4;
                        if (idx == att_index) { 
                                p = image->patient_birth_date;  
                                llength = image->patient_birth_date_length;
                        }
                        break;
        	case PATIENTID:
                        if (idx < att_index) 
                                loffset += image->patient_id_length + 4;
                        if (idx == att_index) { 
                                p = image->patient_id; 
                                llength = image->patient_id_length;
                        } 
                        break;
        	case PATIENTNAME:
                        if (idx < att_index)  
                                loffset += image->patient_name_length + 4;
                        if (idx == att_index) {  
                                p = image->patient_name;  
                                llength = image->patient_name_length;
                        }
                        break;
        	case PATIENTSEX:
                        if (idx < att_index)  
                                loffset += image->patient_sex_length + 4;
                        if (idx == att_index) {  
                                p = image->patient_sex;  
                                llength = image->patient_sex_length;
                        }
                        break;
        	case PERFORMINGPHYSICIANNAME:
                        if (idx < att_index)  
                                loffset += image->
                                    performing_physician_name_length + 4;
                        if (idx == att_index) {  
                                p = image->performing_physician_name; 
                                llength = image->
                                    performing_physician_name_length;
                        } 
                        break;
        	case REFERRINGPHYSICIANNAME:
                        if (idx < att_index)  
                                loffset += image->
                                    referring_physician_name_length + 4;
                        if (idx == att_index) {  
                                p = image->referring_physician_name; 
                                llength = 
				    image->referring_physician_name_length;
                        } 
                        break;
        	case SERIESDATE:
                        if (idx < att_index)  
                                loffset += image->series_date_length + 4;
                        if (idx == att_index) {  
                                p = image->series_date;  
                                llength = image->series_date_length;
                        }
                        break;
        	case SERIESINSTANCEUID:
                        if (idx < att_index)  
                                loffset += image->
                                    series_instance_uid_length + 4;
                        if (idx == att_index) {  
                                p = image->series_instance_uid;  
                                llength = 
				    image->series_instance_uid_length;
                        }
                        break;
        	case SLICETHICKNESS:
                        if (idx < att_index)  
                                loffset += 
				    image->slice_thickness_length + 4;
                        if (idx == att_index) {  
                                p = image->slice_thickness; 
                                llength = image->slice_thickness_length;
                        } 
                        break;
        	case SOFTWAREVERSION:
                        if (idx < att_index)  
                                loffset += 
				    image->software_version_length + 4;
                        if (idx == att_index) {  
                                p = image->software_version; 
                                llength = image->software_version_length;
                        } 
                        break;
        	case SOPCLASSUID:
                        if (idx < att_index)  
                                loffset += image->sop_class_uid_length + 4;
                        if (idx == att_index) {  
                                p = image->sop_class_uid;  
                                llength = image->sop_class_uid_length;
                        }
                        break;
        	case SOPINSTANCEUID:
                        if (idx < att_index)  
                                loffset += 
				    image->sop_instance_uid_length + 4;
                        if (idx == att_index) {  
                                p = image->sop_instance_uid;  
                                llength = image->sop_instance_uid_length;
                        }
                        break;
        	case SOURCETYPE:
                        if (idx < att_index)  
                                loffset += image->source_type_length + 4;
                        if (idx == att_index) {  
                                p = image->source_type; 
                                llength = image->source_type_length;
                        } 
                        break;
        	case SPATIALRESOLUTION:
                        if (idx < att_index)  
                                loffset +=
				    image->spatial_resolution_length + 4;
                        if (idx == att_index) {  
                                p = image->spatial_resolution; 
                                llength = image->spatial_resolution_length;
                        } 
                        break;
        	case STATIONNAME:
                        if (idx < att_index)  
                                loffset += image->station_name_length + 4;
                        if (idx == att_index) {  
                                p = image->station_name; 
                                llength = image->station_name_length;
                        } 
                        break;
        	case STUDYDESCRIPTION:
                        if (idx < att_index)  
                                loffset +=
				    image->study_description_length + 4;
                        if (idx == att_index) {  
                                p = image->study_description;  
                                llength = image->study_description_length;
                        }
                        break;
        	case STUDYDATE:
                        if (idx < att_index)  
                                loffset += image->study_date_length + 4;
                        if (idx == att_index) {  
                                p = image->study_date; 
                                llength = image->study_date_length;
                        }
                        break;
                }
	}
	
	/* Return the offset if needed. */
	if (offset != NULL)
	       *offset = loffset;
	
	/* Return the length if needed. */
        if (length != NULL)
                *length = llength;	       
           	       
	return p;
}

/*
 * Retrieve all DICOM from primary database.
 */ 
int
retrieve_all_dicom(dbs, env, txn, dicom_list, number, all)
	DICOM_DBS *dbs;
	DB_ENV *env;
	DB_TXN *txn;
	DICOM_IMAGE *dicom_list;
	int *number;
	int all;
{
        DBC *cursor;
        DBT bdb_key, bdb_data;
        DICOM_IMAGE *lp;
        int ret, cret, i, length, offset;

	lp = dicom_list;                       /* Pointer to DICOM list. */
	length = 0;                  /* The length of current attribute. */
	*number = 0;                       /* Number of retrieved DICOM. */
	
	/* Initialize DBTs. */
        memset(&bdb_key, 0, sizeof(DBT));
        memset(&bdb_data, 0, sizeof(DBT));
	
        /* Create a cursor to traverse through all DICOM stored in bdb. */
        ret = dbs->dicom_dbp->cursor(dbs->dicom_dbp, txn, &cursor, 0);
        if (ret != 0) 
                return EXIT_FAILURE;
        
        /* Get all DICOM and put them into a DICOM image list. */
        while ((cret = cursor->get(cursor, &bdb_key, &bdb_data, DB_NEXT)) 
            == 0) {
                /* Get DICOM id from data in key. */
                lp->id = *((int*)bdb_key.data);

                /* If all attributes and thumbnail are requested, get 
                 * complete DICOM from bdb. Unless get patient's name, 
                 * modality and acquisition date. */
		if (all == 1)
			get_dicom_image_from_dbt(&bdb_data, lp);
		else {
                        offset = 0;           /* The current read bytes. */
			i = 0;           /* The current read attributes. */
			while (i <= PATIENTNAME) {
                                /* Get the length of next attribute. */
                                length = convert_bytes_to_int(
                                    (char *)bdb_data.data + offset);

                                /* Move to the content of attribute. */
				offset += 4;
                                
                                /* If the current attribute is acquisition 
                                   date, modality or patient's name, copy 
                                   content to the attribute of the current 
                                   DICOM in the returned list. */
				if (i == ACQUISITIONDATE) {
					lp->acquisition_date_length = 
					    length;
					lp->acquisition_date = (char *)
                                            malloc(sizeof(char) * (
					    length + 1));
					memset(lp->acquisition_date, 0, 
                                            sizeof(char) * (length + 1));
					memcpy(lp->acquisition_date, 
                                            (char*)bdb_data.data + offset, 
                                            sizeof(char) *length);
					lp->acquisition_date[length] = 
					    '\0';
                                } else if (i == MODALITY) {
					lp->modality_length =  length;
					lp->modality = (char *)malloc(
                                            sizeof(char) * (length + 1));
					memset(lp->modality, 0, 
                                            sizeof(char) * (length + 1));
					memcpy(lp->modality, 
                                            (char*)bdb_data.data + offset, 
                                            sizeof(char) *length);
					lp->modality[length] = '\0';
				} else if (i == PATIENTNAME) {
					lp->patient_name_length =  length;
					lp->patient_name = (char *)malloc(
                                            sizeof(char) * (length + 1));
					memset(lp->patient_name, 0, 
                                            sizeof(char) * (length + 1));
					memcpy(lp->patient_name, 
                                            (char*)bdb_data.data + offset, 
                                            sizeof(char) *length);
					lp->patient_name[length] = '\0';
				} 
				
				/* Move to the end of current attribute. */
				offset += length;
				
				/* Move to the next attribute. */
				i++;
			}
		}
		
		/* Move to the next DICOM. */
                lp++;
                
                /* Increase the DICOM number by 1. */
	       (*number)++;
        }

        /* No more DICOM. */
	if (cret == DB_NOTFOUND) 
                cret = 0;

        /* Close the cursor. */
        ret = cursor->close(cursor);
        if (ret != 0) 
                return (EXIT_FAILURE);
        
	return (cret || ret) ? EXIT_FAILURE: EXIT_SUCCESS;
}

/*
 * Retrieve all keys in a list with cursor and return the number. 
 */ 
int 
retrieve_key_with_cursor(env, cursor, key, number)
	DB_ENV *env;
	DBC *cursor;
	char** key;
	int *number;
{
	DBT bdb_key, bdb_data;
	int ret, flag;
	char *p;

	/* Initialize pointers and structures */
	*number = 0;
	memset(&bdb_key, 0, sizeof(DBT));
        memset(&bdb_data, 0, sizeof(DBT));

        /* Traverse through bdb to get all keys from the first record to  
         * the last. */
	flag = DB_FIRST;
        while ((ret = cursor->get(cursor, &bdb_key, &bdb_data, flag)) 
	    == 0) {    
		p = (char *)malloc(sizeof(char) * 50);
		memset(p, 0, 50);
		memcpy(p, bdb_key.data, bdb_key.size);
                key[(*number)]= p;
                
	       (*number)++;
	       
	       /* Skip duplicate records. */
	       if (flag == DB_FIRST)
		       flag = DB_NEXT_NODUP;
        }

	if (ret == DB_NOTFOUND) {
		ret = 0;
	} else if (ret != 0) 
		ret = EXIT_FAILURE;

	return ret;
}

/*
 * Query matching DICOM with cursor in secondary index. Return a list  
 * of retrieved DICOMs and the number. 
 */ 
int
retrieve_dicom_with_cursor(cursor, query, list, number)
	DBC *cursor;
	char * query;
	DICOM_IMAGE *list;
	int *number;
{
	DBT bdb_skey, bdb_data;
	DICOM_IMAGE *listp;
	int cret, flag;

	/* Zero out structures before using them. */
	listp = list;
	memset(&bdb_skey, 0, sizeof(DBT));
        memset(&bdb_data, 0, sizeof(DBT));
        
        /* Initialize DBTs. */
        bdb_skey.data = query;
	bdb_skey.size = (u_int32_t)strlen(query);
        
        /* The number of results in the hits. */
        *number = 0;    

	/* Retrieve all DICOMs that have the matching attribute with 
         * cursor on the secondary index.
         */          
	flag = DB_SET;
	while ((cret = cursor->get(cursor, &bdb_skey, &bdb_data, flag)) 
            == 0) {
                /* Retrieve DICOM_IMAGE struct from bdb_data, add it to  
                 * the end of returned list and increase the number by 1.
                 */                  
		memset(listp, 0, sizeof(DICOM_IMAGE));
		get_dicom_image_from_dbt(&bdb_data, listp);
                listp++;
	       (*number)++;
	       
                /* Get to the next duplicate key in the secondary index. */
                flag = DB_NEXT_DUP;
	}

	/* DB_NOTFOUND is the only non-zero successful value. */
	if (cret == DB_NOTFOUND) 
	       cret = 0;

	return cret;
}

/*
 * Given query on any attribute, retrieve matching DICOM from the  
 * specified database. A list of DICOM_IMAGE and its number are 
 * returned . 
 */ 
int 
retrieve_dicom_with_dbp(dbp, txn, attribute, query, list, number)
	DB *dbp;
	DB_TXN *txn;
	char *attribute;
	char * query;
	DICOM_IMAGE *list;
	int *number;
{
	DBC *cursor;
	DBT bdb_key, bdb_data;
        DICOM_IMAGE *listp;
	DICOM_IMAGE dicom_image;
        int cret, ret, idx, i, flag;
	u_int32_t length = 0;
	u_int32_t offset = 0;
	char value[40];

        /* Find the requested attribute index in all attributes. */
        idx = find_attributes_index(attribute);
	
	/* Zero out structures before using them. */
	listp = list;
	memset(&bdb_key, 0, sizeof(DBT));
        memset(&bdb_data, 0, sizeof(DBT));
	memset(&dicom_image, 0, sizeof(DICOM_IMAGE));
        
        /* The number of results in the hits. */
        *number = 0;    
        
        /* Create a cursor in the database. */
        ret = dbp->cursor(dbp, txn, &cursor, 0);
        if (ret != 0)
                return (EXIT_FAILURE);                

	/* Traverse through all DICOM in database and find out those who 
         * have the same value as query on the specified attribute. All  
         * matching DICOMs are added to a list and the number is  
         * increasing accordingly. 
     	 */
	flag = DB_FIRST;
	while ( (cret = cursor->get(cursor, &bdb_key, &bdb_data, flag))
            == 0) {
                offset = 0;       /* The current read bytes in bdb_data. */
                for (i = 0;i <= idx;i++) {
                        /* Get the length of next attribute. */
                        length = convert_bytes_to_int((char *)(
			    bdb_data.data) + offset);
                        
                        /* If it doesn't reach the requested attribute, go
                         * through the current attribute length as well as 
                         * its content. Unless, move to the first byte of 
                         * its content. */
                        if (i != idx)
                                offset += sizeof(u_int32_t) + length;
                        else 
                                offset += sizeof(u_int32_t);
                }

                /* Get content of the current attribute and compare it  
                 * with the query. If they are the same, retrieve  
                 * DICOM_IMAGE from the bdb_data and add it to the list.
                 */                  
                memcpy(value, (char *)bdb_data.data + offset, length);
                value[length] = '\0';
                if (strcmp(value, query) == 0) {
                        listp->id = *(int*)(bdb_key.data);
                        memset(listp, 0, sizeof(DICOM_IMAGE));
                        get_dicom_image_from_dbt(&bdb_data, listp);
                        listp++;
                        (*number)++;
	       }
	       
               flag = DB_NEXT;
	}

	/* If there is no more record, quit the loop. */		
        if (cret == DB_NOTFOUND) 
                cret = 0;
        
        /* Close the cursor. */
        ret = cursor->close(cursor);
  
        return (cret || ret) ? EXIT_FAILURE : EXIT_SUCCESS;
}

/*
 * When more than one attributes are queried, retrieve DICOMs who have the 
 * same attribute contents as the array, value, on the same attributes. If
 * any indexed attributes are queried, use join cursor to retrieve subset 
 * of DICOMs in bdb on those indexed attributes first. Then find the 
 * matching DICOMs on other attributes in the subset one by one. Any  
 * matching DICOMs appends to the list and the number is increasing 
 * accordingly .   
 */ 
int
retrieve_dicom_with_multiple_attributes
    (env, dbp, scursorpp, queried, value, list, number)
	DB_ENV *env;
	DB *dbp;
	DBC **scursorpp;
	BOOL *queried;
	char **value;
	DICOM_IMAGE *list;
	int *number;
{
        /* Array of cursor created on any requested secondary index. */ 
	DBC *carray[SEC_INDEX_NUM + 1]; 
	/* Cursor to do join query. */
	DBC *join_cursor;                    
        DBC *cursor;               
	DBT bdb_key, bdb_data;
	DICOM_IMAGE dicom_image;
	char *dicom_value;
	int i, j, ret;
	
	*number = 0;
	memset(&bdb_key, 0, sizeof(DBT));
        memset(&bdb_data, 0, sizeof(DBT));
        memset(&dicom_image, 0, sizeof(DICOM_IMAGE));

        /* Go through all attributes. If the current attribute is requested
         * in the queried array, that is it is equal to 1, and it is one of
         * indexed attributes, position the cursor to the record that has 
         * the requested value and add it to the cursor array, carray. If 
         * there is no matching record on any cursor, return with no error
         * but with none retrieved record.
         */
	for (i = 0, j = 0; i < ATTRIBUTENUM;i++) {
		if (queried[i] == 1 && (i == PATIENTNAME || 
		    i == MODALITY || i == ACQUISITIONDATE) ) {
			cursor = position_cursor(i, value, scursorpp);
			if (cursor == NULL)
				return EXIT_SUCCESS;
			carray[j++] = cursor;
		}
	}

	carray[j] = NULL;

        /* Create a join cursor on the cursor array, carray. */
	if ((ret = dbp->join(dbp, carray, &join_cursor, 0)) != 0)
		return (EXIT_FAILURE);
	
	/* Iterate over records with join cursor and compare with value 
         * array on other non-indexed attributes. 
         */
	while((ret = join_cursor->get(join_cursor, &bdb_key, &bdb_data, 0))
            == 0) {
                /* Get DICOM_IMAGE from bdb_data. */
		get_dicom_image_from_dbt(&bdb_data, &dicom_image);
		
		/* Go through non-indexed attributes, retrieve each content
                 * and compare it with the one in value array. If any one
                 * doesn't match, there is no need to continue.
                 */
		for (i = 0; i < ATTRIBUTENUM; i++){
			if (queried[i] == 1) {
				dicom_value = position_dicom_field(
                                    i, &dicom_image, NULL, NULL);
				if (strcmp(dicom_value, value[i]) != 0) 
					break;
			}
		}

                /* If any attribute doesn't match, get the next record. */
		if (i < ATTRIBUTENUM)
			break;
		
		/* All attributes match the value array, add it to the list
                 * and increase the number by 1. 
                 */
		list[*number] = dicom_image;
		(*number)++;
	}

        /* Close join cursor. */
        ret = join_cursor->close(join_cursor);

	return ((ret == 0) || (ret == DB_NOTFOUND)) ? 
            EXIT_SUCCESS : EXIT_FAILURE;
}

/*
 * Retrieve DICOM who has id as its key in DICOM_IMAGE and return the  
 * number of retrieved DICOM. If any error occurs, return -1. 
 */ 
int
retrieve_dicom_with_id(dbp, env, txn, id, dicom_image)
	DB *dbp;
	DB_ENV *env;
	DB_TXN *txn;
	int id;
	DICOM_IMAGE *dicom_image;
{
        DBT bdb_key, bdb_data;
        DBC *cursor;
	int ret, cret, flag, number;

	/* Zero out structures before using them. */
	memset(&bdb_key, 0, sizeof(DBT));
        memset(&bdb_data, 0, sizeof(DBT));
        memset(dicom_image, 0, sizeof(DICOM_IMAGE));
        
        /* Initialize DBT with id as its key. */
        bdb_key.data = &id;
	bdb_key.size = (u_int32_t)sizeof(int);   
        
        /* Create a cursor to traverse through all DICOM in bdb. */
        ret = dbp->cursor(dbp, txn, &cursor, 0);
        if (ret != 0)
                return (EXIT_FAILURE);
	
        number = 0;                     
	flag = DB_SET;	                
	for (;;) {
                /* Get DICOM whose key is the same as id. */
		cret = cursor->get(cursor, &bdb_key, &bdb_data, flag);
		
		/* If error occurs, abort the transaction and break the 
                 * iteration. DB_NOTFOUND should be considered correct.
		 */
                if (cret != 0) {
			if (cret != DB_NOTFOUND) 
                                txn->abort(txn);
			else 
                                cret = 0; 
                        break;
		}
		
		/* Get DICOM_IMAGE from bdb_data. */
                get_dicom_image_from_dbt(&bdb_data, dicom_image);
                dicom_image->id = id;
                
                /* Increase the number by 1. */
                number++;
                
                /* Get the next duplicate record. */
                flag = DB_NEXT_DUP;   
	}
        
        /* Close the cursor. */
        ret = cursor->close(cursor);         
        
        if ((cret == 0) && (ret == 0))
                return number;
        else 
                return -1;
}

/*
 * Write stream, that is serialized DICOM, into bdb. 
 */ 
int 
write_stream_into_bdb(stream, length, dbs, txn)
        char* stream;
	int length;
        DICOM_DBS *dbs;
        DB_TXN *txn;
{
        DBT bdb_key, bdb_data;
        char *content;/* All attributes and thumbnail of a single DICOM. */
        int coffset = 0;                      /* The current read bytes. */
        int count= 0;                /* The number of DICOM from stream. */
	int dicom_id;                        /* The id of current DICOM. */
	int i = 0;                            /* The current attributes. */
        int size = 0;        /* The size of current attribute and image. */
	int ret;

        if (length < 0)
                return -1;
        else if (length == 0)
                return 0;
        
        /* Get DICOM from the stream. Store the DICOM into bdb with id as  
         * key, its attributes and thumbnail as data.
         */                
        while(coffset < length) {   
                /* Zero out DBTs for key and data. */
		memset(&bdb_key, 0, sizeof(DBT));
		memset(&bdb_data, 0, sizeof(DBT));
                
		/* Get DICOM id and put it into bdb_key DBT. */	
		size = convert_bytes_to_int(stream + coffset);
		coffset += sizeof(int);
		dicom_id = convert_bytes_to_int(stream + coffset);
		bdb_key.data = &dicom_id;
		bdb_key.size = size;

                /* Get all attributes, thumbnail's format and its content
                 * of the current DICOM and put them into bdb_data DBT. 
                 * Any attribute or thumbnail's format could be considered 
                 * as a field in the target DBT. Each field is composed of 
                 * the content and its length.
                 */                             
		coffset += size;	
		for (i = 0; i < ATTRIBUTENUM + 2; i++) {
			/* Get byte size of length at the current field. */
			size = convert_bytes_to_int(stream + coffset);
			coffset += sizeof(int);

			/* Get byte size of content at the current field. 
			 */
			coffset += size;
		}
                
                /* Put DICOM attributes and thumbnail into DBTs. */	
		content = malloc(coffset * sizeof(char));
		memset(content, 0, coffset);
		memcpy(content, stream + bdb_key.size + sizeof(int), 
                    coffset - bdb_key.size - sizeof(int));
		bdb_data.data = content;
		bdb_data.size = coffset - bdb_key.size - sizeof(int);

                /* Put the current DICOM into bdb. */
		ret = dbs->dicom_dbp->put(dbs->dicom_dbp, txn, &bdb_key, 
                    &bdb_data, 0);
                if (ret == 0)
                        count++;
                else 
                        break;
                
                /* Free the malloced buffer. */
		free(content);
        }
        
        /* If coffset is larger than length, there might be some missed 
         * bytes that the stream is in a incomplete state.
	 */
        if ((coffset > length) || (ret != 0)) {
                return -1;
        }
        
        /* Return the number of DICOMs that have been stored into bdb. */
        return count;
}


