/* 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"
};

char *fields_name[ATTRIBUTENUM] = {
	"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_model_name",
	"modality",
	"name_of_physician_reading_study",
	"operator_name",
	"patient_age",
	"patient_birth_date",
	"patient_id",
	"patient_name",
	"patient_sex",
	"performing_physician_name",
	"referring_physician_name",
	"series_date",
	"series_instance_uid",
	"slice_thickness",
	"software_version",
	"sop_class_uid",
	"sop_instance_uid",
	"source_type",
	"spatial_resolution",
	"station_name",
	"study_date",
	"study_description"
};

const char *create_index_sql1 = "create index if not exists index1 on dicom(patient_name);";
const char *create_index_sql2 = "create index if not exists index2 on dicom(modality);";
const char *create_index_sql3 = "create index if not exists index3 on dicom(acquisition_date);";
const char *blob_set_sql = "PRAGMA large_record_opt = 2048;";

const char *create_table_sql = "create table dicom(\
	id integer primary key, \
	accession_number text, \
	acquisition_date text, \
	acquisition_time text, \
	body_part_examined text, \
	data_collection_diameter text, \
	device_serial_number text, \
	distance_source_to_detector text, \
	distance_source_to_patient text, \
	image_comments text, \
	institution_address text, \
	institution_name, \
	institutional_department_name, \
	kvp text, \
	manufacturer text, \
	manufacturer_model_name text, \
	modality text, \
	name_of_physician_reading_study text, \
	operator_name text, \
	patient_age text, \
	patient_birth_date text, \
	patient_id text, \
	patient_name text, \
	patient_sex text, \
	performing_physician_name text, \
	referring_physician_name text, \
	series_date text, \
	series_instance_uid text, \
	slice_thickness text, \
	software_version text, \
	sop_class_uid text, \
	sop_instance_uid text, \
	source_type text, \
	spatial_resolution text, \
	station_name text, \
	study_date text, \
	study_description text, \
	thumbnail_format text, \
	thumbnail_content blob);" ;

#define EXEC_SQL(db, sql) do { \
	rc = sqlite3_exec((db), (sql), NULL, 0, NULL); \
} while(0)

/*
 * 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)
	DICOM_DBS *dm_dbs;
{
        int rc = SQLITE_OK;
        
	if (dm_dbs->db != NULL) {
		rc = sqlite3_close(dm_dbs->db);
		dm_dbs->db = NULL;
	}

	return EXIT_SUCCESS; 
}

static void init_image(DICOM_IMAGE *dicom) {
	dicom->accession_number = &dicom->empty[0];
	dicom->acquisition_date = &dicom->empty[0];
	dicom->acquisition_time = &dicom->empty[0];
	dicom->body_part_examined = &dicom->empty[0];
	dicom->data_collection_diameter = &dicom->empty[0];
	dicom->device_serial_number = &dicom->empty[0];
	dicom->distance_source_to_detector = &dicom->empty[0];
	dicom->distance_source_to_patient = &dicom->empty[0];
	dicom->image_comments = &dicom->empty[0];
	dicom->institution_address = &dicom->empty[0];
	dicom->institution_name = &dicom->empty[0];
	dicom->institutional_department_name = &dicom->empty[0];
	dicom->kvp = &dicom->empty[0];
	dicom->manufacturer = &dicom->empty[0];
	dicom->manufacturer_model_name = &dicom->empty[0];
	dicom->modality = &dicom->empty[0];
	dicom->name_of_physician_reading_study = &dicom->empty[0];
	dicom->operator_name = &dicom->empty[0];
	dicom->patient_age = &dicom->empty[0];
	dicom->patient_birth_date = &dicom->empty[0];
	dicom->patient_id = &dicom->empty[0];
	dicom->patient_name = &dicom->empty[0];
	dicom->patient_sex = &dicom->empty[0];
	dicom->performing_physician_name = &dicom->empty[0];
	dicom->referring_physician_name = &dicom->empty[0];
	dicom->series_date = &dicom->empty[0];
	dicom->series_instance_uid = &dicom->empty[0];
	dicom->slice_thickness = &dicom->empty[0];
	dicom->software_version = &dicom->empty[0];
	dicom->sop_class_uid = &dicom->empty[0];
	dicom->sop_instance_uid = &dicom->empty[0];
	dicom->source_type = &dicom->empty[0];
	dicom->spatial_resolution = &dicom->empty[0];
	dicom->station_name = &dicom->empty[0];
	dicom->study_date = &dicom->empty[0];
	dicom->study_description = &dicom->empty[0];
	dicom->thumbnail_format = &dicom->empty[0];
}

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));
	init_image(des_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, 1);    
	copy_memory(src_dicom_image->acquisition_date, 
	    &(des_dicom_image->acquisition_date),
	    src_dicom_image->acquisition_date_length, 1);    
	copy_memory(src_dicom_image->acquisition_time, 
	    &(des_dicom_image->acquisition_time),
	    src_dicom_image->acquisition_time_length, 1);    
	copy_memory(src_dicom_image->body_part_examined, 
	    &(des_dicom_image->body_part_examined),
	    src_dicom_image->body_part_examined_length, 1);   
	copy_memory(src_dicom_image->data_collection_diameter, 
	    &(des_dicom_image->data_collection_diameter),
	    src_dicom_image->data_collection_diameter_length, 1);   
	copy_memory(src_dicom_image->device_serial_number, 
	    &(des_dicom_image->device_serial_number),
	    src_dicom_image->device_serial_number_length, 1);   
	copy_memory(src_dicom_image->distance_source_to_detector, 
	    &(des_dicom_image->distance_source_to_detector),
	    src_dicom_image->distance_source_to_detector_length, 1);   
	copy_memory(src_dicom_image->distance_source_to_patient, 
	    &(des_dicom_image->distance_source_to_patient),
	    src_dicom_image->distance_source_to_patient_length, 1); 
	copy_memory(src_dicom_image->image_comments, 
	    &(des_dicom_image->image_comments),
	    src_dicom_image->image_comments_length, 1);    
	copy_memory(src_dicom_image->institution_address, 
	    &(des_dicom_image->institution_address),
	    src_dicom_image->institution_address_length, 1);    
	copy_memory(src_dicom_image->institution_name, 
	    &(des_dicom_image->institution_name),
	    src_dicom_image->institution_name_length, 1);    
	copy_memory(src_dicom_image->institutional_department_name, 
	    &(des_dicom_image->institutional_department_name),
	    src_dicom_image->institutional_department_name_length, 1);    
	copy_memory(src_dicom_image->kvp, 
	    &(des_dicom_image->kvp),
	    src_dicom_image->kvp_length, 1);    
	copy_memory(src_dicom_image->manufacturer, 
	    &(des_dicom_image->manufacturer),
	    src_dicom_image->manufacturer_length, 1);    
	copy_memory(src_dicom_image->manufacturer_model_name, 
	    &(des_dicom_image->manufacturer_model_name),
	    src_dicom_image->manufacturer_model_name_length, 1);    
	copy_memory(src_dicom_image->modality, 
	    &(des_dicom_image->modality),
	    src_dicom_image->modality_length, 1);    
	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, 1);    
	copy_memory(src_dicom_image->operator_name, 
	    &(des_dicom_image->operator_name),
	    src_dicom_image->operator_name_length, 1);   
	copy_memory(src_dicom_image->patient_age, 
	    &(des_dicom_image->patient_age),
	    src_dicom_image->patient_age_length, 1);
	copy_memory(src_dicom_image->patient_birth_date, 
	    &(des_dicom_image->patient_birth_date),
	    src_dicom_image->patient_birth_date_length, 1);   
	copy_memory(src_dicom_image->patient_id, 
	    &(des_dicom_image->patient_id),
	    src_dicom_image->patient_id_length, 1);
	copy_memory(src_dicom_image->patient_name, 
	    &(des_dicom_image->patient_name),
	    src_dicom_image->patient_name_length, 1);   
	copy_memory(src_dicom_image->patient_sex, 
	    &(des_dicom_image->patient_sex),
	    src_dicom_image->patient_sex_length, 1);    
	copy_memory(src_dicom_image->performing_physician_name, 
	    &(des_dicom_image->performing_physician_name),
	    src_dicom_image->performing_physician_name_length, 1);   
	copy_memory(src_dicom_image->referring_physician_name, 
	    &(des_dicom_image->referring_physician_name),
	    src_dicom_image->referring_physician_name_length, 1);     
	copy_memory(src_dicom_image->series_date, 
	    &(des_dicom_image->series_date),
	    src_dicom_image->series_date_length, 1);   
	copy_memory(src_dicom_image->series_instance_uid, 
	    &(des_dicom_image->series_instance_uid),
	    src_dicom_image->series_instance_uid_length, 1);   
	copy_memory(src_dicom_image->slice_thickness, 
	    &(des_dicom_image->slice_thickness),
	    src_dicom_image->slice_thickness_length, 1);
	copy_memory(src_dicom_image->software_version, 
	    &(des_dicom_image->software_version),
	    src_dicom_image->software_version_length, 1); 
	copy_memory(src_dicom_image->sop_class_uid, 
	    &(des_dicom_image->sop_class_uid),
	    src_dicom_image->sop_class_uid_length, 1);   
	copy_memory(src_dicom_image->sop_instance_uid, 
	    &(des_dicom_image->sop_instance_uid),
	    src_dicom_image->sop_instance_uid_length, 1);   
	copy_memory(src_dicom_image->source_type, 
	    &(des_dicom_image->source_type),
	    src_dicom_image->source_type_length, 1);   
	copy_memory(src_dicom_image->spatial_resolution, 
	    &(des_dicom_image->spatial_resolution),
	    src_dicom_image->spatial_resolution_length, 1);   
	copy_memory(src_dicom_image->station_name, 
	    &(des_dicom_image->station_name),
	    src_dicom_image->station_name_length, 1);   
	copy_memory(src_dicom_image->study_date, 
	    &(des_dicom_image->study_date),
	    src_dicom_image->study_date_length, 1);  
	copy_memory(src_dicom_image->study_description, 
	    &(des_dicom_image->study_description),
	    src_dicom_image->study_description_length, 1);   
	copy_memory(src_dicom_image->thumbnail_format, 
	    &(des_dicom_image->thumbnail_format),
	    src_dicom_image->thumbnail_format_length, 1);          	
	copy_memory(src_dicom_image->thumbnail_content, 
	    &(des_dicom_image->thumbnail_content),
	    src_dicom_image->thumbnail_content_length, 0);
}

void 
copy_memory(src_data, des_data, length, is_str)
        void *src_data;
        void **des_data;
        int length, is_str;
{
	int sz;
        /* Copy the content of src_data to des_data. */
	if (length > 0) {
		sz = length;
		if (is_str)
			sz += 1;
		*des_data = malloc(sz * sizeof(char));
		memset(*des_data, 0, sz);  
		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)
	DICOM_DBS *dbs;
{
	const char *sql;
	int rc;

	sql = "delete from dicom;";
	EXEC_SQL(dbs->db, sql);

	/*
	sql = "drop table dicom;";
	EXEC_SQL(dbs->db, sql);

	*/

	return 0;	
}

/*
 * 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, id)
	DICOM_DBS *dbs;
	int id;
{
	const char *sql = "delete from dicom where id = ?;";
	sqlite3_stmt *stmt;
	int rc;

	rc = sqlite3_prepare_v2(dbs->db, (sql), -1, &stmt, NULL);
	rc = sqlite3_bind_int(stmt, 1, id);
	rc = sqlite3_step(stmt);
	rc = sqlite3_finalize(stmt); 

	return 0;
}

/*
 * 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. */
	if (dicom_image->accession_number != &dicom_image->empty[0]) 
	        free_pointer(dicom_image->accession_number);
	if (dicom_image->acquisition_date != &dicom_image->empty[0]) 	
        	free_pointer(dicom_image->acquisition_date);    
	if (dicom_image->acquisition_time != &dicom_image->empty[0]) 
		free_pointer(dicom_image->acquisition_time);    
	if (dicom_image->body_part_examined != &dicom_image->empty[0]) 
		free_pointer(dicom_image->body_part_examined);   
	if (dicom_image->data_collection_diameter != &dicom_image->empty[0]) 
		free_pointer(dicom_image->data_collection_diameter);   
	if (dicom_image->device_serial_number != &dicom_image->empty[0]) 
		free_pointer(dicom_image->device_serial_number);   
	if (dicom_image->distance_source_to_detector != &dicom_image->empty[0]) 
		free_pointer(dicom_image->distance_source_to_detector);   
	if (dicom_image->distance_source_to_patient != &dicom_image->empty[0]) 
		free_pointer(dicom_image->distance_source_to_patient); 
	if (dicom_image->image_comments != &dicom_image->empty[0]) 	
		free_pointer(dicom_image->image_comments);    
	if (dicom_image->institution_address != &dicom_image->empty[0]) 
		free_pointer(dicom_image->institution_address);    
	if (dicom_image->institution_name != &dicom_image->empty[0]) 
		free_pointer(dicom_image->institution_name);    
	if (dicom_image->institutional_department_name != &dicom_image->empty[0]) 	
		free_pointer(dicom_image->institutional_department_name);    
	if (dicom_image->kvp != &dicom_image->empty[0]) 
		free_pointer(dicom_image->kvp);    
	if (dicom_image->manufacturer != &dicom_image->empty[0]) 
		free_pointer(dicom_image->manufacturer);    
	if (dicom_image->manufacturer_model_name != &dicom_image->empty[0]) 
		free_pointer(dicom_image->manufacturer_model_name);    
	if (dicom_image->modality != &dicom_image->empty[0]) 
		free_pointer(dicom_image->modality);    
	if (dicom_image->name_of_physician_reading_study != &dicom_image->empty[0]) 	
		free_pointer(dicom_image->name_of_physician_reading_study);    
	if (dicom_image->operator_name != &dicom_image->empty[0]) 
		free_pointer(dicom_image->operator_name);   
	if (dicom_image->patient_age != &dicom_image->empty[0]) 
		free_pointer(dicom_image->patient_age);
	if (dicom_image->patient_birth_date != &dicom_image->empty[0]) 	
		free_pointer(dicom_image->patient_birth_date);   
	if (dicom_image->patient_id != &dicom_image->empty[0]) 
		free_pointer(dicom_image->patient_id);
	if (dicom_image->patient_name != &dicom_image->empty[0]) 
		free_pointer(dicom_image->patient_name);   
	if (dicom_image->patient_sex != &dicom_image->empty[0]) 	
		free_pointer(dicom_image->patient_sex);    
	if (dicom_image->performing_physician_name != &dicom_image->empty[0]) 	
		free_pointer(dicom_image->performing_physician_name);   
	if (dicom_image->referring_physician_name != &dicom_image->empty[0]) 
		free_pointer(dicom_image->referring_physician_name);     
	if (dicom_image->series_date != &dicom_image->empty[0]) 
		free_pointer(dicom_image->series_date);   
	if (dicom_image->series_instance_uid != &dicom_image->empty[0]) 	
		free_pointer(dicom_image->series_instance_uid);   
	if (dicom_image->slice_thickness != &dicom_image->empty[0]) 	
		free_pointer(dicom_image->slice_thickness);
	if (dicom_image->software_version != &dicom_image->empty[0]) 	
		free_pointer(dicom_image->software_version); 
	if (dicom_image->sop_class_uid != &dicom_image->empty[0]) 	
		free_pointer(dicom_image->sop_class_uid);   
	if (dicom_image->sop_instance_uid != &dicom_image->empty[0]) 
		free_pointer(dicom_image->sop_instance_uid);   
	if (dicom_image->source_type != &dicom_image->empty[0]) 
		free_pointer(dicom_image->source_type);   
	if (dicom_image->spatial_resolution != &dicom_image->empty[0]) 	
		free_pointer(dicom_image->spatial_resolution);   
	if (dicom_image->station_name != &dicom_image->empty[0]) 	
		free_pointer(dicom_image->station_name);   
	if (dicom_image->study_date != &dicom_image->empty[0]) 	
		free_pointer(dicom_image->study_date);  
	if (dicom_image->study_description != &dicom_image->empty[0]) 	
		free_pointer(dicom_image->study_description);   
	if (dicom_image->thumbnail_format != &dicom_image->empty[0]) 	
		free_pointer(dicom_image->thumbnail_format);          	
	if (dicom_image->thumbnail_content != &dicom_image->empty[0]) 
		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_row(stmt, dicom_image)
	sqlite3_stmt *stmt;
	DICOM_IMAGE *dicom_image;
{
	char **att_value;
	int *att_length;
	int i, size;
	const char *strval;

	dicom_image->id = sqlite3_column_int(stmt, 0);
	dicom_image->empty[0] = '\0';
        
	/* Iteratively get all DICOM attributes and thumbnail from stmt. */ 
	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. */
		if (i != (ATTRIBUTENUM + 1)) {
			strval = sqlite3_column_text(stmt, i+1);
			if (strval == NULL) {
				*att_length = 0;
				*att_value = &dicom_image->empty[0];
			} else {
				size = sqlite3_column_bytes(stmt, i+1);
				*att_length = size;
				*att_value = malloc((size + 1) * sizeof(char));
				memset(*att_value, 0, size + 1);
				memcpy(*att_value, strval, size);
			}
		} else {
			strval = (const char *)sqlite3_column_blob(stmt, i+1);
			if (strval == NULL) {
				*att_length = 0;
				*att_value = NULL;
			} else {
				size = sqlite3_column_bytes(stmt, i+1);
				*att_length = size;
				*att_value = malloc(size * sizeof(char));
				memcpy(*att_value, strval, size);
			}
		}

        }                      
}

/*
 * Initialize all pointers and strings in struct DICOM_DBS. 
 */ 
void
initialize_dbs(dm_dbs)
	DICOM_DBS *dm_dbs;
{
	dm_dbs->db = NULL;
	dm_dbs->dbname = DB_FILE_NAME;


}

/*
 * Open primary database and all secondary indexes, acquisition date,  
 * modality and patient name secondary index.     
 */ 
int
open_dbs(dm_dbs)
	DICOM_DBS *dm_dbs;
{
 	int rc; 
	
	rc = sqlite3_open(dm_dbs->dbname, &dm_dbs->db);

	EXEC_SQL(dm_dbs->db, blob_set_sql);
	EXEC_SQL(dm_dbs->db, create_table_sql);
	EXEC_SQL(dm_dbs->db, create_index_sql1);
	EXEC_SQL(dm_dbs->db, create_index_sql2);
	EXEC_SQL(dm_dbs->db, create_index_sql3);

        return 0;    
}


/*
 * 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, field, length)
	int att_index;
	DICOM_IMAGE *image;
	char **field;
	int *length;
{
	char *p, *fname;
	int llength;

	llength = 0;
	fname = NULL;
	p = NULL;

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

int update_field(dm_dbs, dicom, field_name, valp, valen)
	DICOM_DBS *dm_dbs;
	DICOM_IMAGE *dicom;
	char *field_name, *valp;
	int valen;
{
	char buf[200];
	int rc;
	sqlite3_stmt *stmt;

	sprintf(buf, "update dicom set %s=? where id=%d", field_name, dicom->id);
	rc = sqlite3_prepare_v2(dm_dbs->db, (buf), -1, &stmt, NULL);
	rc = sqlite3_bind_text(stmt, 1, valp, valen, NULL);
	rc = sqlite3_step(stmt);
	rc = sqlite3_finalize(stmt); 

	return  (0);
}

/*
 * Retrieve all DICOM from primary database.
 */ 
int
retrieve_all_dicom(dbs, dicom_list, number)
	DICOM_DBS *dbs;
	DICOM_IMAGE *dicom_list;
	int *number;
{
        DICOM_IMAGE *lp;
        int rc;
	const char *sql;
	sqlite3_stmt *stmt;

	lp = dicom_list;                       /* Pointer to DICOM list. */
	*number = 0;   

	sql = "select * from dicom;";

	rc = sqlite3_prepare_v2(dbs->db, (sql), -1, &stmt, NULL);

        /* Get all DICOM and put them into a DICOM image list. */
        while (sqlite3_step(stmt) == SQLITE_ROW) {
    		get_dicom_image_from_row(stmt, lp);
		/* Move to the next DICOM. */
                lp++;
                /* Increase the DICOM number by 1. */
	       (*number)++;
        }
  	rc = sqlite3_finalize(stmt); 
        
	return (0);
}

int retrieve_dicom_with_oneattr(dbs, attindex, attvalue, dicom_list, number)
	DICOM_DBS *dbs;
	int attindex;
	char *attvalue;
	DICOM_IMAGE *dicom_list;
	int *number;
{
	int rc;
	char buf[200];
	sqlite3_stmt *stmt;
        DICOM_IMAGE *lp;

	lp = dicom_list;                       /* Pointer to DICOM list. */
	*number = 0;   

	sprintf(buf, "select * from dicom where %s = ?;", fields_name[attindex]);
	rc = sqlite3_prepare_v2(dbs->db, (buf), -1, &stmt, NULL);
	rc = sqlite3_bind_text(stmt, 1, attvalue, strlen(attvalue), NULL);
        /* Get all DICOM and put them into a DICOM image list. */
        while (sqlite3_step(stmt) == SQLITE_ROW) {
    		get_dicom_image_from_row(stmt, lp);
		/* Move to the next DICOM. */
                lp++;
                /* Increase the DICOM number by 1. */
	       (*number)++;
        }
  	rc = sqlite3_finalize(stmt); 
	
	return (0);
}

int retrieve_dicom_with_attrs(dbs, attrs, dicom_list, number)
	DICOM_DBS *dbs;
	char **attrs;
	DICOM_IMAGE *dicom_list;
	int *number;
{
	int rc, loc, i;
	char *buf;
	sqlite3_stmt *stmt;
       DICOM_IMAGE *lp;

	lp = dicom_list;                       /* Pointer to DICOM list. */
	*number = 0;   
	buf = malloc(2000);

	sprintf(buf, "select * from dicom where ");
	loc = strlen(buf);
	for ( i = 0; i < ATTRIBUTENUM; i++) {
		if (attrs[i] != NULL) {
			sprintf(buf+loc, "%s = ?", fields_name[i]);
			loc = strlen(buf);
		}
	}
	rc = sqlite3_prepare_v2(dbs->db, (buf), -1, &stmt, NULL);
	loc = 1;
	for ( i = 0; i < ATTRIBUTENUM; i++) {
		if (attrs[i] != NULL) {
			rc = sqlite3_bind_text(stmt, loc, attrs[i], strlen(attrs[i]), NULL);
			loc++;
		}
	}
        /* Get all DICOM and put them into a DICOM image list. */
        while (sqlite3_step(stmt) == SQLITE_ROW) {
    		get_dicom_image_from_row(stmt, lp);
		/* Move to the next DICOM. */
                lp++;
                /* Increase the DICOM number by 1. */
	       (*number)++;
        }
  	rc = sqlite3_finalize(stmt); 

	if (buf != NULL)
		free(buf);

	return (0);
}

/*
 * Retrieve all keys in a list with cursor and return the number. 
 */ 
int 
retrieve_keys(dbs, keys, number)
	DICOM_DBS *dbs;
	int *keys;
	int *number;
{
	int rc, key;
	const char *sql;
	sqlite3_stmt *stmt;

	/* Initialize pointers and structures */
	*number = 0;
	sql = "select id from dicom;";
	rc = sqlite3_prepare_v2(dbs->db, (sql), -1, &stmt, 0);
	
        while ((sqlite3_step(stmt) == SQLITE_ROW)) {    
		key = sqlite3_column_int(stmt, 0);
                keys[(*number)]= key;
	       (*number)++;
        }
	rc = sqlite3_finalize(stmt); 

	return (0);
}

int retrieve_attrs(DICOM_DBS *dbs, int at_index, char **key, int *number) {
	int rc;
	char buf[200];
	char *field, *p, *newp;
	sqlite3_stmt *stmt;
	int sz;

	field = fields_name[at_index];
	sprintf(buf, "select distinct %s from dicom", field);
	rc = sqlite3_prepare_v2(dbs->db, (buf), -1, &stmt, NULL);
	while ((sqlite3_step(stmt) == SQLITE_ROW)) {
		sz = 0;
		p = sqlite3_column_text(stmt, 0);
		if (p != NULL)
			sz = sqlite3_column_bytes(stmt, 0);
		newp = (char *)malloc(sizeof(char) * 50);
		memset(newp, 0, 50);
		if (sz > 0)
			memcpy(newp, p, sz);
		key[(*number)]= newp;
		(*number)++;
	}
	rc = sqlite3_finalize(stmt); 

	return (0);
}

/*
 * 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(dbs, id, dicom_image)
	DICOM_DBS *dbs;
	int id;
	DICOM_IMAGE *dicom_image;
{
        sqlite3_stmt *stmt;
	int rc, number;
	const char *sql;

        memset(dicom_image, 0, sizeof(DICOM_IMAGE));
        sql = "select * from dicom where id = ?;";
	rc = sqlite3_prepare_v2(dbs->db, (sql), -1, &stmt, 0);
	rc = sqlite3_bind_int(stmt, 1, id);
	rc = sqlite3_step(stmt);
	get_dicom_image_from_row(stmt, dicom_image);
	if (rc == SQLITE_ROW)
		number = 1;
	else
		number = 0;
 	rc = sqlite3_finalize(stmt); 

	return number;
}

/*
 * Get DICOM_IMAGE struct from DBT.
 */ 
int 
get_dicom_image_from_bytes(data, dicom_image)
	const char *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. */
        
	dicom_image->empty[0] = '\0';
	/* 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 *)data + offset);
		offset += sizeof(int);    
		
		/* No need to malloc for 0 chars */
		if (*att_length == 0) {
			if (i == (ATTRIBUTENUM + 1))
				*att_value = NULL;
			else
				*att_value = &dicom_image->empty[0];
			continue;
		}
                /* 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 + 1) * sizeof(char));  
                memset(*att_value, 0, (*att_length + 1) * sizeof(char)); 
                memcpy(*att_value, (char *)data + offset, 
                    *att_length);                      
                
		/*
		 * if (i != (ATTRIBUTENUM + 1))                            
                 *      *(*att_value + *att_length) = '\0';  
		 */
                
                /* Move to the next attribute. */
                offset += *att_length;    
        }
	return (offset);	
}

/*
 * Write stream, that is serialized DICOM, into bdb. 
 */ 
int 
write_stream_into_bdb(stream, length, dbs)
        char* stream;
	int length;
        DICOM_DBS *dbs;
{
        int coffset = 0;                      /* The current read bytes. */
        int count= 0;                /* The number of DICOM from stream. */
	int i = 0;                            /* The current attributes. */
        int size = 0;        /* The size of current attribute and image. */
	int rc, ret;
	DICOM_IMAGE dicom_image;
	sqlite3_stmt *stmt;
	const char *sql;

        if (length < 0)
                return -1;
        else if (length == 0)
                return 0;
        
	ret = 0;
	sql = "insert into dicom values(?,?,?,?,?,?,?,?,?,?, \
		?,?,?,?,?,?,?,?,?,?, \
		?,?,?,?,?,?,?,?,?,?, \
		?,?,?,?,?,?,?,?,?);";
	rc = sqlite3_prepare_v2(dbs->db, (sql), -1, &stmt, NULL);

        /* Get DICOM from the stream. Store the DICOM into bdb with id as  
         * key, its attributes and thumbnail as data.
         */                
        while(coffset < length) {   
		/* Get DICOM id and put it into bdb_key DBT. */	
		size = convert_bytes_to_int(stream + coffset);
		coffset += sizeof(int);
		dicom_image.id = convert_bytes_to_int(stream + coffset);

		coffset += size;
		coffset += get_dicom_image_from_bytes(stream + coffset, &dicom_image);
		rc = sqlite3_bind_int(stmt, 1, dicom_image.id);

#define BIND_FIELD(field, field_length, loc) do { \
	if (dicom_image.field_length == 0) \
		rc = sqlite3_bind_null(stmt, loc); \
	else \
		rc = sqlite3_bind_text(stmt, loc, dicom_image.field, \
		    dicom_image.field_length, NULL); \
} while(0);

		BIND_FIELD(accession_number, accession_number_length, 2);
		BIND_FIELD(acquisition_date, acquisition_date_length, 3);
		BIND_FIELD(acquisition_time, acquisition_time_length, 4);
		BIND_FIELD(body_part_examined, body_part_examined_length, 5);
		BIND_FIELD(data_collection_diameter, data_collection_diameter_length, 6);
		BIND_FIELD(device_serial_number, device_serial_number_length, 7);
		BIND_FIELD(distance_source_to_detector, distance_source_to_detector_length, 8);
		BIND_FIELD(distance_source_to_patient, distance_source_to_patient_length, 9);
		BIND_FIELD(image_comments, image_comments_length, 10);
		BIND_FIELD(institution_address, institution_address_length, 11);
		BIND_FIELD(institution_name, institution_name_length, 12);
		BIND_FIELD(institutional_department_name, institutional_department_name_length, 13);
		BIND_FIELD(kvp, kvp_length, 14);
		BIND_FIELD(manufacturer, manufacturer_length, 15);
		BIND_FIELD(manufacturer_model_name, manufacturer_model_name_length, 16);
		BIND_FIELD(modality, modality_length, 17);
		BIND_FIELD(name_of_physician_reading_study, name_of_physician_reading_study_length, 18);
		BIND_FIELD(operator_name, operator_name_length, 19);
		BIND_FIELD(patient_age, patient_age_length, 20);
		BIND_FIELD(patient_birth_date, patient_birth_date_length, 21);
		BIND_FIELD(patient_id, patient_id_length, 22);
		BIND_FIELD(patient_name, patient_name_length, 23);
		BIND_FIELD(patient_sex, patient_sex_length, 24);
		BIND_FIELD(performing_physician_name, performing_physician_name_length, 25);
		BIND_FIELD(referring_physician_name, referring_physician_name_length, 26);
		BIND_FIELD(series_date, series_date_length, 27);
		BIND_FIELD(series_instance_uid, series_instance_uid_length, 28);
		BIND_FIELD(slice_thickness, slice_thickness_length, 29);
		BIND_FIELD(software_version, software_version_length, 30);
		BIND_FIELD(sop_class_uid, sop_class_uid_length, 31);
		BIND_FIELD(sop_instance_uid, sop_instance_uid_length, 32);
		BIND_FIELD(source_type, source_type_length, 33);
		BIND_FIELD(spatial_resolution, spatial_resolution_length, 34);
		BIND_FIELD(station_name, station_name_length, 35);
		BIND_FIELD(study_date, study_date_length, 36);
		BIND_FIELD(study_description, study_description_length, 37);
		BIND_FIELD(thumbnail_format, thumbnail_format_length, 38);
		if (dicom_image.thumbnail_content_length == 0)
			rc = sqlite3_bind_null(stmt, 39);
		else
			rc = sqlite3_bind_blob(stmt, 39, dicom_image.thumbnail_content,
			    dicom_image.thumbnail_content_length, NULL);
		rc = sqlite3_step(stmt);

		count++;
		free_dicom_image(&dicom_image);
  				
        }
        rc = sqlite3_finalize(stmt);

        /* 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;
}
