/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * filename: m-ecat72.c                                                    *
 *                                                                         *
 * UTIL C-source: Medical Image Conversion Utility                         *
 *                                                                         *
 * purpose      : Read ECAT 7.2 files                                      *
 *                                                                         *
 * project      : (X)MedCon by Erik Nolf                                   *
 *                                                                         *
 * Functions    : MdcCheckECAT7()          - Check for ECAT7 format        *
 *                MdcEcatPrintMainHdr()    - Print content main header     *
 *                MdcEcatPrintImgSubHdr()  - Print content image subheader *
 *                MdcEcatPrintAttnSubHdr() - Print content attenuation hdr *
 *                MdcEcatPrintScanSubHdr() - Print content scan header     *
 *                MdcEcatPrintNormSubHdr() - Print content norm header     *
 *                MdcReadECAT7()           - Read ECAT7 file               *
 *                MdcWriteECAT7()          - Write ECAT7 file              *
 *                                                                         *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
 */

/*
   Copyright (C) 1997-2026 by Erik Nolf

   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation; either version 2, or (at your option) any later
   version.

   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
   Public License for more details.

   You should have received a copy of the GNU General Public License along
   with this program; if not, write to the Free Software Foundation, Inc.,
   59 Place - Suite 330, Boston, MA 02111-1307, USA.  */

/****************************************************************************
                              H E A D E R S
****************************************************************************/

#include "m-depend.h"

#include <stdio.h>
#define __USE_POSIX 1
#include <time.h>
#ifdef LIBMDC_HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef LIBMDC_HAVE_STRING_H
#include <string.h>
#endif
#ifdef LIBMDC_HAVE_STRINGS_H
#ifndef _WIN32
#include <strings.h>
#endif
#endif

#include "medcon.h"

#if MDC_INCLUDE_TPC
#define MDC_TPC_SITE 0       /* 0/1 disable/enable TPC site items */
#define __USE_ISOC99 1
#include <math.h>
#include "ecat7.h"
#endif


/****************************************************************************
                              D E F I N E S
****************************************************************************/

#define MDC_NUM_PLANES_FIX    MDC_YES /* fix for wrong num_planes = 1 bug */

#define MDC_MAX_ECAT7FILETYPES      15
#define MDC_MAX_ECAT7FILETYPES_SIZE 15

static char MdcEcat7FileTypes
        [MDC_MAX_ECAT7FILETYPES][MDC_MAX_ECAT7FILETYPES_SIZE]=
                       {"Unknown","Sinogram","Image16","AttnCorr","Norm",
                        "PolarMap","Volume8","Volume16","Projection8",
                        "Projection16","Image8","3DSino16","3DSino8",
                        "3DNorm","3DSinoFlt"};

/****************************************************************************
                            F U N C T I O N S
****************************************************************************/
int MdcCheckECAT7(FILEINFO *fi)
{
  Mdc_Main_header7 mh;

  if (mdc_mat_read_main_header7(fi->ifp,&mh)) return MDC_BAD_READ;

  if (memcmp(mh.magic_number,MDC_ECAT7_SIG,7) ) return(MDC_FRMT_NONE);

  return MDC_FRMT_ECAT7;

}

void MdcEcatPrintMainHdr(Mdc_Main_header7 *mh)
{
  int i;
  time_t my_time;

  MdcPrintLine('-',MDC_HALF_LENGTH);
  MdcPrntScrn("ECAT7 Main Header (%d bytes)\n",MH_72_SIZE);
  MdcPrintLine('-',MDC_HALF_LENGTH);

  MdcGetSafeString(mdcbufr,(char *)mh->magic_number,14,14);
  MdcPrntScrn("magic_number             : %s\n",mdcbufr);
  MdcGetSafeString(mdcbufr,mh->original_file_name,32,32);
  MdcPrntScrn("original_file_name       : %s\n",mdcbufr);
  MdcPrntScrn("sw_version               : %hd\n",mh->sw_version);
  MdcPrntScrn("system_type              : %hd\n",mh->system_type);
  MdcPrntScrn("file_type                : %hd ",mh->file_type);
  if ((mh->file_type > -1) && (mh->file_type < 15))
    MdcPrntScrn("(= %s)\n",MdcEcat7FileTypes[mh->file_type]);
  else
    MdcPrntScrn("(= Unknown)\n");
  MdcGetSafeString(mdcbufr,mh->serial_number,10,10);
  MdcPrntScrn("serial_number            : %s\n",mdcbufr);
  my_time = (time_t)mh->scan_start_time;
  MdcPrntScrn("scan_start_time          : %s\n",ctime(&my_time));
  MdcGetSafeString(mdcbufr,mh->isotope_name,8,8);
  MdcPrntScrn("isotope_name             : %s\n",mdcbufr);
  MdcPrntScrn("isotope_halflife         : %f [sec]\n",mh->isotope_halflife);
  MdcGetSafeString(mdcbufr,mh->radiopharmaceutical,32,32);
  MdcPrntScrn("radiopharmaceutical      : %s\n",mdcbufr);
  MdcPrntScrn("gantry_tilt              : %f [degrees]\n",mh->gantry_tilt);
  MdcPrntScrn("gantry_rotation          : %f [degrees]\n",mh->gantry_rotation);
  MdcPrntScrn("bed_elevation            : %f [cm]\n",mh->bed_elevation);
  MdcPrntScrn("intrinsic_tilt           : %f [degrees]\n",mh->intrinsic_tilt);
  MdcPrntScrn("wobble_speed             : %hd [rpm]\n",mh->wobble_speed);
  MdcPrntScrn("tansm_source_type        : %hd\n",mh->transm_source_type);
  MdcPrntScrn("distance_scanned         : %f [cm]\n",mh->distance_scanned);
  MdcPrntScrn("transaxial_fov           : %f [cm]\n",mh->transaxial_fov);
  MdcPrntScrn("angular_compression      : %hd\n",mh->angular_compression);
  MdcPrntScrn("coin_samp_mode           : %hd\n",mh->coin_samp_mode);
  MdcPrntScrn("axial_samp_mode          : %hd\n",mh->axial_samp_mode);
  MdcPrntScrn("ecat_calibration_factor  : %e\n",mh->ecat_calibration_factor);
  MdcPrntScrn("calibration_units        : %hd\n",mh->calibration_units);
  MdcPrntScrn("calibration_units_label  : %hd\n",mh->calibration_units_label);
  MdcPrntScrn("compression_code         : %hd\n",mh->compression_code);
  MdcGetSafeString(mdcbufr,mh->study_type,14,14);
  MdcPrntScrn("study_type               : %s\n",mdcbufr);
  MdcGetSafeString(mdcbufr,mh->patient_id,16,16);
  MdcPrntScrn("patient_id               : %s\n",mdcbufr);
  MdcGetSafeString(mdcbufr,mh->patient_name,32,32);
  MdcPrntScrn("patient_name             : %s\n",mdcbufr);
  MdcPrntScrn("patient_sex              : ");
    switch (mh->patient_sex[0]) {
      case 0:  MdcPrntScrn("M\n"); break;
      case 1:  MdcPrntScrn("F\n"); break;
      default: MdcPrntScrn("U\n");
    }
  MdcPrntScrn("patient_dexterity        : %c\n",mh->patient_dexterity[0]);
  MdcPrntScrn("patient_age              : %f\n",mh->patient_age);
  MdcPrntScrn("patient_height           : %f\n",mh->patient_height);
  MdcPrntScrn("patient_weight           : %f\n",mh->patient_weight);
  my_time = (time_t)mh->patient_birth_date;
  MdcPrntScrn("patient_birth_date       : %s\n",ctime(&my_time));
  MdcGetSafeString(mdcbufr,mh->physician_name,32,32);
  MdcPrntScrn("physician_name           : %s\n",mdcbufr);
  MdcGetSafeString(mdcbufr,mh->operator_name,32,32);
  MdcPrntScrn("operator_name            : %s\n",mdcbufr);
  MdcGetSafeString(mdcbufr,mh->study_description,32,32);
  MdcPrntScrn("study_description        : %s\n",mdcbufr);
  MdcPrntScrn("acquisition_type         : %hd\n",mh->acquisition_type);
  MdcPrntScrn("patient_orientation      : %hd\n",mh->patient_orientation);
  MdcGetSafeString(mdcbufr,mh->facility_name,20,20);
  MdcPrntScrn("facility_name            : %s\n",mdcbufr);
  MdcPrntScrn("num_planes               : %hd\n",mh->num_planes);
  MdcPrntScrn("num_frames               : %hd\n",mh->num_frames);
  MdcPrntScrn("num_gates                : %hd\n",mh->num_gates);
  MdcPrntScrn("num_bed_pos              : %hd\n",mh->num_bed_pos);
  MdcPrntScrn("init_bed_position        : %f\n",mh->init_bed_position);
    for (i=0; i<15; i++)
  MdcPrntScrn("bed_position[%2d]         : %f\n",i,mh->bed_position[i]);
  MdcPrntScrn("plane_separation         : %f [cm]\n",mh->plane_separation);
  MdcPrntScrn("lwr_sctr_thres           : %hd [Kev]\n",mh->lwr_sctr_thres);
  MdcPrntScrn("lwr_true_thres           : %hd [Kev]\n",mh->lwr_true_thres);
  MdcPrntScrn("upr_true_thres           : %hd [Kev]\n",mh->upr_true_thres);
  MdcGetSafeString(mdcbufr,mh->user_process_code,10,10);
  MdcPrntScrn("user_process_code        : %s\n",mdcbufr);
  MdcPrntScrn("acquisition_mode         : %hd\n",mh->acquisition_mode);
  MdcPrntScrn("bin_size                 : %f [cm]\n",mh->bin_size);
  MdcPrntScrn("branching_fraction       : %f\n",mh->branching_fraction);
  my_time = (time_t)mh->dose_start_time;
  MdcPrntScrn("dose_start_time          : %s\n",ctime(&my_time));
  MdcPrntScrn("dosage                   : %e [mCi]\n",mh->dosage);
  MdcPrntScrn("well_counter_corr_factor : %f\n",mh->well_counter_corr_factor);
  MdcGetSafeString(mdcbufr,mh->data_units,32,32);
  MdcPrntScrn("data_units               : %s\n",mdcbufr);
  MdcPrntScrn("septa_state              : %hd\n",mh->septa_state);
    for (i=0; i<6; i++)
  MdcPrntScrn("fill_cti[%d]              : %hd\n",i,mh->fill_cti[i]);

}

void MdcEcatPrintImgSubHdr(Mdc_Image_subheader7 *ish, int nr)
{
  MdcPrintLine('-',MDC_HALF_LENGTH);
  MdcPrntScrn("ECAT7 Image Sub Header %05d (%d bytes)\n",nr,ISH_72_SIZE);
  MdcPrintLine('-',MDC_HALF_LENGTH);

  MdcPrntScrn("data_type                : %hd\n",ish->data_type);
  MdcPrntScrn("num_dimensions           : %hd\n",ish->num_dimensions);
  MdcPrntScrn("x_dimension              : %hd\n",ish->x_dimension);
  MdcPrntScrn("y_dimension              : %hd\n",ish->y_dimension);
  MdcPrntScrn("z_dimension              : %hd\n",ish->z_dimension);
  MdcPrntScrn("x_offset                 : %f [cm]\n",ish->x_offset);
  MdcPrntScrn("y_offset                 : %f [cm]\n",ish->y_offset);
  MdcPrntScrn("z_offset                 : %f [cm]\n",ish->z_offset);
  MdcPrntScrn("recon_zoom               : %f\n",ish->recon_zoom);
  MdcPrntScrn("scale_factor             : %e\n",ish->scale_factor);
  MdcPrntScrn("image_min                : %hd\n",ish->image_min);
  MdcPrntScrn("image_max                : %hd\n",ish->image_max);
  MdcPrntScrn("x_pixel_size             : %f [cm]\n",ish->x_pixel_size);
  MdcPrntScrn("y_pixel_size             : %f [cm]\n",ish->y_pixel_size);
  MdcPrntScrn("z_pixel_size             : %f [cm]\n",ish->z_pixel_size);
  MdcPrntScrn("frame_duration           : %d [ms]\n",ish->frame_duration);
  MdcPrntScrn("frame_start_time         : %d [ms]\n",ish->frame_start_time);
  MdcPrntScrn("filter_code              : %hd\n",ish->filter_code);
  MdcPrntScrn("x_resolution             : %g\n",ish->x_resolution);
  MdcPrntScrn("y_resolution             : %g\n",ish->y_resolution);
  MdcPrntScrn("z_resolution             : %g\n",ish->z_resolution);
  MdcPrntScrn("num_r_elements           : %g\n",ish->num_r_elements);
  MdcPrntScrn("num_angles               : %g\n",ish->num_angles);
  MdcPrntScrn("z_rotation_angle         ; %g\n",ish->z_rotation_angle);
  MdcPrntScrn("decay_corr_fctr          : %g\n",ish->decay_corr_fctr);
  MdcPrntScrn("processing_code          : %d\n",ish->processing_code);
  MdcPrntScrn("gate_duration            : %u\n",ish->gate_duration);
  MdcPrntScrn("r_wave_offset            : %d\n",ish->r_wave_offset);
  MdcPrntScrn("num_accepted_beats       : %d\n",ish->num_accepted_beats);
  MdcPrntScrn("filter_cutoff_frequency  : %g\n",ish->filter_cutoff_frequency);
  MdcPrntScrn("filter_resolution        : %g\n",ish->filter_resolution);
  MdcPrntScrn("filter_ramp_slope        : %g\n",ish->filter_ramp_slope);
  MdcPrntScrn("filter_order             : %hd\n",ish->filter_order);
  MdcPrntScrn("filter_scatter_fraction  : %g\n",ish->filter_scatter_fraction);
  MdcPrntScrn("filter_scatter_slope     : %g\n",ish->filter_scatter_slope);
  MdcGetSafeString(mdcbufr,ish->annotation,40,40);
  MdcPrntScrn("annotation               : %s\n",mdcbufr);
  MdcPrntScrn("mt_1_1                   : %g\n",ish->mt_1_1);
  MdcPrntScrn("mt_1_2                   : %g\n",ish->mt_1_2);
  MdcPrntScrn("mt_1_3                   : %g\n",ish->mt_1_3);
  MdcPrntScrn("mt_2_1                   : %g\n",ish->mt_2_1);
  MdcPrntScrn("mt_2_2                   : %g\n",ish->mt_2_2);
  MdcPrntScrn("mt_2_3                   : %g\n",ish->mt_2_3);
  MdcPrntScrn("mt_3_1                   : %g\n",ish->mt_3_1);
  MdcPrntScrn("mt_3_2                   : %g\n",ish->mt_3_2);
  MdcPrntScrn("mt_3_3                   : %g\n",ish->mt_3_3);
  MdcPrntScrn("rfilter_cutoff           : %g\n",ish->rfilter_cutoff);
  MdcPrntScrn("rfilter_resolution       : %g\n",ish->rfilter_resolution);
  MdcPrntScrn("rfilter_code             : %hd\n",ish->rfilter_code);
  MdcPrntScrn("rfilter_order            : %hd\n",ish->rfilter_order);
  MdcPrntScrn("zfilter_cutoff           : %g\n",ish->zfilter_cutoff);
  MdcPrntScrn("zfilter_resolution       : %g\n",ish->zfilter_resolution);
  MdcPrntScrn("zfilter_code             : %hd\n",ish->zfilter_code);
  MdcPrntScrn("zfilter_order            : %hd\n",ish->zfilter_order);
  MdcPrntScrn("mt_1_4                   : %g\n",ish->mt_1_4);
  MdcPrntScrn("mt_2_4                   : %g\n",ish->mt_2_4);
  MdcPrntScrn("mt_3_4                   : %g\n",ish->mt_3_4);
  MdcPrntScrn("scatter_type             : %hd\n",ish->scatter_type);
  MdcPrntScrn("recon_type               : %hd\n",ish->recon_type);
  MdcPrntScrn("recon_views              : %hd\n",ish->recon_views);
  MdcPrntScrn("fill_cti[87]             : <not printed>\n");
  MdcPrntScrn("fill_user[48]            : <not printed>\n");

}

void MdcEcatPrintAttnSubHdr(Mdc_Attn_subheader7 *ash, int nr)
{
  int i;

  MdcPrintLine('-',MDC_HALF_LENGTH);
  MdcPrntScrn("ECAT7 Attenuation Sub Header %05d (%d bytes)\n",nr,ASH_72_SIZE);
  MdcPrintLine('-',MDC_HALF_LENGTH);

  MdcPrntScrn("data_type                : %hd\n",ash->data_type);
  MdcPrntScrn("num_dimensions           : %hd\n",ash->num_dimensions);
  MdcPrntScrn("attenuation_type         : %hd\n",ash->attenuation_type);
  MdcPrntScrn("num_r_elements           : %hd\n",ash->num_r_elements);
  MdcPrntScrn("num_angles               : %hd\n",ash->num_angles);
  MdcPrntScrn("num_z_elements           : %hd\n",ash->num_z_elements);
  MdcPrntScrn("ring_difference          : %hd\n",ash->ring_difference);
  MdcPrntScrn("x_resolution             : %g [cm]\n",ash->x_resolution);
  MdcPrntScrn("y_resolution             : %g [cm]\n",ash->y_resolution);
  MdcPrntScrn("z_resolution             : %g [cm]\n",ash->z_resolution);
  MdcPrntScrn("w_resolution             : %g\n",ash->w_resolution);
  MdcPrntScrn("scale_factor             : %e\n",ash->scale_factor);
  MdcPrntScrn("x_offset                 : %g [cm]\n",ash->x_offset);
  MdcPrntScrn("y_offset                 : %g [cm]\n",ash->y_offset);
  MdcPrntScrn("x_radius                 : %g [cm]\n",ash->x_radius);
  MdcPrntScrn("y_radius                 : %g [cm]\n",ash->y_radius);
  MdcPrntScrn("tilt_angle               : %g [degrees]\n",ash->tilt_angle);
  MdcPrntScrn("attenuation_coeff        : %g [1/cm]\n",ash->attenuation_coeff);
  MdcPrntScrn("attenuation_min          : %g\n",ash->attenuation_min);
  MdcPrntScrn("attenuation_max          : %g\n",ash->attenuation_max);
  MdcPrntScrn("skull_thickness          : %g [cm]\n",ash->skull_thickness);
  MdcPrntScrn("num_xtra_atten_coeff     : %hd\n",ash->num_xtra_atten_coeff);
  for (i=0; i<8; i++)
  MdcPrntScrn("xtra_atten_coeff[%d]      : %g\n",i,ash->xtra_atten_coeff[i]);
  MdcPrntScrn("edge_finding_threshold   : %g\n",ash->edge_finding_threshold);
  MdcPrntScrn("storage_order            : %hd\n",ash->storage_order);
  MdcPrntScrn("span                     : %hd\n",ash->span);
  for (i=0; i<64; i++)
  MdcPrntScrn("z_elements[%2d]           : %hd\n",i,ash->z_elements[i]);
  MdcPrntScrn("fill_unused[86]          : <not printed>\n");
  MdcPrntScrn("fill_user[50]            : <not printed>\n");

}

void MdcEcatPrintScanSubHdr(Mdc_Scan_subheader7 *ssh)
{
  MdcPrntScrn("data_type                : %hd\n",ssh->data_type);
  MdcPrntScrn("num_dimensions           : %hd\n",ssh->num_dimensions);
  MdcPrntScrn("num_r_elements           : %hd\n",ssh->num_r_elements);
  MdcPrntScrn("num_angles               : %hd\n",ssh->num_angles);
  MdcPrntScrn("corrections_applied      : %hd\n",ssh->corrections_applied);
  MdcPrntScrn("num_z_elements           : %hd\n",ssh->num_z_elements);
  MdcPrntScrn("ring_difference          : %hd\n",ssh->ring_difference);
  MdcPrntScrn("x_resolution             : %g [cm]\n",ssh->x_resolution);
  MdcPrntScrn("y_resolution             : %g [cm]\n",ssh->y_resolution);
  MdcPrntScrn("z_resolution             : %g [cm]\n",ssh->z_resolution);
  MdcPrntScrn("w_resolution             : %g\n",ssh->w_resolution);
  MdcPrntScrn("fill[6]                  : <not printed>\n");
  MdcPrntScrn("gate_duration            : %u [ms]\n",ssh->gate_duration);
  MdcPrntScrn("r_wave_offset            : %d [ms]\n",ssh->r_wave_offset);
  MdcPrntScrn("num_accepted_beats       : %d\n",ssh->num_accepted_beats);
  MdcPrntScrn("scale_factor             : %e\n",ssh->scale_factor);
  MdcPrntScrn("scan_min                 : %hd\n",ssh->scan_min);
  MdcPrntScrn("scan_max                 : %hd\n",ssh->scan_max);
  MdcPrntScrn("prompts                  : %d\n",ssh->prompts);
  MdcPrntScrn("delayed                  : %d\n",ssh->delayed);
  MdcPrntScrn("multiples                : %d\n",ssh->multiples);
  MdcPrntScrn("net_trues                : %d\n",ssh->net_trues);
  MdcPrntScrn("cor_singles[16]          : <not printed>\n");
  MdcPrntScrn("uncor_singles[16]        : <not printed>\n");
  MdcPrntScrn("tot_avg_cor              : %g\n",ssh->tot_avg_cor);
  MdcPrntScrn("tot_avg_uncor            : %g\n",ssh->tot_avg_uncor);
  MdcPrntScrn("total_coin_rate          : %d\n",ssh->total_coin_rate);
  MdcPrntScrn("frame_start_time         : %u\n",ssh->frame_start_time);
  MdcPrntScrn("frame_duration           : %u\n",ssh->frame_duration);
  MdcPrntScrn("deadtime_correction_factor: %g\n"
                                        ,ssh->deadtime_correction_factor);
  MdcPrntScrn("phy_planes[8]            : <not printed>\n");
  MdcPrntScrn("cti_fill[90]             : <not printed>\n");
  MdcPrntScrn("user_fill[50]            : <not printed>\n");

}


void MdcEcatPrintNormSubHdr(Mdc_Norm_subheader7 *nsh)
{
  MdcPrntScrn("data_type                : %hd\n",nsh->data_type);
  MdcPrntScrn("num_dimensions           : %hd\n",nsh->num_dimensions);
  MdcPrntScrn("num_r_elements           : %hd\n",nsh->num_r_elements);
  MdcPrntScrn("num_angles               : %hd\n",nsh->num_angles);
  MdcPrntScrn("num_z_elements           : %hd\n",nsh->num_z_elements);
  MdcPrntScrn("ring_difference          : %hd\n",nsh->ring_difference);
  MdcPrntScrn("scale_factor             : %e\n",nsh->scale_factor);
  MdcPrntScrn("norm_min                 : %g\n",nsh->norm_min);
  MdcPrntScrn("norm_max                 : %g\n",nsh->norm_max);
  MdcPrntScrn("fov_source_width         : %g\n",nsh->fov_source_width);
  MdcPrntScrn("norm_quality_factor      : %g\n",nsh->norm_quality_factor);
  MdcPrntScrn("norm_quality_factor_code : %hd\n",nsh->norm_quality_factor_code);
  MdcPrntScrn("storage_order            : %hd\n",nsh->storage_order);
  MdcPrntScrn("span                     : %hd\n",nsh->span);
  MdcPrntScrn("z_elements[64]           : <not printed>\n");
  MdcPrntScrn("cti_fill[123]            : <not printed>\n");
  MdcPrntScrn("user_fill[50]            : <not printed>\n");

}

const char *MdcReadECAT7(FILEINFO *fi)
{
  FILE *fp = fi->ifp;
  int i, error/*, UNSUPPORTED*/;
  const char *err;
  char *str;
  struct tm time, *ptime;
  time_t my_epoch;
  Mdc_Main_header7 mh;
  Mdc_Image_subheader7 ish;
  Mdc_Attn_subheader7  ash;
/*
  Mdc_Scan_subheader7 ssh;
  Mdc_Norm_subheader7 nsh;
*/
  struct Mdc_MatDir entry, matrix_list[MDC_ECAT7_MAX_MATRICES];
  struct Mdc_Matval matval;
  Uint32 number, slice, img=0, vol=0, bytes;
  Uint32 group=0, group_slice, skip_bytes=0,  row, rbytes;
  int bed,gate,frame,plane,nb,ng,nf,np,nd;
  int matnum, startblk, endblk, num_matrices;
  IMG_DATA *id;
  DYNAMIC_DATA *dd=NULL;
  Int16 bits, type;
  Uint8 *mbufr, *pmbufr, *pbuf;
  float slice_position;

  if (MDC_PROGRESS) MdcProgress(MDC_PROGRESS_BEGIN,0.,"Reading ECAT7:");

  if (MDC_VERBOSE) MdcPrntMesg("ECAT7 Reading <%s> ...",fi->ifname);

  /* put some defaults we use */
  fi->endian=MDC_FILE_ENDIAN=MDC_BIG_ENDIAN;
  fi->modality = M_PT;

  error = mdc_mat_read_main_header7(fp, &mh);
  if (error) return("ECAT7 Bad read main header");

  if (MDC_INFO || MDC_INFO_DB) MdcEcatPrintMainHdr(&mh);

  if (MDC_INFO_DB) return(NULL); /* just needed db info */

  /* check for supported file types *
  switch (mh.file_type) {
    case MDC_ECAT7_FILE_TYPE_IMAGE16 :
    case MDC_ECAT7_FILE_TYPE_VOLUME8 :
    case MDC_ECAT7_FILE_TYPE_VOLUME16:
    case MDC_ECAT7_FILE_TYPE_IMAGE8  :
        UNSUPPORTED = MDC_NO;
        break;
    default:
        UNSUPPORTED = MDC_YES;
  }*/
  /* if (UNSUPPORTED == MDC_YES) return("ECAT7 Unsupported file type"); */

  if (mh.num_frames  <= 0 ) mh.num_frames  = 1;
  if (mh.num_gates   <= 0 ) mh.num_gates   = 1;
  if (mh.num_bed_pos <  0 ) mh.num_bed_pos = 0;

  /* fill in global FILEINFO data */
  fi->dim[0]= 6;
  fi->dim[3]= mh.num_planes;
  fi->dim[4]= mh.num_frames;
  fi->dim[5]= mh.num_gates;
  fi->dim[6]= mh.num_bed_pos + 1; /* must be 1-based */

  /* check for unsupported bed overlap */
  if (fi->dim[6] > 1) {
    float axial_width, bed_offset=mh.bed_position[0];

    if (bed_offset < 0) bed_offset = -bed_offset;

    axial_width = mh.plane_separation * (float)fi->dim[3];
    if ((axial_width - bed_offset) >= 1.0) {
      MdcPrntWarn("ECAT7 Bed overlaps unsupported");
    }
  }

  for (i=3, number=1; i<=6; i++) number*=fi->dim[i];

  if (number == 0) return("ECAT7 No valid images specified");

  /* fill in orientation information */
  switch (mh.patient_orientation) {
    case MDC_ECAT7_FEETFIRST_PRONE:
        fi->pat_slice_orient = MDC_PRONE_FEETFIRST_TRANSAXIAL;  break;
    case MDC_ECAT7_HEADFIRST_PRONE:
        fi->pat_slice_orient = MDC_PRONE_HEADFIRST_TRANSAXIAL;  break;
    case MDC_ECAT7_FEETFIRST_SUPINE:
        fi->pat_slice_orient = MDC_SUPINE_FEETFIRST_TRANSAXIAL; break;
    case MDC_ECAT7_HEADFIRST_SUPINE:
        fi->pat_slice_orient = MDC_SUPINE_HEADFIRST_TRANSAXIAL; break;
    case MDC_ECAT7_FEETFIRST_RIGHT:
        fi->pat_slice_orient = MDC_DECUBITUS_RIGHT_FEETFIRST_TRANSAXIAL; break;
    case MDC_ECAT7_HEADFIRST_RIGHT:
        fi->pat_slice_orient = MDC_DECUBITUS_RIGHT_HEADFIRST_TRANSAXIAL; break;
    case MDC_ECAT7_FEETFIRST_LEFT:
        fi->pat_slice_orient = MDC_DECUBITUS_LEFT_FEETFIRST_TRANSAXIAL;  break;
    case MDC_ECAT7_HEADFIRST_LEFT:
        fi->pat_slice_orient = MDC_DECUBITUS_LEFT_HEADFIRST_TRANSAXIAL;  break;
    default:
        fi->pat_slice_orient = MDC_SUPINE_HEADFIRST_TRANSAXIAL;
        MdcPrntWarn("ECAT7 unknown patient orientation");
  }

  str = MdcGetStrPatPos(fi->pat_slice_orient);
  MdcStringCopy(fi->pat_pos,str,strlen(str));

  str = MdcGetStrPatOrient(fi->pat_slice_orient);
  MdcStringCopy(fi->pat_orient,str,strlen(str));

  /* fill in patient study related information */
  switch (mh.patient_sex[0]) {
    case 0:  fi->patient_sex[0] = 'M'; break;
    case 1:  fi->patient_sex[0] = 'F'; break;
    default: fi->patient_sex[0] = 'U';
  }
  fi->patient_sex[1]='\0';
  MdcStringCopy(fi->patient_name,mh.patient_name,32);
  MdcStringCopy(fi->patient_id,mh.patient_id,16);
  fi->patient_height = mh.patient_height;
  fi->patient_weight = mh.patient_weight;

  ptime = &time;
  my_epoch = (time_t)mh.patient_birth_date;
#ifdef LIBMDC_HAVE_LOCALTIME_R
  localtime_r(&my_epoch, ptime);
#else
  ptime = localtime(&my_epoch);
#endif
  if (ptime == NULL) {
    MdcPrntWarn("ECAT7: Couldn't resolve patient birth date");
    strcpy(fi->patient_dob,"00000000");
  }else{
    sprintf(fi->patient_dob,"%.4d%.2d%.2d",ptime->tm_year + 1900
                                          ,ptime->tm_mon  + 1
                                          ,ptime->tm_mday);
  }

  ptime = &time;
#ifdef LIBMDC_HAVE_LOCALTIME_R
  my_epoch = (time_t)(mh.scan_start_time);
  localtime_r(&my_epoch, ptime);
#else
  ptime = localtime(&my_epoch);
#endif
  if (ptime == NULL) {
    MdcPrntWarn("ECAT7: Couldn't resolve scan start time");
    fi->study_date_day   = 0;
    fi->study_date_month = 1;
    fi->study_date_year  = 1900;
    fi->study_time_hour  = 0;
    fi->study_time_minute= 0;
    fi->study_time_second= 0;
  }else{
    fi->study_date_day   = ptime->tm_mday;
    fi->study_date_month = ptime->tm_mon  + 1;
    fi->study_date_year  = ptime->tm_year + 1900;
    fi->study_time_hour  = ptime->tm_hour;
    fi->study_time_minute= ptime->tm_min;
    fi->study_time_second= ptime->tm_sec;
  }

  switch (mh.acquisition_type) {
    case MDC_ECAT7_SCAN_TRANSMISSION:
    case MDC_ECAT7_SCAN_STATIC_EMISSION:
      fi->acquisition_type = MDC_ACQUISITION_TOMO;
      break;
    case MDC_ECAT7_SCAN_DYNAMIC_EMISSION:
      fi->acquisition_type = MDC_ACQUISITION_DYNAMIC;
      break;
    case MDC_ECAT7_SCAN_GATED_EMISSION:
      fi->acquisition_type = MDC_ACQUISITION_GSPECT;
      break;
    case MDC_ECAT7_SCAN_BLANK:
    case MDC_ECAT7_SCAN_TRANS_RECTILINEAR:
    case MDC_ECAT7_SCAN_EMISSION_RECTILINEAR:
    default:
      fi->acquisition_type = MDC_ACQUISITION_UNKNOWN;
      break;
  }

  sprintf(mdcbufr,"ECAT%hd",mh.system_type);
  MdcStringCopy(fi->manufacturer,mdcbufr,strlen(mdcbufr));
  MdcStringCopy(fi->operator_name,mh.operator_name,32);
  MdcStringCopy(fi->study_descr,mh.study_description,32);
  MdcStringCopy(fi->study_id,mh.study_type,12);
  MdcStringCopy(fi->institution,mh.facility_name,20);
  MdcStringCopy(fi->radiopharma,mh.radiopharmaceutical,32);
  MdcStringCopy(fi->isotope_code,mh.isotope_name,8);

  fi->isotope_halflife = mh.isotope_halflife;
  fi->injected_dose    = MdcmCi2MBq(mh.dosage);
  fi->gantry_tilt      = mh.gantry_tilt;

  if (MDC_ECHO_ALIAS == MDC_YES) {
    MdcEchoAliasName(fi); return(NULL);
  }

  if (!MdcGetStructID(fi,number))
    return("ECAT7 Bad malloc IMG_DATA structs");

  /* always malloc dyndata structs */
  if (!MdcGetStructDD(fi,(Uint32)fi->dim[4]*fi->dim[5]*fi->dim[6]))
    return("ECAT7 Couldn't malloc DYNAMIC_DATA structs");

  /* ECAT7: matrices for each volume */
  num_matrices = mdc_mat_list7(fp, matrix_list, MDC_ECAT7_MAX_MATRICES);
  if (num_matrices == 0)
    return("ECAT7 No matrices found");
  if ((Uint32)num_matrices > (fi->number / fi->dim[3]))
    return("ECAT7 Too many matrices found");

  if (MDC_MY_DEBUG) {
    int t;

    MdcDebugPrint("%d.%d.%d.%d",fi->dim[3]
                               ,fi->dim[4]
                               ,fi->dim[5]
                               ,fi->dim[6]);

    for (t=0; t<num_matrices; t++) {
       MdcDebugPrint("[%2d]\t%d\t%d\t%d\t%d",t
                                            ,matrix_list[t].matnum
                                            ,matrix_list[t].strtblk
                                            ,matrix_list[t].endblk
                                            ,matrix_list[t].matstat);
    }
  }

  img=0; vol=0;
  for (bed=0; bed<fi->dim[6]; bed++)
  for (gate=1; gate<=fi->dim[5]; gate++)
  for (frame=1; frame<=fi->dim[4]; frame++, vol++)
  for (plane=1; plane<=fi->dim[3]; ) {

     if (vol == num_matrices) break;

     if (fi->dynnr > 0) dd = &fi->dyndata[(fi->dim[4]*bed) + (frame-1)];

     mdc_mat_numdoc(matrix_list[vol].matnum,&matval);
     nf = matval.frame; np = matval.plane; ng = matval.gate;
     nb = matval.bed;   nd = matval.data;
     matnum = mdc_mat_numcod(nf,np,ng,nd,nb);
     MdcDebugPrint("matnum       = %d",matnum);
     if (!mdc_mat_lookup7(fp, matnum, &entry)) continue;
     startblk = entry.strtblk + 1;
     endblk = entry.endblk - entry.strtblk;

     MdcDebugPrint("entry.endblk = %d",entry.endblk);
     MdcDebugPrint("entry.strtblk= %d",entry.strtblk);
     MdcDebugPrint("startblk     = %d",startblk);
     MdcDebugPrint("endblk       = %d",endblk);

     switch (mh.file_type) {

       case MDC_ECAT7_FILE_TYPE_IMAGE8  :
       case MDC_ECAT7_FILE_TYPE_IMAGE16 :
       case MDC_ECAT7_FILE_TYPE_VOLUME8 :
       case MDC_ECAT7_FILE_TYPE_VOLUME16:

         error = mdc_mat_read_image_subheader7(fp, startblk-1, &ish);
         if (error) return("ECAT7 Bad read image subheader");

         if (MDC_INFO) MdcEcatPrintImgSubHdr(&ish, (int)(vol + 1));

         fi->dim[1] = ish.x_dimension;
         fi->dim[2] = ish.y_dimension;

#if MDC_NUM_PLANES_FIX
         if ((mh.num_planes != ish.z_dimension) && (vol == 0)) {

           MdcPrntWarn("ECAT7 Fix wrong num_planes value");

           mh.num_planes = ish.z_dimension;
           fi->dim[3]    = ish.z_dimension;

           for (i=3, number=1; i<=6; i++) number*=fi->dim[i];
           if (!MdcGetStructID(fi,number))
             return("ECAT7 Bad realloc IMG_DATA structs");

         }
#endif

         switch (ish.data_type) {
           case BYTE_TYPE:
               bits = 8;  type = BIT8_U;  break;
           case VAX_I2:
           case SUN_I2:
               bits = 16; type = BIT16_S; break;
           case VAX_I4:
           case SUN_I4:
               bits = 32; type = BIT32_S; break;
           case VAX_R4:
           case IEEE_R4:
               bits = 32; type = FLT32;   break;
           default:
               return("ECAT7: Unsupported data type");
         }

         /* fill in DYNAMIC_DATA structs */
         if ((dd != NULL) && (plane == 1)) {
           /* just one subheader */
           dd->nr_of_slices        = fi->dim[3];
           dd->time_frame_start    = (float)ish.frame_start_time;
           dd->time_frame_duration = (float)ish.frame_duration;
         }

         /* bytes entire volume (matrix blocks) */
         bytes = fi->dim[1] * fi->dim[2] * fi->dim[3] * MdcType2Bytes(type);
         MdcDebugPrint("volume: %d bytes",bytes);
         bytes = MdcMatrixBlocks(bytes);

         MdcDebugPrint("matrix: %d bytes",bytes);

         mbufr = malloc(bytes);
         if (mbufr == NULL)
           return("ECAT7 Bad malloc image matrix data buffer");

         error = mdc_mat_read_mat_data(fp,startblk,endblk,mbufr,ish.data_type);

         if (error) {
           MdcPrntWarn("ECAT7 Bad read image matrix data");
           err=MdcHandleTruncated(fi,img+1,MDC_YES);
           if(err != NULL) { MdcFree(mbufr); return(err); }
         }

         if (fi->truncated) break;

         /* bytes each image */
         bytes = fi->dim[1] * fi->dim[2] * MdcType2Bytes(type);

         for (slice=0; slice < fi->dim[3]; slice++, img++, plane++) {

            if (MDC_PROGRESS)
              MdcProgress(MDC_PROGRESS_INCR,1./(float)fi->number,NULL);

            /* fill in IMG_DATA struct */
            id = &fi->image[img];
            id->width = fi->dim[1];
            id->height= fi->dim[2];
            id->bits  = bits;
            id->type  = type;

            id->quant_units  = 1;
            id->quant_scale  = ish.scale_factor;
            id->calibr_units = mh.calibration_units;
            id->calibr_fctr  = mh.ecat_calibration_factor;

            id->pixel_xsize = ish.x_pixel_size * 10.0; /* mm */
            id->pixel_ysize = ish.y_pixel_size * 10.0; /* mm */
            id->slice_width = ish.z_pixel_size * 10.0; /* mm */
            id->slice_spacing = id->slice_width;

            /* slice position with bed offset (mm) */
            if (bed == 0) {
              slice_position = mh.init_bed_position;
            }else{
              slice_position = mh.init_bed_position + mh.bed_position[bed-1];
            }
            slice_position *= 10.; /* mm */

            MdcFillImgPos(fi,img,slice,slice_position);

            MdcFillImgOrient(fi,img);

            id->buf = MdcGetImgBuffer(bytes);
            if (id->buf == NULL) {
              MdcFree(mbufr);
              return("ECAT7 Bad malloc image buffer");
            }

            memcpy(id->buf, mbufr + (bytes*slice), bytes);

         }

         MdcFree(mbufr);

         break;

       case MDC_ECAT7_FILE_TYPE_ATTNCORR:

         error = mdc_mat_read_attn_subheader7(fp, startblk-1, &ash);
         if (error) return("ECAT7 Bad read attenuation subheader");

         if (MDC_INFO) MdcEcatPrintAttnSubHdr(&ash, (int)(vol + 1));

         fi->dim[1] = ash.num_r_elements;
         fi->dim[2] = ash.num_angles;

         /* MARK: just retrieve group0 */
         /* fi->dim[3] = ash.num_z_elements; */

         /* MARK: or   retrieve all    */
         fi->dim[3] = 0;
         for (i=0; ash.z_elements[i]; i++) fi->dim[3] += ash.z_elements[i];


         for (i=3, number=1; i<=6; i++) number*=fi->dim[i];
         if (!MdcGetStructID(fi,number))
           return("ECAT7 Bad realloc IMG_DATA structs");

         switch (ash.data_type) {
           case BYTE_TYPE:
               bits = 8;  type = BIT8_U;  break;
           case VAX_I2:
           case SUN_I2:
               bits = 16; type = BIT16_S; break;
           case VAX_I4:
           case SUN_I4:
               bits = 32; type = BIT32_S; break;
           case VAX_R4:
           case IEEE_R4:
               bits = 32; type = FLT32;   break;
           default:
               return("ECAT7: Unsupported data type");
         }

         /* bytes entire volume (matrix blocks) */
         bytes = fi->dim[1] * fi->dim[2] * fi->dim[3] * MdcType2Bytes(type);
         MdcDebugPrint("volume: %d bytes",bytes);
         bytes = MdcMatrixBlocks(bytes);


         MdcDebugPrint("matrix: %d bytes",bytes);

         mbufr = malloc(bytes);
         if (mbufr == NULL)
           return("ECAT7 Bad malloc attenuation matrix data buffer");

         endblk = startblk + (bytes / MdcMatBLKSIZE) - 1;

         error = mdc_mat_read_mat_data(fp,startblk,endblk,mbufr,ash.data_type);

         if (error) {
           MdcPrntWarn("ECAT7 Bad read attenuation matrix data");
           err=MdcHandleTruncated(fi,img+1,MDC_YES);
           if(err != NULL) { MdcFree(mbufr); return(err); }
         }

         if (fi->truncated) break;

         /* bytes each image */
         bytes = fi->dim[1] * fi->dim[2] * MdcType2Bytes(type);


         group_slice=0;
         for (slice=0; slice < fi->dim[3]; slice++, img++, plane++) {

            if (MDC_PROGRESS)
              MdcProgress(MDC_PROGRESS_INCR,1./(float)fi->number,NULL);

            /* fill in IMG_DATA struct */
            id = &fi->image[img];
            id->width = fi->dim[1];

            id->height= fi->dim[2];
            id->bits  = bits;
            id->type  = type;

            id->quant_units  = 1;
            id->quant_scale  = ash.scale_factor;
            id->calibr_units = mh.calibration_units;
            id->calibr_fctr  = mh.ecat_calibration_factor;

            id->pixel_xsize = ash.x_resolution * 10.0; /* mm */
            id->pixel_ysize = ash.y_resolution * 10.0; /* mm */
            id->slice_width = ash.z_resolution * 10.0; /* mm */
            id->slice_spacing = id->slice_width;

            /* slice position with bed offset (mm) */
            if (bed == 0) {
              slice_position = mh.init_bed_position;
            }else{
              slice_position = mh.init_bed_position + mh.bed_position[bed-1];
            }
            slice_position *= 10.; /* mm */

            MdcFillImgPos(fi,img,slice,slice_position);

            MdcFillImgOrient(fi,img);

            id->buf = MdcGetImgBuffer(bytes);
            if (id->buf == NULL) {
              MdcFree(mbufr);
              return("ECAT7 Bad malloc image buffer");
            }

            /* copy date */
            if (ash.storage_order == 0) {
              /* view mode: for the first group of z_elements we have       */
              /*            all 1st rows, all 2nd rows, etc. Then follows   */
              /*            same storage for next group of z_elements,      */
              /*            at least we think                               */
              /* MARK: still not proper for all groups, actually only 1st ok*/
              rbytes = fi->dim[1] * MdcType2Bytes(type);
              if (group_slice == ash.z_elements[group] ) {
                skip_bytes += ash.z_elements[group] * bytes;
                group += 1;
                group_slice = 0;
              }
              pmbufr = mbufr + skip_bytes  + group_slice * rbytes; /* MARK */

              /* MARK printf("DEBUG: skip_bytes(%u) + group_slice(%u) * rbytes (%u) = %u\n",skip_bytes,group_slice,rbytes,skip_bytes+group_slice*rbytes); */

              for (row=0; row < fi->dim[2]; row++) {
                 pbuf = id->buf+(rbytes * row);
                 memcpy(pbuf, pmbufr, rbytes);
                 pmbufr += ash.z_elements[group] * rbytes;
              }
            }else{
              /* sinogram mode: fine, just copy */
              memcpy(id->buf, mbufr + (bytes*slice), bytes);
            }

            group_slice++;

         }

         MdcFree(mbufr);

         break;

       default:

              return("ECAT7 Unsupported file type");

     }

  }

  /* set remaing FILEINFO data */
  id = &fi->image[0];
  fi->bits = id->bits;
  fi->type = id->type;
  fi->pixdim[0] = 3;
  fi->pixdim[1] = id->pixel_xsize;
  fi->pixdim[2] = id->pixel_ysize;
  fi->pixdim[3] = id->slice_width;

  switch (mh.file_type) {
    case MDC_ECAT7_FILE_TYPE_IMAGE8  :
    case MDC_ECAT7_FILE_TYPE_IMAGE16 :
    case MDC_ECAT7_FILE_TYPE_VOLUME8 :
    case MDC_ECAT7_FILE_TYPE_VOLUME16:
      fi->reconstructed = MDC_YES;
      if (ish.decay_corr_fctr > 1.0) fi->decay_corrected = MDC_YES;
      break;
    default:
      fi->reconstructed = MDC_NO;
  }

  MdcCloseFile(fi->ifp);

  if (fi->truncated) return("ECAT7 Truncated image file");

  return(NULL);

}

#if MDC_INCLUDE_TPC
/*!
 * Converts FILEINFO structure and MDC mainheader to TPC ecat7 mainheader
 *
 * @param mh pointer to MDC mainheader
 * @param h pointer to TPC imageheader
 * @param image_i image index number [0..fi->number-1]
 * @param frame_i frame index number [0..fi->dim[4]-1]
 */
int MdcConvertToTPCEcat7image(FILEINFO* fi,
                              ECAT7_imageheader* h, int image_i, int frame_i)
{
  int i = 0;

  /* short int <- ECAT7_SUNI2 (must be for matrix writing) */
  h->data_type = ECAT7_SUNI2;
  h->num_dimensions = fi->dim[0];
  switch (fi->pat_slice_orient) {
#if MDC_TPC_SITE
    case MDC_SAGITTAL:
      /* short int <- Int16 */
      h->x_dimension = fi->dim[3];
      h->y_dimension = fi->dim[2];
      h->z_dimension = fi->dim[1];
      /* float <- ??? */
      h->x_offset = 0;
      h->y_offset = 0;
      h->z_offset = 0;
      h->x_pixel_size = fi->pixdim[3]/10;
      h->y_pixel_size = fi->pixdim[2]/10;
      h->z_pixel_size = fi->pixdim[1]/10;
      /* float <- ??? */
      h->x_resolution = 0.0f;
      h->y_resolution = 0.0f;
      h->z_resolution = 0.0f;
      break;
    case MDC_CORONAL:
      /* short int <- Int16 */
      h->x_dimension = fi->dim[1];
      h->y_dimension = fi->dim[3];
      h->z_dimension = fi->dim[2];
      /* float <- ??? */
      h->x_offset = 0;
      h->y_offset = 0;
      h->z_offset = 0;
      h->x_pixel_size = fi->pixdim[1]/10;
      h->y_pixel_size = fi->pixdim[3]/10;
      h->z_pixel_size = fi->pixdim[2]/10;
      /* float <- ??? */
      h->x_resolution = 0.0f;
      h->y_resolution = 0.0f;
      h->z_resolution = 0.0f;
      break;
#endif
    default:
      /* short int <- Int16 */
      h->x_dimension = fi->dim[1];
      h->y_dimension = fi->dim[2];
      h->z_dimension = fi->dim[3];
      /* float <- ??? */
      h->x_offset = 0;
      h->y_offset = 0;
      h->z_offset = 0;
      h->x_pixel_size = fi->pixdim[1]/10;
      h->y_pixel_size = fi->pixdim[2]/10;
      h->z_pixel_size = fi->pixdim[3]/10;
      /* float <- ??? */
      h->x_resolution = 0.0f;
      h->y_resolution = 0.0f;
      h->z_resolution = 0.0f;
      break;
  }

  if (fi->image) {
/* eNlf: MARK - leave out for now, currently no slice_location in IMG_DATA
    switch(fi->pat_slice_orient) {
#if MDC_TPC_SITE
      case MDC_SAGITTAL:
             if(fi->dim[3] > 1 && fabs(fi->image[fi->number-1].slice_location - fi->image[0].slice_location) != 0)
        h->x_pixel_size = fabs(fi->image[fi->number-1].slice_location - fi->image[0].slice_location)/((fi->dim[3]-1)*10.0f);
      else
        h->x_pixel_size = fi->image[0].slice_width/10;
      break;
      case MDC_CORONAL:
             if(fi->dim[3] > 1 && fabs(fi->image[fi->number-1].slice_location - fi->image[0].slice_location) != 0)
        h->y_pixel_size = fabs(fi->image[fi->number-1].slice_location - fi->image[0].slice_location)/((fi->dim[3]-1)*10.0f);
      else
        h->y_pixel_size = fi->image[0].slice_width/10;
      break;
#endif
      default:
             if(fi->dim[3] > 1 && fabs(fi->image[fi->number-1].slice_location - fi->image[0].slice_location) != 0)
        h->z_pixel_size = fabs(fi->image[fi->number-1].slice_location - fi->image[0].slice_location)/((fi->dim[3]-1)*10.0f);
      else
        h->z_pixel_size = fi->image[0].slice_width/10;
    }
*/
    h->recon_zoom = fi->image[image_i].recon_scale;

    if (fi->image[image_i].rescaled) {
      h->scale_factor = fi->image[image_i].rescaled_fctr;
    }else{
      h->scale_factor = 1.;
    }

    /* short int <- double */
    h->image_min = fi->image[image_i].min;
    h->image_max = fi->image[image_i].max;
  }

  if (fi->dyndata && frame_i < fi->dynnr) {
    /* int <- float (ms) */
    h->frame_duration = fi->dyndata[frame_i].time_frame_duration;
    /* int <- float (ms) */
    h->frame_start_time = fi->dyndata[frame_i].time_frame_start;
  }
  /* short int <- ??? */
  h->filter_code = 0;
  /* float <- ??? */
  h->num_r_elements = 0;
  /* float <- ??? */
  h->num_angles = 0.0f;
  /* float <- ??? */
  h->z_rotation_angle = 0.0f;
  /* float <- ??? */
  h->decay_corr_fctr = 0.0f;
  /* int <- ??? */
  h->processing_code = 0;
  if (fi->gatednr > 0 && fi->gdata != NULL) {
    /* int <- float (ms) */
    h->gate_duration = fi->gdata[0].image_duration;
    /* int <- float (ms) */
    h->r_wave_offset = fi->gdata[0].window_low;
    h->num_accepted_beats = fi->gdata[0].cycles_acquired;
  }else{
    h->gate_duration = 0;
    h->r_wave_offset = 0;
    h->num_accepted_beats = 0;
  }
  /* float <- ??? */
  h->filter_cutoff_frequency = 0.0f;
  /* float <- ??? */
  h->filter_resolution = 0.0f;
  /* float <- ??? */
  h->filter_ramp_slope = 0.0f;
  /* short int <- ??? */
  h->filter_order = 0;
  /* float <- ??? */
  h->filter_scatter_fraction = 0.0f;
  /* float <- ??? */
  h->filter_scatter_slope = 0.0f;
  /* char annotation[40]; */
  for(i = 0; i < 40; i++) h->annotation[i] = '\0';
  /* float <- ??? */
  h->mt_1_1 = 0.0f;
  /* float <- ??? */
  h->mt_1_2 = 0.0f;
  /* float <- ??? */
  h->mt_1_3 = 0.0f;
  /* float <- ??? */
  h->mt_2_1 = 0.0f;
  /* float <- ??? */
  h->mt_2_2 = 0.0f;
  /* float <- ??? */
  h->mt_2_3 = 0.0f;
  /* float <- ??? */
  h->mt_3_1 = 0.0f;
  /* float <- ??? */
  h->mt_3_2 = 0.0f;
  /* float <- ??? */
  h->mt_3_3 = 0.0f;
  /* float <- ??? */
  h->rfilter_cutoff = 0.0f;
  /* float <- ??? */
  h->rfilter_resolution = 0.0f;
  /* short int <- ??? */
  h->rfilter_code = 0.0f;
  /* short int <- ??? */
  h->rfilter_order = 0.0f;
  /* float <- ??? */
  h->zfilter_cutoff = 0.0f;
  /* float <- ??? */
  h->zfilter_resolution = 0.0f;
  /* short int <- ??? */
  h->zfilter_code = 0;
  /* short int <- ??? */
  h->zfilter_order = 0;
  /* float <- ??? */
  h->mt_1_4 = 0.0f;
  /* float <- ??? */
  h->mt_2_4 = 0.0f;
  /* float <- ??? */
  h->mt_3_4 = 0.0f;
  /* short int <- ??? */
  h->scatter_type = 0;
  /* short int <- ??? */
  h->recon_type = 0;
  /* short int <- ??? */
  h->recon_views = 0;
  /* short int <- ??? */
  for(i = 0; i < 87; i++) h->fill_cti[i] = 0;
  /* short int <- ??? */
  for(i = 0; i < 49; i++) h->fill_user[i] = 0;

  return 0;
}

/*!
 * Converts FILEINFO structure and MDC mainheader to TPC ecat7 mainheader
 *
 * @param fi file structure
 * @param mh pointer to MDC mainheader
 * @param h pointer to TPC mainheader
 */
int MdcConvertToTPCEcat7(FILEINFO* fi,
                         Mdc_Main_header* mh, ECAT7_mainheader* h)
{
  struct tm timeinfo;
  int i = 0;
  char number[5];

  /* use default ecat7 magic number */
  strncpy(h->magic_number, ECAT7V_MAGICNR,14);
  strncpy(h->original_file_name,mh->original_file_name,20);
  h->sw_version = 72;
  /* short int <- Int16 */
  h->system_type = mh->system_type;
  /* short int <- ECAT7_VOLUME16 */
  h->file_type = ECAT7_VOLUME16;
  strncpy(h->serial_number,"unknown",10);
  /* int <- Int16 Int16 Int16 Int16 */
  memset((void *)&timeinfo,0,sizeof(timeinfo));
    timeinfo.tm_year = mh->scan_start_year-1900;
    timeinfo.tm_mon  = mh->scan_start_month-1;
    timeinfo.tm_mday = mh->scan_start_day;
    timeinfo.tm_hour = mh->scan_start_hour;
    timeinfo.tm_min  = mh->scan_start_minute;
    timeinfo.tm_sec  = mh->scan_start_second;
    timeinfo.tm_isdst= -1;
  h->scan_start_time = (unsigned int)mktime(&timeinfo);
  strncpy(h->isotope_name,fi->isotope_code,(8<MDC_MAXSTR ? 8 : MDC_MAXSTR));
  h->isotope_halflife = mh->isotope_halflife;
  strncpy(h->radiopharmaceutical,
          fi->radiopharma,(32<MDC_MAXSTR ? 32 : MDC_MAXSTR));
  h->gantry_tilt = mh->gantry_tilt;
  h->gantry_rotation = mh->gantry_rotation;
  h->bed_elevation = mh->bed_elevation;
  /* float <- ??? */
  h->intrinsic_tilt = 0.0f;
  /* short int <- Int16 */
  h->wobble_speed = mh->wobble_speed;
  /* short int <- Int16 */
  h->transm_source_type = mh->transm_source_type;
  /* float <- ??? */
  h->distance_scanned = 0.0f;
  h->transaxial_fov = mh->transaxial_fov;
  /* short int <- Int16 */
  h->angular_compression = mh->compression_code;
  /* short int <- Int16 */
  h->coin_samp_mode = mh->coin_samp_mode;
  /* short int <- Int16 */
  h->axial_samp_mode = mh->axial_samp_mode;
  h->ecat_calibration_factor = 1.; /* eNlf: global, prefer scale per plane*/
  /* short int <- Int16 */
  h->calibration_units = mh->calibration_units;
  /* short int <- ??? */
  h->calibration_units_label = 0;
  /* short int <- Int16 */
  h->compression_code = mh->compression_code;
  strncpy(h->study_type,mh->study_name,12);
  strncpy(h->patient_id,mh->patient_id,16);
  strncpy(h->patient_name,mh->patient_name,32);
  switch (fi->patient_sex[0]) {
    case 'M': h->patient_sex = 0; break;
    case 'F': h->patient_sex = 1; break;
    default : h->patient_sex = 3;
  }
  h->patient_dexterity = mh->patient_dexterity;
  sscanf(mh->patient_age,"%f",&h->patient_age);
  h->patient_height = fi->patient_height;
  h->patient_weight = fi->patient_weight;

  /* patient_dob: YYYYMMDD -> tm_year, tm_mon, tm_day -> unsigned int*/
  memset((void *)&timeinfo,0,sizeof(timeinfo));
     /* YYYY */
     memcpy(number,&fi->patient_dob[0],4); number[4]='\0';
     timeinfo.tm_year = atoi(number) - 1900;
     /* MM */
     memcpy(number,&fi->patient_dob[4],2); number[2]='\0';
     timeinfo.tm_mon = atoi(number) - 1;
     /* DD */
     memcpy(number,&fi->patient_dob[6],2); number[2]='\0';
     timeinfo.tm_mday = atoi(number);
  h->patient_birth_date = (unsigned int)mktime(&timeinfo);

  strncpy(h->physician_name,mh->physician_name,32);
  strncpy(h->operator_name,mh->operator_name,32);
  strncpy(h->study_description,mh->study_description,32);
  /* short int <- Int16 */
  h->acquisition_type = mh->acquisition_type;
  /* short int <- char[MDC_MAXSTR] */
  h->patient_orientation = 0;
  if(strncmp(fi->pat_pos,"FFP",3) == 0) h->patient_orientation = 0;
  else if(strncmp(fi->pat_pos,"HFP",3) == 0) h->patient_orientation = 1;
  else if(strncmp(fi->pat_pos,"FFS",3) == 0) h->patient_orientation = 2;
  else if(strncmp(fi->pat_pos,"HFS",3) == 0) h->patient_orientation = 3;
  else if(strncmp(fi->pat_pos,"FFDR",4) == 0) h->patient_orientation = 4;
  else if(strncmp(fi->pat_pos,"HFDR",4) == 0) h->patient_orientation = 5;
  else if(strncmp(fi->pat_pos,"FFDL",4) == 0) h->patient_orientation = 6;
  else if(strncmp(fi->pat_pos,"HFDL",4) == 0) h->patient_orientation = 7;
  else MdcPrntWarn("Unrecognized patient position: %s\n",fi->pat_pos);
#if MDC_TPC_SITE
  strncpy(h->facility_name,mh->original_file_name,20);
#else
  strncpy(h->facility_name,fi->institution,20);
#endif
  /* short int <- Int16 */
  switch (fi->pat_slice_orient) {
#if MDC_TPC_SITE
    case MDC_SAGITTAL: h->num_planes = fi->dim[1]; break;
    case MDC_CORONAL:  h->num_planes = fi->dim[2]; break;
#endif
    default:           h->num_planes = fi->dim[3]; break;
  }
  /* short int <- Int16 */
  h->num_frames = fi->dim[4];
  /* short int <- Int16 */
  h->num_gates = fi->dim[5];
  /* short int (zero-based value) <- Int16 (one-based value) */
  h->num_bed_pos = (fi->dim[6]>=1 ? fi->dim[6]-1 : 0);
  h->init_bed_position = mh->init_bed_position;
  for (i = 0; i < 15; i++) h->bed_position[i] = mh->bed_offset[i];
  h->plane_separation = mh->plane_separation;
  /* short int <- Int16 */
  h->lwr_sctr_thres = mh->lwr_sctr_thres;
  /* short int <- Int16 */
  h->lwr_true_thres = mh->lwr_true_thres;
  /* short int <- Int16 */
  h->upr_true_thres = mh->upr_true_thres;
  strncpy(h->user_process_code,mh->original_file_name,10);
  /* short int <- Int16 */
  h->acquisition_mode = mh->acquisition_mode;
  /* float <- ?? */
  h->bin_size = 0.0f;
  /* float <- ?? */
  h->branching_fraction = 0.0f;
  /* int <- Int16 Int16 Int16 Int16 */
  timeinfo.tm_year = fi->study_date_year-1900;
  timeinfo.tm_mon = fi->study_date_month-1;
  timeinfo.tm_mday = fi->study_date_day;
  timeinfo.tm_hour = fi->dose_time_hour;
  timeinfo.tm_min = fi->dose_time_minute;
  timeinfo.tm_sec = fi->dose_time_second;
  h->dose_start_time = (unsigned int)mktime(&timeinfo);
  h->dosage = MdcMBq2mCi(fi->injected_dose);
  /* float <- ?? */
  h->well_counter_corr_factor = 0.0f;
  /* char[32] <- char[MDC_MAXSTR] */
  strncpy(h->data_units,"unknown",32);
/* eNlf: MARK -  doesn't belong in modality XA
  if (fi->mod) {
    if ( strcmp(fi->mod->xa_info.Photo_Interp,"BQML") == 0 )
      strncpy(h->data_units,"kBq/ml",32);
  }
*/
  /* int <- ??? */
  h->septa_state = 0;
  /* int[6] <- ??? */
  for(i = 0; i < 6; i++) h->fill_cti[i] = 0;

  return 0;
}

/*!
 * Modified to execute TPC ecat7 writing
 *
 * @param fi file structure
 * @return Error message, or NULL if successfull
 */
const char *MdcWriteECAT7(FILEINFO *fi)
{
  int ret = 0;
  int bytes = 0;
  ECAT7_mainheader h;
  ECAT7_imageheader ih;
  float* TPC_frame = 0;
  float* TPC_frame_start = 0;
  int frame_size = 0;
/*  int plane_dir = 0;*/
  Uint32 uwidth, uheight;
  IMG_DATA *id;
  Mdc_Main_header mh;
  Uint8 *buf, *maxbuf;
  Int16 type;
  Int32 matnum, bed, gate, frame, plane, img=0;
#if MDC_TPC_SITE
  Int32 column, row;
  float flt32=0;
#else
  Uint32 size;
#endif

  if (MDC_FILE_STDOUT == MDC_YES)
    return("ECAT7 Writing to stdout unsupported for this format");

  MDC_WRITE_ENDIAN = MDC_LITTLE_ENDIAN; /* always (VAX) */

  if (XMDC_GUI == MDC_NO) {
    MdcDefaultName(fi,MDC_FRMT_ECAT7,fi->ofname,fi->ifname);
  }
  if (MDC_PROGRESS) MdcProgress(MDC_PROGRESS_BEGIN,0.,"Writing ECAT7:");
  if (MDC_VERBOSE) MdcPrntMesg("ECAT7 Writing <%s> ...",fi->ofname);

  /* check for colored files */
  if (fi->map == MDC_MAP_PRESENT)
    return("ECAT7 Colored files unsupported");

  if (MdcKeepFile(fi->ofname)) {
    return("ECAT7 File exists!!");
  }

  if (MDC_FORCE_INT != MDC_NO) {
    if (MDC_FORCE_INT != BIT16_S) {
      MdcPrntWarn("ECAT7 Only Int16 pixels supported");
    }
  }

  /* check some integrities */

  /* check integrity of planes, frames, gates, beds */
  if (fi->dim[3] > MDC_ECAT7_MAX_PLANES)
    return("ECAT7 number of planes too big (1024)");
  if (fi->dim[4] > MDC_ECAT7_MAX_FRAMES)
    return("ECAT7 number of frames too big (512)");
  if (fi->dim[5] > MDC_ECAT7_MAX_GATES)
    return("ECAT7 number of gates  too big (32)");
  if ((fi->dim[6]*fi->dim[7]) > MDC_ECAT7_MAX_BEDS)
    return("ECAT7 number of beds   too big (32)");

  /* use TPC library to open file */
  MdcFillMainHeader(fi,&mh);
  MdcConvertToTPCEcat7(fi,&mh,&h);
  fi->ofp=ecat7Create(fi->ofname, &h);
  if (fi->ofp == NULL) {
    return("ECAT7 Failed to open file for writing");
  }

  /* write all planes */
  frame_size = mh.num_planes*fi->mwidth*fi->mheight;
  TPC_frame_start = (float*)malloc(frame_size*sizeof(float));
  if (TPC_frame_start == NULL) {
    MdcCloseFile(fi->ofp);
    return("ECAT7 Failed to allocate frame buffer");
  }

  if ( ! ( fi->pat_slice_orient == MDC_TRANSAXIAL ||
           fi->pat_slice_orient == MDC_CORONAL    ||
           fi->pat_slice_orient == MDC_SAGITTAL   )) {
    MdcPrntWarn("ECAT7 Couldn't resolve slice orientation, using transaxial\n");
  }

  /* head feet direction L\P,L\FP,P\F,L\F,P\FR,R\F
  plane_dir = MDC_HEADFIRST;
  if      (strcmp(fi->pat_orient,"L\\P")  == 0) plane_dir = MDC_FEETFIRST;
  else if (strcmp(fi->pat_orient,"R\\P")  == 0) plane_dir = MDC_FEETFIRST;
  else if (strcmp(fi->pat_orient,"L\\FP") == 0) plane_dir = MDC_HEADFIRST;
  else if (strcmp(fi->pat_orient,"P\\F")  == 0) plane_dir = MDC_HEADFIRST;
  else if (strcmp(fi->pat_orient,"L\\F")  == 0) plane_dir = MDC_HEADFIRST;
  else if (strcmp(fi->pat_orient,"P\\FR") == 0) plane_dir = MDC_HEADFIRST;
  else if (strcmp(fi->pat_orient,"R\\F")  == 0) plane_dir = MDC_HEADFIRST;
  else {
    MdcPrntWarn("ECAT7 Unrecognized patient orientation: %s\n",fi->pat_orient);
  }*/

  for (bed=0; bed <= mh.num_bed_pos; bed++)
  for (gate=1; gate <= mh.num_gates; gate++)
  for (frame=1; frame <= h.num_frames; frame++) {

    TPC_frame = TPC_frame_start;

#if MDC_TPC_SITE
    for (plane = 0; plane < fi->dim[3]; plane++) {
#else
    for (plane = 0; plane < fi->dim[3]; plane++, img++) {
#endif
       if (MDC_PROGRESS)
         MdcProgress(MDC_PROGRESS_INCR,1./(float)fi->number,NULL);
       if (img < 0) {
         img = fi->number-1;
         MdcPrntWarn("ECAT7 underflow %d %d %d %d\n",
                      mh.num_bed_pos,mh.num_gates,h.num_frames,fi->dim[3]);
       }
       if (img >= fi->number) {
         img = fi->number-1;
         MdcPrntWarn("ECAT7 overflow %d %d %d %d\n",
                      mh.num_bed_pos,mh.num_gates,h.num_frames,fi->dim[3]);
       }

#if MDC_TPC_SITE
       img = (h.num_frames - frame)*fi->dim[3]+plane;
       id = &fi->image[img];
#else
       id = &fi->image[img];
#endif

       /* TPC requires float buffer */
       buf = MdcGetImgFLT32(fi, (Uint32)img);
       if (buf == NULL) {
         MdcFree(TPC_frame_start);
         return("ECAT7 Bad malloc float buf");
       }
       type= FLT32;

       if (fi->diff_size) {
         uwidth  = fi->mwidth; uheight = fi->mheight;
         maxbuf = MdcGetResizedImage(fi, buf, type, (Uint32)img);
         if (maxbuf == NULL) {
           MdcFree(buf);
           MdcFree(TPC_frame_start);
           return("ECAT7 Bad malloc maxbuf");
         }
         MdcFree(buf);
       }else{
         uwidth = id->width; uheight = id->height;
         maxbuf = buf;
       }

       bytes = MdcType2Bytes(type);

#if MDC_TPC_SITE
       /* copy plane to row-column-plane orientation as scaled float data */
       for (column=0; column < uwidth; column++)
       for (row=0; row < uheight*bytes; row+=bytes) {
          flt32 = *((float*)&maxbuf[column*uheight*bytes+row]);
          *(TPC_frame + ((plane)*uheight*uwidth
                      + column*uheight
                      + (row/bytes))) = flt32;
       }
#else
       /* copy plane at once */
       size  = uwidth * uheight;

       TPC_frame = TPC_frame_start + (size*plane);
       memcpy(TPC_frame,maxbuf,size*bytes);
#endif

       MdcFree(maxbuf);

    }

    TPC_frame = TPC_frame_start;
    matnum = mdc_mat_numcod(frame,1,1,0,0);
    MdcConvertToTPCEcat7image(fi,&ih,img-1,frame-1);
    ret = ecat7WriteImageMatrix(fi->ofp, matnum, &ih, TPC_frame);
    if (ret) {
      MdcFree(maxbuf);
      MdcFree(TPC_frame);
      MdcPrntWarn("ECAT7: Matrix write error code=%d\n",ret);
      return("ECAT7 Bad write image matrix");
    }

  }

  MdcFree(TPC_frame);

  MdcCloseFile(fi->ofp);

  MdcCheckQuantitation(fi);

  return(NULL);
}

#else

const char *MdcWriteECAT7(FILEINFO *fi)
{
  return("ECAT7 Writing not yet supported");
}

#endif
