/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * filename: m-dicm.c                                                      *
 *                                                                         *
 * UTIL C-source: Medical Image Conversion Utility                         *
 *                                                                         *
 * purpose      : Read DICOM files                                         *
 *                                                                         *
 * project      : (X)MedCon by Erik Nolf                                   *
 *                                                                         *
 * Functions    : MdcCheckDICM()       - Check DICOM format                *
 *                MdcReadDICM()        - Read  DICOM format                *
 *                MdcWriteDICM()       - Write DICOM format                *
 *                MdcCheckMosaic()     - Check  Mosaic file                *
 *                                                                         *
 * Notes        : Source needs VT-DICOM-package written by Tony Voet       *
 *                                                                         *
 * Credits      : - DICOM library      - Tony Voet                         *
 *                - Mosaic support     - Roland Marcus Rutschmann          *
 *                                                                         *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
 */

/*
   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 <time.h>
#ifdef LIBMDC_HAVE_STRING_H
#include <string.h>
#endif
#ifdef LIBMDC_HAVE_STRINGS_H
#ifndef _WIN32
#include <strings.h>
#endif
#endif

#include "dicom.h"
#include "medcon.h"

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

#define MDC_STR_UID_CREATOR   "777.777.0.0.0"

#define UNDEFINED_LENGTH      0xffffffff

#define MDC_DICM_FIX_TYPE     MDC_YES     /* fix wrong unsigned pixel type */

#define IROW 2
#define ICOL 1

/* extra stuff for reading DICOM */
static void MdcDicomInvert(IMG_DATA *id);
static char *MdcDicomHandleImages(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom
, IMAGE *image, Uint32 number);
static char *MdcHandleMosaic(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom
, IMAGE *image);
static void MdcPrintDicomInfoDB(FILEINFO *fi);

/* extra stuff for writing DICOM */
static Uint32 MdcDicomMakeUID(FILEINFO *fi, Int8 uid, char str[]);

static void MdcDicomWriteInfoSeq(FILE *fp, Uint16 group, Uint16 element);
static void MdcDicomWriteItem(FILE *fp);
static void MdcDicomWriteItemDelItem(FILE *fp);
static void MdcDicomWriteSeqDelItem(FILE *fp);

static char *MdcDicomWriteMetaHeader (FILEINFO *fi, MDC_DICOM_STUFF_T *dicom);
static char *MdcDicomWriteSetModality(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom);
static char *MdcDicomWriteG0008(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom);
static char *MdcDicomWriteG0010(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom);
static char *MdcDicomWriteG0018(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom);
static char *MdcDicomWriteG0020(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom);
static char *MdcDicomWriteG0028(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom);
static char *MdcDicomWriteG0054(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom);
static char *MdcDicomWriteG7FE0(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom);

/* including the addapted dicom lib functions */
static int  mdc_dicom_read(FILEINFO *fi, IMAGE **image, Uint32 *number);
static void mdc_dicom_dumpinfo(FILEINFO *fi);
static void mdc_dicom_printinfo(const ELEMENT *e,const char *description);
static void mdc_dicom_getinfo(FILEINFO *fi);

static void mdc_dicom_get_vr(ELEMENT *e);
static Uint8 *mdc_dicom_handle_vr(ELEMENT *e, Uint8 *tdata);
static int  mdc_dicom_write_element(FILE *fp, Uint16 group, Uint16 element, Uint32 length, Uint8 *data);

static int MDC_DICOM_VERBOSE = MDC_YES;

static off_t MDC_REWRF_SLOPE;         /* rewrite offset to slope     tag */
static off_t MDC_REWRF_INTERCEPT;     /* rewrite offset to intercept tag */

static Int32 mdc_prev_nr_series = -MDC_TYPE_UID_SERIES;
static Int32 mdc_prev_series_uid = 0;

static time_t mdc_sec, *mdc_psec=NULL; /* universal time */

static char mdc_dummy1[]="1";

static GATED_DATA *gd;
static ACQ_DATA   *acq;
static DYNAMIC_DATA *dd;

extern MDC_DICOM_STUFF_T mdc_dicom_stuff;

/****************************************************************************
                            F U N C T I O N S
****************************************************************************/

int MdcCheckDICM(FILEINFO *fi)
{
   char sig[5];

   fseeko(fi->ifp,(off_t)128,SEEK_SET);
   if (fread(sig,1,4,fi->ifp) != 4) return(MDC_BAD_READ);
   fseeko(fi->ifp,(off_t)0,SEEK_SET);

   sig[4]='\0';
   MdcLowStr(sig);

   if (strstr(sig,MDC_DICM_SIG) == NULL) return(MDC_FRMT_NONE);

   return(MDC_FRMT_DICM);
}

void MdcDicomInvert(IMG_DATA *id)
{
   double pixvalue;
   double max=0., min=0.;
   Uint8 *pixel;
   Uint32 i, n;

   n = id->width * id->height;
   /* retrieve present max/min values */
   for (pixel=id->buf, i=0; i<n; i++, pixel+=MdcType2Bytes(id->type)) {
      pixvalue = MdcGetDoublePixel(pixel,id->type);
      if (i==0) {
        max = pixvalue; min = pixvalue;
      }else{
        if ( pixvalue > max ) max = pixvalue;
        else if ( pixvalue < min ) min = pixvalue;
      }
   }

   /* invert pixel values */
   for (pixel=id->buf, i=0; i<n; i++, pixel+=MdcType2Bytes(id->type)) {
      pixvalue = MdcGetDoublePixel(pixel,id->type);
      pixvalue = max - pixvalue + min;
      MdcPutDoublePixel(pixel,pixvalue,id->type);
   }

}

char *MdcDicomHandleImages(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom, IMAGE *image, Uint32 number)
{
  IMAGE *pimg;
  IMG_DATA *id;
  Uint32 img=0, i, f, bytes, t;
  Int64 length; /* for large files > 4GB */
  Uint8 *pdata = NULL;

  for (img=0, i=0; i<number; i++) {
     pimg = &image[i];
     for (f=0; f<image[i].frames; f++) {

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

        id = &fi->image[img];
        id->width  = (Uint32)pimg->w;
        id->height = (Uint32)pimg->h;
        id->type   = fi->type;
        id->bits   = MdcType2Bits(id->type);

        if (id->type != COLRGB) {
          id->quant_scale = dicom->si_slope;
          id->intercept   = dicom->si_intercept;
        }

        bytes = id->width*id->height*MdcType2Bytes(id->type);
        id->buf = MdcGetImgBuffer(bytes);
        if (id->buf == NULL) return("DICM Couldn't allocate image buffer");

        if (fi->type == COLRGB) {
          pdata = (Uint8 *)pimg->data.rgb;
        }else{
          pdata = (Uint8 *)pimg->data.gray;
        }

        /* move data pointer & copy ; cast to Int64 for large file > 4GB     */
        /* we settle on Int64 because core library uses decreasing for-loops */
        length = (Int64)bytes * f;

        pdata+=length;

        memcpy(id->buf,pdata,bytes);

        if (!((img == 0) && (f == 0))) {
          /* copy voxel size and orient values from the first image */
          id->pixel_xsize = fi->image[0].pixel_xsize;
          id->pixel_ysize = fi->image[0].pixel_ysize;
          id->slice_width = fi->image[0].slice_width;
          id->slice_spacing = fi->image[0].slice_spacing;
        }

        /* image orient/position according to patient coordinate system */
        if (id->image_orient_pat[0]==0.0 && id->image_orient_pat[1]==0.0 &&
            id->image_orient_pat[4]==0.0 && id->image_orient_pat[5]==0.0 ) {
            /* no patient coordinate system defines, try pat_orient */
          if ((img == 0) && (f == 0))
            fi->pat_slice_orient = MdcTryPatSliceOrient(fi->pat_orient);
          if (fi->pat_slice_orient != MDC_UNKNOWN) {
            MdcFillImgPos(fi,img,fi->dim[3]==0 ? 0 : img%fi->dim[3],0.0);
            MdcFillImgOrient(fi,img);
          }
        }

        /* image orient/position according to device (RETIRED)*/
        if (id->image_orient_dev[0]==0.0 && id->image_orient_dev[1]==0.0 &&
            id->image_orient_dev[4]==0.0 && id->image_orient_dev[5]==0.0 ) {
            /* no patient coordinate system defines */
          switch (fi->pat_slice_orient) {
            case MDC_SUPINE_HEADFIRST_TRANSAXIAL:
            case MDC_SUPINE_HEADFIRST_SAGITTAL   :
            case MDC_SUPINE_HEADFIRST_CORONAL    : /* same coordinate system */
                for (t=0; t<6; t++)
                   id->image_orient_dev[t] = id->image_orient_pat[t];
                break;
          }
        }

        if (id->image_pos_dev[0] == 0.0 && id->image_pos_dev[1] == 0.0 &&
            id->image_pos_dev[2] == 0.0 ) {

          switch (fi->pat_slice_orient) {
            case MDC_SUPINE_HEADFIRST_TRANSAXIAL:
            case MDC_SUPINE_HEADFIRST_SAGITTAL   :
            case MDC_SUPINE_HEADFIRST_CORONAL    : /* same coordinate system */
                for (t=0; t<3; t++)
                   id->image_pos_dev[t] = id->image_pos_pat[t];
                break;
          }
        }

        if (id->type != COLRGB) {

          if (dicom->INVERT == MDC_YES) MdcDicomInvert(id);

        }

        img+=1;
     }
  }

  return(NULL);

}

int MdcCheckMosaic(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom)
{

  /* support enabled ? */
  if (MDC_DICOM_MOSAIC_ENABLED == MDC_NO) return(MDC_NO);

  if (dicom->MOSAIC == MDC_NO) return(MDC_NO);

  if (MDC_DICOM_MOSAIC_FORCED == MDC_YES) {
    dicom->mosaic_width = mdc_mosaic_width;
    dicom->mosaic_height= mdc_mosaic_height;
    dicom->mosaic_number= mdc_mosaic_number;
    dicom->mosaic_interlaced = mdc_mosaic_interlaced;
  }

  /* do some sanity checks before handling as MOSAIC */
  if ( fi->number == 1 && dicom->mosaic_number > 0 &&
    dicom->mosaic_width > 0  && dicom->mosaic_height > 0 &&
    fi->mwidth > dicom->mosaic_width &&
    fi->mheight > dicom->mosaic_height &&
    fi->mwidth  % dicom->mosaic_width  == 0 &&
    fi->mheight % dicom->mosaic_height == 0 ) {

    return(MDC_YES);

  }

  return(MDC_NO);

}

char *MdcHandleMosaic(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom, IMAGE *image)
{
  IMG_DATA *id=NULL;
  IMAGE *pimg = &image[0];
  Uint32 width, height, number, bytes, size;
  Uint32 bytes_per_mosaic, bytes_per_old_img_line, bytes_per_y_plane;
  Uint32 nr_of_pics_per_line, sl, s, x, y, offset, yline, i;
  Uint8 *pmosaic, *p, *pdata;
  Int8 DO_VOXEL_FIX=MDC_NO;
  float f;

  /* in case we need to know */
  MdcDebugPrint("handling image as MOSAIC");

  /* allocate new block of memory */
  width = dicom->mosaic_width;  height = dicom->mosaic_height;
  number= dicom->mosaic_number; bytes  = MdcType2Bytes(fi->type);
  size = bytes * width * height;
  pmosaic = malloc(size * number);
  if (pmosaic == NULL) return("DICM Bad malloc pmosaic buffer");

  /* extract the stamps */
  bytes_per_mosaic = bytes * width;
  bytes_per_old_img_line = bytes * fi->mwidth;
  nr_of_pics_per_line = bytes_per_old_img_line / bytes_per_mosaic;
  bytes_per_y_plane = height * bytes_per_old_img_line;
  for (sl=0; sl<number; sl++) {
     if (dicom->mosaic_interlaced == MDC_YES) {
       if (sl%2 == 0) {
         s = sl/2;
       }else{
         s = number/2 + (sl-1)/2;
       }
     }else{
       s=sl;
     }
     x = s % nr_of_pics_per_line;
     y = s / nr_of_pics_per_line;
     offset = (y * bytes_per_y_plane) + (x * bytes_per_mosaic);
     for (yline=0; yline < height; yline++) {
        p = pmosaic + sl*size + yline*bytes_per_mosaic;
        pdata = (Uint8 *)pimg->data.gray + offset;
        memcpy(p,pdata,bytes_per_mosaic);
        offset += bytes_per_old_img_line;
     }
  }

  /* fake multi frame image */
  MdcFree(pimg->data.gray); pimg->data.gray= (Uint16 *)pmosaic;
  image[0].frames = number; pimg->w = width; pimg->h = height;

  /* set FILEINFO appropriate */
  fi->dim[3] = number; fi->mwidth = width; fi->mheight = height;
  if (!MdcGetStructID(fi,number)) {
    MdcFree(pmosaic);
    return("DICM Bad malloc IMG_DATA structs for mosaic");
  }

  /* fake DYNAMIC_DATA later */
  dicom->frameduration = 1.; /* no idea what tag to use */

  /* set IMG_DATA appropriate */
  id = &fi->image[0];
  id->width = width; id->height = height;

  if (MDC_DICOM_MOSAIC_FORCED    == MDC_YES) {
    /* mosaic forced: voxel fixing only when requested */
    if (MDC_DICOM_MOSAIC_FIX_VOXEL == MDC_YES) {
      DO_VOXEL_FIX = MDC_YES;
    }else{
      DO_VOXEL_FIX = MDC_NO;
    }
  }else{
    /* mosaic autodetect: always do voxel fixing */
    DO_VOXEL_FIX = MDC_YES;
  }
  if (DO_VOXEL_FIX == MDC_YES) {
    /* handle mosaic calculation of pixel size                                */
    /* mosaic calculates pixsizefact by field of view/nr_of_all_pix not taking*/
    /* into account that there are severall slices in the whole picture.      */
    /* eg 200x200mm˛ fov 8x8 pix each having 64x64 pix. The whole matrix has  */
    /* 512x512 pix so pixsize is calculated by 200/512 instead of 200/64      */

    /* NOTE: apparently valid for MAGNETOM dialect, not valid for SONATA      */

    id->pixel_xsize *= (float)nr_of_pics_per_line;
    id->pixel_ysize *= (float)nr_of_pics_per_line;
    /* set globally as well */
    fi->pixdim[1] = id->pixel_xsize;
    fi->pixdim[2] = id->pixel_ysize;
  }

  /* make orthogonal */
  for (i=0; i<6; i++) {
     f = id->image_orient_dev[i];
     id->image_orient_dev[i]=(float)MdcGetOrthogonalInt(f);
  }
  for (i=0; i<6; i++) {
     f = id->image_orient_pat[i];
     id->image_orient_pat[i]=(float)MdcGetOrthogonalInt(f);
  }
  fi->pat_slice_orient = MdcGetPatSliceOrient(fi,0);
  MdcFillImgPos(fi,0,0,0);

  return(NULL);
}

static void MdcPrintDicomInfoDB(FILEINFO *fi)
{
  /* make string */
  sprintf(mdcbufr,"%s+%04d%02d%02d+%02d%02d%02d",fi->patient_name
                                                ,fi->study_date_year
                                                ,fi->study_date_month
                                                ,fi->study_date_day
                                                ,fi->study_time_hour
                                                ,fi->study_time_minute
                                                ,fi->study_time_second);
  /* print string */
  MdcPrntScrn("%s\n",mdcbufr);

}

const char *MdcReadDICM(FILEINFO *fi)
{
  IMAGE *image=NULL, *pimg=NULL;
  IMG_DATA *id=NULL;
  MDC_DICOM_STUFF_T *dicom=&mdc_dicom_stuff;
  Uint32 i, t, number=0, nrimages=0;
  int COLOR=MDC_NO;
  const char *msg=NULL;

  MDC_FILE_ENDIAN = MDC_HOST_ENDIAN;

  /* init dicom struct */
  MdcDicomInitStuff(dicom);

  /* init  MOD structs */
  MdcGetStructMOD(fi);

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

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

  if ((MDC_ECHO_ALIAS == MDC_YES) || (MDC_INFO_DB == MDC_YES)) {
    /* get one struct to fill */
    MdcGetStructID(fi,1);
    /* retrieve info from file */
    mdc_dicom_getinfo(fi);
    /* echo alias */
    if (MDC_ECHO_ALIAS == MDC_YES) MdcEchoAliasName(fi);
    /* print db info */
    if (MDC_INFO_DB == MDC_YES) MdcPrintDicomInfoDB(fi);
    /* leave */
    return(NULL);
  }

  MdcMergePath(fi->ipath,fi->idir,fi->ifname);

  /* first pass: limit log level to errors */
  if (MDC_BLOCK_MESSAGES == MDC_LEVEL_ALL) {
    dicom_log_level = EMERGENCY;
  }else{
    dicom_log_level = ERROR;
  }

  /* reading file 1st time for info printout */
  if (MDC_INFO) {
    MdcPrintLine('*',MDC_HALF_LENGTH);
    MdcPrntScrn("Pass #1: through DICOM reader\n");
    MdcPrintLine('*',MDC_HALF_LENGTH);
    mdc_dicom_dumpinfo(fi);
  }

  if (MDC_PROGRESS) MdcProgress(MDC_PROGRESS_INCR,0.1,NULL);

  /* second pass: allow warnings */
  if (MDC_BLOCK_MESSAGES == MDC_NO) dicom_log_level = NOTICE;

  /* reading file 2nd time for images */
  if (mdc_dicom_read(fi,&image,&nrimages)) {
    MdcSplitPath(fi->ipath,fi->idir,fi->ifname);
    dicom_free(image, nrimages);
    return("DICM Error reading file");
  }

  if (MDC_PROGRESS) MdcProgress(MDC_PROGRESS_INCR,0.1,NULL);

  MdcSplitPath(fi->ipath,fi->idir,fi->ifname);

  /* number of images & flag color */
  for (number=0, i=0; i<nrimages; i++) {
     pimg = &image[i];
     number += pimg->frames;
     if (number  == 0 ) {
       dicom_free(image, nrimages);
       return("DICM Bad number of images");
     }
     if (pimg->rgb) COLOR = MDC_YES; /* MARK: all images must be identical */
  }


  if (!MdcGetStructID(fi,number)) {
    dicom_free(image, nrimages);
    return("DICM Bad malloc IMG_DATA structs");
  }

  /* third pass: limit log level back to errors */
  if (MDC_BLOCK_MESSAGES == MDC_NO) dicom_log_level = ERROR;

  /* reading file 3rd time for info retrieving (through Acr/Nema reader) */
  if (MDC_INFO) {
    MdcPrntScrn("\n\n");
    MdcPrintLine('*',MDC_HALF_LENGTH);
    MdcPrntScrn("Pass #2: through Acr/Nema reader\n");
    MdcPrintLine('*',MDC_HALF_LENGTH);
  }

  MdcMergePath(fi->ipath,fi->idir,fi->ifname);
  mdc_dicom_getinfo(fi);
  MdcSplitPath(fi->ipath,fi->idir,fi->ifname);

  /* init FILEINFO parameters */
  if (COLOR == MDC_YES) {
    fi->map = MDC_MAP_PRESENT;
    fi->type= COLRGB;
  }else{
    fi->map = MDC_MAP_GRAY;
    fi->type= (dicom->sign == 1) ? BIT16_S : BIT16_U;
  }
  fi->bits = MdcType2Bits(fi->type);
  fi->endian = MDC_HOST_ENDIAN;
  fi->dim[0] = 3;
  fi->pixdim[0] = 0.;

  /* fix for single image PT modality */
  if (dicom->modality == M_PT) {
    fi->number = 1;
    for (t=3; t < MDC_MAX_DIMS; t++) fi->dim[t] = 1;
  }

  /* in case of mosaic, fake a multi frame image */
  if (MdcCheckMosaic(fi,dicom) == MDC_YES) {
    msg = MdcHandleMosaic(fi,dicom,image);
    if (msg != NULL) { dicom_free(image, nrimages); return(msg); }
  }

  /* fill in the FILEINFO structs */
  for (t=(MDC_MAX_DIMS - 1); t > 3; t--) if (fi->dim[t] > 1) break;
  fi->dim[0] = t; fi->pixdim[0] = t;
  fi->pixdim[1] = fi->image[0].pixel_xsize;
  fi->pixdim[2] = fi->image[0].pixel_ysize;
  fi->pixdim[3] = fi->image[0].slice_width;
  id = &fi->image[0];
  if (MDC_TRUE_GAP == MDC_YES) id->slice_spacing += id->slice_width;

  /* fill in DYNAMIC_DATA structs */
  if (fi->acquisition_type == MDC_ACQUISITION_TOMO ||
      fi->acquisition_type == MDC_ACQUISITION_DYNAMIC) {
    if (dicom->frameduration > 0.) {
      if ((fi->dynnr > 0) && (fi->dyndata != NULL)) {
        /* preserve but do set frame_duration */
        for (i=0; i < fi->dynnr; i++) {
           dd = &fi->dyndata[i];
           if (fi->planar == MDC_YES) {
             /* planar: sum of all images + delays */
             dd->time_frame_duration *= dd->nr_of_slices;
             dd->time_frame_duration += dd->delay_slices * (dd->nr_of_slices-1);
           }else{
             dd->time_frame_start    = dicom->framestart;
             dd->time_frame_duration = dicom->frameduration;
           }
        }
      }else{
        if (!MdcGetStructDD(fi,1)) {
          dicom_free(image, nrimages);
          return("DICM Couldn't malloc DYNAMIC_DATA structs");
        }
        fi->dyndata[0].nr_of_slices = fi->number;
        fi->dyndata[0].time_frame_start    = dicom->framestart;
        fi->dyndata[0].time_frame_duration = dicom->frameduration;
      }
    }
  }

  /* fill in GATED_DATA structs */
  if (fi->gatednr > 0 && fi->gdata != NULL) {
    gd = &fi->gdata[0];

    gd->nr_projections  = dicom->nrframes;
    gd->extent_rotation = dicom->scan_arc;
    gd->image_duration  = dicom->frametime;
    gd->time_per_proj   = dicom->frameduration;
    gd->study_duration  = dicom->nrframes * dicom->frameduration;

    gd->cycles_acquired = dicom->intervals_acquired;
    gd->cycles_observed = dicom->intervals_acquired + dicom->intervals_rejected;

    gd->window_low = dicom->window_low;
    gd->window_high= dicom->window_high;
  }


  /* put images and info in IMG_DATA structs */
  msg = MdcDicomHandleImages(fi,dicom,image,nrimages);
  if (msg != NULL) { dicom_free(image, nrimages); return(msg); }

  dicom_free(image, nrimages);

  MdcCloseFile(fi->ifp);

  return(NULL);

}

Uint32 MdcDicomMakeUID(FILEINFO *fi, Int8 uid, char str[])
{
  Int16 year, month, day;
  Int16 hour, minute, second;
  Uint32 utc, len, study_uid, series_uid, instance_uid;

  year  = fi->study_date_year;
  month = fi->study_date_month;
  day   = fi->study_date_day;
  hour  = fi->study_time_hour;
  minute= fi->study_time_minute;
  second= fi->study_time_second;

  if (mdc_psec != NULL) {
    utc = (Uint32)*mdc_psec;
  }else{
    utc = 777UL;
  }

  /* study_uid = hash(patient_name + study_date + study_time) */
  sprintf(str,"%s%s%hd%02hd%02hd%02hd%02hd%02hd"
             ,fi->patient_name,fi->patient_id
             ,year,month,day,hour,minute,second);
  study_uid = MdcHashSDBM((unsigned char *)str);
  if (study_uid == 182208422) { /* Unknown000000000 case */
    sprintf(str,"%u",utc);
    study_uid = MdcHashSDBM((unsigned char *)str);
  }

  /* series_uid = hash(input filename)*/
  if (mdc_prev_nr_series == -MDC_TYPE_UID_SERIES) {
    mdc_prev_nr_series = fi->nr_series;
    series_uid = MdcHashSDBM((unsigned char *)fi->ifname);
    mdc_prev_series_uid = series_uid;
  }

  /* new series_uid for each input file */
  if ((fi->nr_series != mdc_prev_nr_series) || (fi->nr_series <= 0)) {
    mdc_prev_nr_series = fi->nr_series;
    series_uid = MdcHashSDBM((unsigned char*)fi->ifname);
    mdc_prev_series_uid = series_uid;
  }else{
    series_uid = mdc_prev_series_uid;
  }

  /* new instance_uid for each output file */
  instance_uid = MdcHashSDBM((unsigned char *)fi->ofname);

  switch (uid) {
    case MDC_TYPE_UID_CREATOR:
        sprintf(str,"%s",MDC_STR_UID_CREATOR);
        break;
    case MDC_TYPE_UID_SOP_INSTANCE:
    case MDC_TYPE_UID_MEDIA_INSTANCE:
        sprintf(str,"%s.%u.%u.%u.%u"
                   ,MDC_STR_UID_CREATOR,utc
                   ,study_uid,series_uid,instance_uid);
        break;
    case MDC_TYPE_UID_FRAME:
    case MDC_TYPE_UID_STUDY:
        sprintf(str,"%s.%u.%u"
                   ,MDC_STR_UID_CREATOR,utc
                   ,study_uid);
        break;
    case MDC_TYPE_UID_SERIES:
        sprintf(str,"%s.%u.%u.%u"
                   ,MDC_STR_UID_CREATOR,utc
                   ,study_uid,series_uid);
        break;
    default:
        sprintf(str,"%s.%u.%u"
                   ,MDC_STR_UID_CREATOR,utc
                   ,study_uid);
  }

  len = (Uint32)strlen(str);

  if (len > MDC_UID_MAXSTR) {
    MdcPrntWarn("DICM Inappropriate UID length");
  }

  return(len);
}

void MdcDicomWriteInfoSeq(FILE *fp, Uint16 group, Uint16 element)
{
  mdc_dicom_write_element(fp,group,element,UNDEFINED_LENGTH,NULL);
}
void MdcDicomWriteItem(FILE *fp)
{
  mdc_dicom_write_element(fp,0xfffe,0xe000,UNDEFINED_LENGTH,NULL);
}
void MdcDicomWriteItemDelItem(FILE *fp)
{
  mdc_dicom_write_element(fp,0xfffe,0xe00d,0,NULL);
}
void MdcDicomWriteSeqDelItem(FILE *fp)
{
  mdc_dicom_write_element(fp,0xfffe,0xe0dd,0,NULL);
}


char *MdcDicomWriteMetaHeader(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom)
{
  Uint32 len;
  off_t REWRF, BEGIN, END;
  Int32 i32;
  FILE *ofp = fi->ofp;

  /* write the empty preamable */
  memset(mdcbufr,0,128);  fwrite(mdcbufr,1,128,ofp);

  /* write the signature */
  strcpy(mdcbufr,"DICM"); fwrite(mdcbufr,1,4,ofp);

  /* group 0x0002 */
  REWRF = ftello(ofp); i32=0;
  mdc_dicom_write_element(ofp,0x0002,0x0000,4,(Uint8 *)&i32);

  BEGIN = ftello(ofp);

  mdcbufr[0]=0x00; mdcbufr[1]=0x01;
  mdc_dicom_write_element(ofp,0x0002,0x0001,2,(Uint8 *)mdcbufr);

  switch (dicom->modality) {
    case M_PT:
        strcpy(mdcbufr,"1.2.840.10008.5.1.4.1.1.128"); len = strlen(mdcbufr);
        mdc_dicom_write_element(fi->ofp,0x0002,0x0002,len,(Uint8 *)mdcbufr);
        break;
    default  : /* default to NM modality */
        strcpy(mdcbufr,"1.2.840.10008.5.1.4.1.1.20"); len = strlen(mdcbufr);
        mdc_dicom_write_element(fi->ofp,0x0002,0x0002,len,(Uint8 *)mdcbufr);
  }

  /* strcpy(mdcbufr,"1.2.840.10008.Media.Storage.SOP.Instance"); */
  len = MdcDicomMakeUID(fi,MDC_TYPE_UID_MEDIA_INSTANCE,mdcbufr);
  mdc_dicom_write_element(ofp,0x0002,0x0003,len,(Uint8 *)mdcbufr);

  /* transfer syntax */
  if (dicom->ENCAPSULATED_UNCOMPRESSED == MDC_YES) {
    strcpy(mdcbufr,"1.2.840.10008.1.2.1.98");  /* encapsulated uncompressed explicit VR little endian */
  }else if (MDC_DICOM_WRITE_IMPLICIT == MDC_YES) {
    strcpy(mdcbufr,"1.2.840.10008.1.2");       /* implicit VR little */
  }else{
    if (MDC_FILE_ENDIAN == MDC_LITTLE_ENDIAN) {
      strcpy(mdcbufr,"1.2.840.10008.1.2.1");   /* explicit VR little */
    }else{
      strcpy(mdcbufr,"1.2.840.10008.1.2.2");   /* explicit VR big    */
    }
  }
  len = strlen(mdcbufr);
  mdc_dicom_write_element(ofp,0x0002,0x0010,len,(Uint8 *)mdcbufr);

  strcpy(mdcbufr,"0.0.0.0"); len = strlen(mdcbufr);
  mdc_dicom_write_element(ofp,0x0002,0x0012,len,(Uint8 *)mdcbufr);

  strcpy(mdcbufr,"NOTSPECIFIED"); len = strlen(mdcbufr);
  mdc_dicom_write_element(ofp,0x0002,0x0013,len,(Uint8 *)mdcbufr);

  strcpy(mdcbufr,"NOTSPECIFIED"); len = strlen(mdcbufr);
  mdc_dicom_write_element(ofp,0x0002,0x0016,len,(Uint8 *)mdcbufr);

  END = ftello(ofp);

  /* rewrite group length */
  fseeko(ofp,REWRF,SEEK_SET); i32 = (Int32)(END - BEGIN);
  mdc_dicom_write_element(ofp,0x0002,0x0000,4,(Uint8 *)&i32);
  fseeko(ofp,(off_t)0,SEEK_END);

  if (ferror(ofp)) return("DICM Failure to write MetaHeader");

  return(NULL);

}
/*
char *MdcWritePatientModule(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom)
{

  return(NULL);
}

char *MdcWriteGeneralStudyModule(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom)
{

  return(NULL);
}

char *MdcWriteGeneralSeriesModule(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom)
{

  return(NULL);
}

char *MdcWriteNMSeriesModule(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom)
{

  return(NULL);
}

char *MdcWriteGeneralEquipmentModule(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom)
{

  return(NULL);
}

char *MdcWriteSOPCommonModule(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom)
{

  return(NULL);
}

char *MdcWriteGeneralImageModule(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom)
{

  return(NULL);
}

char *MdcWriteImagePixelModule(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom)
{

  return(NULL);
}

char *MdcWriteMultiFrameModule(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom)
{

  return(NULL);
}

char *MdcWriteNMImageModule(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom)
{

  return(NULL);
}

char *MdcWriteNMImagePixelModule(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom)
{

  return(NULL);
}

char *MdcWriteNMMultiFrameImageModule(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom)
{

  return(NULL);
}

char *MdcWriteNMIsotopeImageModule(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom)
{

  return(NULL);
}

char *MdcWriteNMDetectorImageModule(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom)
{

  return(NULL);
}
*/

char *MdcDicomWriteSetModality(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom)
{
  /* currently only NM writing supported */
  switch (fi->modality) {
    case M_PT:

    default  : dicom->modality = M_NM;
  }

  return(NULL);

}

char *MdcDicomWriteG0008(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom)
{
  Uint32 bytes, len;
  char *pdata;

  /* group 0x0008 */
  strcpy(mdcbufr,"DERIVED\\PRIMARY");
  switch (dicom->modality) {
    case M_PT: break;
    default  : /* default to NM modality */
        switch (fi->acquisition_type) {
          case MDC_ACQUISITION_TOMO:
              if (fi->reconstructed == MDC_YES)
                strcat(mdcbufr,"\\RECON TOMO");
              else
                strcat(mdcbufr,"\\TOMO");
              break;
          case MDC_ACQUISITION_DYNAMIC:
              strcat(mdcbufr,"\\DYNAMIC");
              break;
          case MDC_ACQUISITION_GATED:
              strcat(mdcbufr,"\\GATED");
              break;
          case MDC_ACQUISITION_GSPECT:
              if (fi->reconstructed == MDC_YES)
                strcat(mdcbufr,"\\RECON GATED TOMO");
              else
                strcat(mdcbufr,"\\GATED TOMO");
              break;
          case MDC_ACQUISITION_UNKNOWN: /* fake as static */
          case MDC_ACQUISITION_STATIC:
              strcat(mdcbufr,"\\STATIC");
              break;
          default: strcat(mdcbufr,"\\UNSPECIFIED");
        }
        strcat(mdcbufr,"\\EMISSION"); /* MARK: no flag for transmission yet */
  }
  len = strlen(mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0008,0x0008,len,(Uint8 *)mdcbufr);

  strftime(mdcbufr,35,"%Y%m%d",localtime(mdc_psec)); len = strlen(mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0008,0x0012,len,(Uint8 *)mdcbufr);

  strftime(mdcbufr,35,"%H%M%S",localtime(mdc_psec)); len = strlen(mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0008,0x0013,len,(Uint8 *)mdcbufr);

  len = MdcDicomMakeUID(fi,MDC_TYPE_UID_CREATOR,mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0008,0x0014,len,(Uint8 *)mdcbufr);

  switch (dicom->modality) {
    case M_PT:
        strcpy(mdcbufr,"1.2.840.10008.5.1.4.1.1.128"); len = strlen(mdcbufr);
        mdc_dicom_write_element(fi->ofp,0x0008,0x0016,len,(Uint8 *)mdcbufr);
        break;
    default  : /* default to NM modality */
        strcpy(mdcbufr,"1.2.840.10008.5.1.4.1.1.20"); len = strlen(mdcbufr);
        mdc_dicom_write_element(fi->ofp,0x0008,0x0016,len,(Uint8 *)mdcbufr);
  }

  len = MdcDicomMakeUID(fi,MDC_TYPE_UID_SOP_INSTANCE,mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0008,0x0018,len,(Uint8 *)mdcbufr);

  /* date settings, make sure it is conform */
  if (fi->mod != NULL) {
    pdata = fi->mod->gn_info.study_date;       len = strlen(pdata);
    mdc_dicom_write_element(fi->ofp,0x0008,0x0020,len,(Uint8 *)pdata);
    pdata = fi->mod->gn_info.series_date;      len = strlen(pdata);
    mdc_dicom_write_element(fi->ofp,0x0008,0x0021,len,(Uint8 *)pdata);
    pdata = fi->mod->gn_info.acquisition_date; len = strlen(pdata);
    mdc_dicom_write_element(fi->ofp,0x0008,0x0022,len,(Uint8 *)pdata);
    pdata = fi->mod->gn_info.image_date;       len = strlen(pdata);
    mdc_dicom_write_element(fi->ofp,0x0008,0x0023,len,(Uint8 *)pdata);
  }else{
    if (fi->study_date_year == 0) {
      pdata = NULL; bytes=0;
    }else{
      sprintf(mdcbufr,"%04d%02d%02d",fi->study_date_year
                                    ,fi->study_date_month
                                    ,fi->study_date_day);
      pdata = mdcbufr; bytes = strlen(mdcbufr);
    }
    mdc_dicom_write_element(fi->ofp,0x0008,0x0020,bytes,(Uint8 *)pdata);
    mdc_dicom_write_element(fi->ofp,0x0008,0x0021,bytes,(Uint8 *)pdata);
    mdc_dicom_write_element(fi->ofp,0x0008,0x0022,bytes,(Uint8 *)pdata);
    mdc_dicom_write_element(fi->ofp,0x0008,0x0023,bytes,(Uint8 *)pdata);
  }
  /* time settings, can be full of zero's ... */
  if (fi->mod != NULL) {
    pdata = fi->mod->gn_info.study_time;       len = strlen(pdata);
    mdc_dicom_write_element(fi->ofp,0x0008,0x0030,len,(Uint8 *)pdata);
    pdata = fi->mod->gn_info.series_time;      len = strlen(pdata);
    mdc_dicom_write_element(fi->ofp,0x0008,0x0031,len,(Uint8 *)pdata);
    pdata = fi->mod->gn_info.acquisition_time; len = strlen(pdata);
    mdc_dicom_write_element(fi->ofp,0x0008,0x0032,len,(Uint8 *)pdata);
    pdata = fi->mod->gn_info.image_time;       len = strlen(pdata);
    mdc_dicom_write_element(fi->ofp,0x0008,0x0033,len,(Uint8 *)pdata);
  }else{
    sprintf(mdcbufr,"%02d%02d%02d",fi->study_time_hour
                                  ,fi->study_time_minute
                                  ,fi->study_time_second);
    len = strlen(mdcbufr);
    mdc_dicom_write_element(fi->ofp,0x0008,0x0030,len,(Uint8 *)mdcbufr);
    mdc_dicom_write_element(fi->ofp,0x0008,0x0031,len,(Uint8 *)mdcbufr);
    if (fi->image[0].sdata != NULL) {
      sprintf(mdcbufr,"%02d%02d%02d",fi->image[0].sdata->start_time_hour
                                    ,fi->image[0].sdata->start_time_minute
                                    ,fi->image[0].sdata->start_time_second);
      len = strlen(mdcbufr);
      mdc_dicom_write_element(fi->ofp,0x0008,0x0032,len,(Uint8 *)mdcbufr);
    }else{
      mdc_dicom_write_element(fi->ofp,0x0008,0x0032,len,(Uint8 *)mdcbufr);
    }
    sprintf(mdcbufr,"%02d%02d%02d",fi->study_time_hour
                                  ,fi->study_time_minute
                                  ,fi->study_time_second);
    len = strlen(mdcbufr);
    mdc_dicom_write_element(fi->ofp,0x0008,0x0033,len,(Uint8 *)mdcbufr);
  }

  len = strlen(mdc_dummy1);
  mdc_dicom_write_element(fi->ofp,0x0008,0x0050,len,(Uint8 *)mdc_dummy1);

  pdata = MdcGetStrModality((signed)dicom->modality); /* already in mdcbufr */
  len = strlen(pdata);
  mdc_dicom_write_element(fi->ofp,0x0008,0x0060,len,(Uint8 *)pdata);

  strcpy(mdcbufr,fi->manufacturer); len = strlen(mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0008,0x0070,len,(Uint8 *)mdcbufr);

  strcpy(mdcbufr,fi->institution); len = strlen(mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0008,0x0080,len,(Uint8 *)mdcbufr);

  strcpy(mdcbufr,"Unknown^^^^"); len = strlen(mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0008,0x0090,len,(Uint8 *)mdcbufr);

  strcpy(mdcbufr,fi->study_descr); len = strlen(mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0008,0x1030,len,(Uint8 *)mdcbufr);

  strcpy(mdcbufr,fi->series_descr); len = strlen(mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0008,0x103E,len,(Uint8 *)mdcbufr);

  strcpy(mdcbufr,fi->operator_name); len = strlen(mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0008,0x1070,len,(Uint8 *)mdcbufr);

  strcpy(mdcbufr,MDC_LIBVERS); len = strlen(mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0008,0x2111,len,(Uint8 *)mdcbufr);

  return(NULL);
}

char *MdcDicomWriteG0010(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom)
{
  Uint32 len;
  char *pdata;

  /* group 0x0010 */
  sprintf(mdcbufr,"%.64s^^^^",fi->patient_name); len = strlen(mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0010,0x0010,len,(Uint8 *)mdcbufr);

  pdata = fi->patient_id; len = strlen(pdata);
  mdc_dicom_write_element(fi->ofp,0x0010,0x0020,len,(Uint8 *)pdata);

  pdata = fi->patient_dob; len = strlen(pdata);
  if (len > 0 && pdata[0] != '0' ) {
    mdc_dicom_write_element(fi->ofp,0x0010,0x0030,len,(Uint8 *)pdata);
  }else{
    mdc_dicom_write_element(fi->ofp,0x0010,0x0030,0,NULL);
  }
  mdc_dicom_write_element(fi->ofp,0x0010,0x0032,0,NULL); /* Pat Birth Time */

  strcpy(mdcbufr,fi->patient_sex); MdcLowStr(mdcbufr);
  if (strchr(mdcbufr,'f') != NULL) {
    /* first check for fe-male */
    strcpy(mdcbufr,"F");
  }else if (strchr(mdcbufr,'m') != NULL) {
    /* now   check for    male */
    strcpy(mdcbufr,"M");
  }else {
    /* guess what? */
    strcpy(mdcbufr,"O");
  }
  len = strlen(mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0010,0x0040,len,(Uint8 *)mdcbufr);

  sprintf(mdcbufr,"%.2f",fi->patient_height); len = strlen(mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0010,0x1020,len,(Uint8 *)mdcbufr);

  sprintf(mdcbufr,"%.2f",fi->patient_weight); len = strlen(mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0010,0x1030,len,(Uint8 *)mdcbufr);

  return(NULL);
}

char *MdcDicomWriteG0018(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom)
{
  Uint32 len;
  Uint16 ui16;
  char *pdata;

  /* 0x0018 */
  strcpy(mdcbufr,fi->organ_code); len = strlen(mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0018,0x0015,len,(Uint8 *)mdcbufr);

  sprintf(mdcbufr,"%+e",fi->image[0].slice_width); len = strlen(mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0018,0x0050,len,(Uint8 *)mdcbufr);

  strcpy(mdcbufr,"0"); len = strlen(mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0018,0x0070,len,(Uint8 *)mdcbufr);

  sprintf(mdcbufr,"%+e",fi->image[0].slice_spacing); len = strlen(mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0018,0x0088,len,(Uint8 *)mdcbufr);

  if (fi->gatednr > 0 && fi->gdata != NULL) {
    /* heart beat */
    ui16 = (Uint16)MdcGetHeartRate(gd,MDC_HEART_RATE_OBSERVED);
    sprintf(mdcbufr,"%u",ui16); len = strlen(mdcbufr);
    mdc_dicom_write_element(fi->ofp,0x0018,0x1088,len,(Uint8 *)mdcbufr);
  }

  switch (dicom->modality) {
    case M_PT: break;
    default  : /* default to NM modality */
        if ((fi->acquisition_type == MDC_ACQUISITION_UNKNOWN) ||
            (fi->acquisition_type == MDC_ACQUISITION_STATIC)) {
          /* normally whole body too ...*/
          if (fi->image[0].sdata != NULL) {
            sprintf(mdcbufr,"%-12.0f",fi->image[0].sdata->image_duration);
            len = strlen(mdcbufr);
            mdc_dicom_write_element(fi->ofp,0x0018,0x1242,len,(Uint8 *)mdcbufr);
          }
        }
  }

  if (strcmp(fi->pat_pos,"Unknown") == 0) {
    pdata = NULL; len = 0;
  }else{
    pdata = fi->pat_pos; len = strlen(pdata);
  }
  mdc_dicom_write_element(fi->ofp,0x0018,0x5100,len,(Uint8 *)pdata);

  return(NULL);
}

char *MdcDicomWriteG0020(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom)
{
  Uint32 len;
  char *pdata;

  /* group 0x0020 */
  len = MdcDicomMakeUID(fi,MDC_TYPE_UID_STUDY,mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0020,0x000D,len,(Uint8 *)mdcbufr);

  len = MdcDicomMakeUID(fi,MDC_TYPE_UID_SERIES,mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0020,0x000E,len,(Uint8 *)mdcbufr);

  pdata = fi->study_id; len = strlen(pdata);
  mdc_dicom_write_element(fi->ofp,0x0020,0x0010,len,(Uint8 *)pdata);

  if (fi->nr_series >= 0) sprintf(mdcbufr,"%d",fi->nr_series);
  else strcpy(mdcbufr,"0");
  len = strlen(mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0020,0x0011,len,(Uint8 *)mdcbufr);

  if (fi->nr_acquisition >= 0) sprintf(mdcbufr,"%d",fi->nr_acquisition);
  else strcpy(mdcbufr,"0");
  len = strlen(mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0020,0x0012,len,(Uint8 *)mdcbufr);

  if (fi->nr_instance >= 0) sprintf(mdcbufr,"%d",fi->nr_instance);
  else strcpy(mdcbufr,"0");
  len = strlen(mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0020,0x0013,len,(Uint8 *)mdcbufr);

  len = MdcDicomMakeUID(fi,MDC_TYPE_UID_STUDY,mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0020,0x0052,len,(Uint8 *)mdcbufr);

  sprintf(mdcbufr,"%u",fi->number); len = strlen(mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0020,0x1002,len,(Uint8 *)mdcbufr);

  mdc_dicom_write_element(fi->ofp,0x0020,0x1040,0,NULL);

  strcpy(mdcbufr,"*** NOT APPROVED ***"); len = strlen(mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0020,0x4000,len,(Uint8 *)mdcbufr);

  return(NULL);
}

char *MdcDicomWriteG0028(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom)
{
  Uint32 len, bytes;
  Uint16 ui16, *pui16, bits_allocated, bits_stored;
  float intercept=0., slope=1.;
  Int16 type = dicom->type;

  /* group 0x0028 */
  ui16 = 1; len = sizeof(ui16);
  mdc_dicom_write_element(fi->ofp,0x0028,0x0002,len,(Uint8 *)&ui16);

  strcpy(mdcbufr,"MONOCHROME2"); len = strlen(mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0028,0x0004,len,(Uint8 *)mdcbufr);

  sprintf(mdcbufr,"%u",fi->number); len = strlen(mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0028,0x0008,len,(Uint8 *)mdcbufr);

  bytes = 0; pui16 = NULL;
  switch (dicom->modality) {
    case M_PT:
        switch (fi->acquisition_type) {
          case MDC_ACQUISITION_DYNAMIC:
              bytes = 4 * sizeof(Uint16); pui16 = (Uint16 *)malloc(bytes);
              if (pui16 == NULL)
                return("DICM Bad malloc (PT/DYNAMIC) FrameIncrPointer");
              pui16[0]=0x0054; pui16[1]=0x0080; /* slices */
              pui16[2]=0x0054; pui16[3]=0x0100; /* frames */
              dicom->VectDO[MDC_VECT_SLICE]     = MDC_YES;
              dicom->VectDO[MDC_VECT_TIMESLICE] = MDC_YES;
              break;
          case MDC_ACQUISITION_TOMO:
          case MDC_ACQUISITION_STATIC:
          case MDC_ACQUISITION_UNKNOWN:
          default:
              bytes = 2 * sizeof(Uint16); pui16 = (Uint16 *)malloc(bytes);
              if (pui16 == NULL)
                return("DICM Bad malloc (PT/STATIC) FrameIncrPointer");
              pui16[0]=0x0054; pui16[1]=0x0080; /* slices */
              dicom->VectDO[MDC_VECT_SLICE]     = MDC_YES;
        }
        mdc_dicom_write_element(fi->ofp,0x0028,0x0009,bytes,(Uint8 *)pui16);
        MdcFree(pui16);
        break;
    default  : /* default to NM modality */
        switch (fi->acquisition_type) {
          case MDC_ACQUISITION_TOMO:
              bytes = 2 * sizeof(Uint16); pui16 = (Uint16 *)malloc(bytes);
              if (pui16 == NULL)
                return("DICM Bad malloc (NM/TOMO) FrameIncrPointer");
              pui16[0]=0x0054; pui16[1]=0x0080; /* slices */
              dicom->VectDO[MDC_VECT_SLICE]     = MDC_YES;
              break;
          case MDC_ACQUISITION_DYNAMIC:
              bytes = 8 * sizeof(Uint16); pui16 = (Uint16 *)malloc(bytes);
              if (pui16 == NULL)
                return("DICM Bad malloc (NM/DYNAMIC) FrameIncrPointer");
              pui16[0]=0x0054; pui16[1]=0x0010; /* energy windows */
              pui16[2]=0x0054; pui16[3]=0x0020; /* detectors      */
              pui16[4]=0x0054; pui16[5]=0x0030; /* phases         */
              pui16[6]=0x0054; pui16[7]=0x0100; /* time slices    */
              dicom->VectDO[MDC_VECT_ENERGYWINDOW] = MDC_YES;
              dicom->VectDO[MDC_VECT_DETECTOR]     = MDC_YES;
              dicom->VectDO[MDC_VECT_PHASE]        = MDC_YES;
              dicom->VectDO[MDC_VECT_TIMESLICE]    = MDC_YES;
              break;
          case MDC_ACQUISITION_GATED:
              bytes = 8 * sizeof(Uint16); pui16 = (Uint16 *)malloc(bytes);
              if (pui16 == NULL)
                return("DICM Bad malloc (NM/GATED) FrameIncrPointer");
              pui16[0]=0x0054; pui16[1]=0x0010; /* energy windows */
              pui16[2]=0x0054; pui16[3]=0x0020; /* detectors      */
              pui16[4]=0x0054; pui16[5]=0x0060; /* RR-intervals   */
              pui16[6]=0x0054; pui16[7]=0x0070; /* time slots     */
              dicom->VectDO[MDC_VECT_ENERGYWINDOW] = MDC_YES;
              dicom->VectDO[MDC_VECT_DETECTOR]     = MDC_YES;
              dicom->VectDO[MDC_VECT_RRINTERVAL]   = MDC_YES;
              dicom->VectDO[MDC_VECT_TIMESLOT]     = MDC_YES;
              break;
          case MDC_ACQUISITION_GSPECT:
              if (fi->reconstructed == MDC_YES) {
                bytes = 6 * sizeof(Uint16); pui16 = (Uint16 *)malloc(bytes);
                if (pui16 == NULL)
                  return("DICM Bad malloc (NM/RECON GATED) FrameIncrPointer");
                pui16[0]=0x0054; pui16[1]=0x0060; /* RR-intervals */
                pui16[2]=0x0054; pui16[3]=0x0070; /* time slots   */
                pui16[4]=0x0054; pui16[5]=0x0080; /* slices       */
                dicom->VectDO[MDC_VECT_RRINTERVAL] = MDC_YES;
                dicom->VectDO[MDC_VECT_TIMESLOT]   = MDC_YES;
                dicom->VectDO[MDC_VECT_SLICE]      = MDC_YES;
              }else{
                bytes = 12 * sizeof(Uint16); pui16 = (Uint16 *)malloc(bytes);
                if (pui16 == NULL)
                  return("DICM Bad malloc (NM/GATED TOMO) FrameIncrPointer");
                pui16[0]=0x0054; pui16[1]=0x0010; /* energy windows */
                pui16[2]=0x0054; pui16[3]=0x0020; /* detectors      */
                pui16[4]=0x0054; pui16[5]=0x0050; /* rotations = 1  */
                pui16[6]=0x0054; pui16[7]=0x0060; /* RR-intervals   */
                pui16[8]=0x0054; pui16[9]=0x0070; /* time slot      */
                pui16[10]=0x0054;pui16[11]=0x0090;/* angular views  */
                dicom->VectDO[MDC_VECT_ENERGYWINDOW] = MDC_YES;
                dicom->VectDO[MDC_VECT_DETECTOR]     = MDC_YES;
                dicom->VectDO[MDC_VECT_ROTATION]     = MDC_YES;
                dicom->VectDO[MDC_VECT_RRINTERVAL]   = MDC_YES;
                dicom->VectDO[MDC_VECT_TIMESLOT]     = MDC_YES;
                dicom->VectDO[MDC_VECT_ANGULARVIEW]  = MDC_YES;
              }
              break;
          case MDC_ACQUISITION_UNKNOWN: /* fake as static */
          case MDC_ACQUISITION_STATIC:
              bytes = 4 * sizeof(Uint16); pui16 = (Uint16 *)malloc(bytes);
              if (pui16 == NULL)
                return("DICM Bad malloc (NM/STATIC) FrameIncrPointer");
              pui16[0]=0x0054; pui16[1]=0x0010; /* energy windows */
              pui16[2]=0x0054; pui16[3]=0x0020; /* detectors      */
              dicom->VectDO[MDC_VECT_ENERGYWINDOW] = MDC_YES;
              dicom->VectDO[MDC_VECT_DETECTOR]     = MDC_YES;
              break;
        }

        mdc_dicom_write_element(fi->ofp,0x0028,0x0009,bytes,(Uint8 *)pui16);
        MdcFree(pui16);

  }

  ui16 = (Uint16) fi->mheight; len = sizeof(ui16);
  mdc_dicom_write_element(fi->ofp,0x0028,0x0010,len,(Uint8 *)&ui16);

  ui16 = (Uint16) fi->mwidth; len = sizeof(ui16);
  mdc_dicom_write_element(fi->ofp,0x0028,0x0011,len,(Uint8 *)&ui16);

  sprintf(mdcbufr,"%+e\\%+e",fi->pixdim[IROW],fi->pixdim[ICOL]);
  len = strlen(mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0028,0x0030,len,(Uint8 *)mdcbufr);

  mdcbufr[0]='\0';
  if (fi->decay_corrected) strcat(mdcbufr,"DECY\\");
  if (fi->flood_corrected) strcat(mdcbufr,"UNIF\\");
  len = strlen(mdcbufr);
  /* if present, remove last redundant backslash */
  if (len > 0) {
    len -= 1;
    mdcbufr[len] = '\0';
  }
  mdc_dicom_write_element(fi->ofp,0x0028,0x0051,len,(Uint8 *)mdcbufr);

  bits_allocated = (Uint16)MdcType2Bits(type);
  if (MDC_FORCE_INT == BIT16_S) {
    bits_stored = MDC_INT16_BITS_USED;
  }else{
    bits_stored = MdcType2Bits(type);
  }
  ui16 = bits_allocated; /* bits allocated */ len = sizeof(ui16);
  mdc_dicom_write_element(fi->ofp,0x0028,0x0100,len,(Uint8 *)&ui16);

  ui16 = bits_stored;      /* bits stored */  len = sizeof(ui16);
  mdc_dicom_write_element(fi->ofp,0x0028,0x0101,len,(Uint8 *)&ui16);

  ui16 = bits_stored - 1;  /* high bit */     len = sizeof(ui16);
  mdc_dicom_write_element(fi->ofp,0x0028,0x0102,len,(Uint8 *)&ui16);

  switch (type) {
   case  BIT8_U:
   case BIT16_U:
   case BIT32_U:
   case BIT64_U:  ui16 = 0; break;

   case  BIT8_S:
   case BIT16_S:
   case BIT32_S:
   case BIT64_S:  ui16 = 1; break;

   default: ui16 = 0;
  }
  if (type == BIT16_S && MDC_INT16_BITS_USED < 16) ui16 = 0; /* unsigned */
  len = sizeof(ui16);
  mdc_dicom_write_element(fi->ofp,0x0028,0x0103,len,(Uint8 *)&ui16);

  /* need to rewrite the following tag after rescaling images */
  MDC_REWRF_INTERCEPT = ftello(fi->ofp);
  sprintf(mdcbufr,"%+e",intercept); len = strlen(mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0028,0x1052,len,(Uint8 *)mdcbufr);

  /* need to rewrite the following tag after rescaling images */
  MDC_REWRF_SLOPE = ftello(fi->ofp);
  sprintf(mdcbufr,"%+e",slope); len = strlen(mdcbufr);
  mdc_dicom_write_element(fi->ofp,0x0028,0x1053,len,(Uint8 *)mdcbufr);


  return(NULL);
}

/* write NM Image Information */
char *MdcDicomWriteG0054(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom)
{
  ACQ_DATA tmp_acqdata, *acqdata=NULL;
  DYNAMIC_DATA *dd=NULL;
  Uint32 i, t, bytes, dim, vect, len, acqnr;
  Uint32 ph, ts, inr;
  Uint16 ui16, *pui16;
  float v;

  if (fi->acqnr > 0 && fi->acqdata != NULL) {
    acqnr = fi->acqnr;
    acqdata = (ACQ_DATA *)fi->acqdata;
  }else{
    acqnr = 1;
    acqdata = (ACQ_DATA *)&tmp_acqdata; MdcInitAD(acqdata);
    if (gd->nr_projections > 0.) {
      acqdata->angle_step = gd->extent_rotation / gd->nr_projections;
    }else{
      acqdata->angle_step = acqdata->scan_arc / (float)fi->dim[3];
    }
  }

  /* group 0x0054 */
  if (dicom->VectDO[MDC_VECT_ENERGYWINDOW] == MDC_YES) {
    /* MARK: window vectors */
      if (fi->dim[7] == 0)
        return("DICM Bad zero value for fi->dim[7]");
      if (fi->number % fi->dim[7])
        return("DICM Garbled value for fi->dim[7]");
      vect = fi->number / fi->dim[7];
      bytes = fi->number * sizeof(Uint16);
      pui16 = (Uint16 *)malloc(bytes);
      if (pui16 == NULL)
        return("DICM Bad malloc EnergyWindowVector");
      for (i=0; i<fi->number; i++) pui16[i] = (Uint16)((i/vect)+1);
      mdc_dicom_write_element(fi->ofp,0x0054,0x0010,bytes,(Uint8 *)pui16);
      MdcFree(pui16);
  }

  /* number of energy windows */
  ui16 = fi->dim[7]; len = sizeof(ui16);
  mdc_dicom_write_element(fi->ofp,0x0054,0x0011,len,(Uint8 *)&ui16);

  /* window information sequence */
  MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0012);

  /* item */
  MdcDicomWriteItem(fi->ofp);
    /* window range sequence */
    MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0013);
     /* item */
     MdcDicomWriteItem(fi->ofp);
     /* item delimitation item */
     MdcDicomWriteItemDelItem(fi->ofp);
     /* sequence delimitation item*/
    MdcDicomWriteSeqDelItem(fi->ofp);
  /* item delimitation item */
  MdcDicomWriteItemDelItem(fi->ofp);
  /* sequence delimitation item */
  MdcDicomWriteSeqDelItem(fi->ofp);

  /* radiopharmaceutical info sequence */
  MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0016);
    /* item */
    MdcDicomWriteItem(fi->ofp);

     strcpy(mdcbufr,fi->radiopharma); len = strlen(mdcbufr);
     mdc_dicom_write_element(fi->ofp,0x0018,0x0031,len,(Uint8 *)mdcbufr);
     strcpy(mdcbufr,"0.0"); len = strlen(mdcbufr);
     mdc_dicom_write_element(fi->ofp,0x0018,0x1071,len,(Uint8 *)mdcbufr);
     sprintf(mdcbufr,"%02d%02d%02d",fi->dose_time_hour
                                   ,fi->dose_time_minute
                                   ,fi->dose_time_second);
     len = strlen(mdcbufr);
     mdc_dicom_write_element(fi->ofp,0x0018,0x1072,len,(Uint8 *)mdcbufr);
     sprintf(mdcbufr,"%g",fi->injected_dose); len = strlen(mdcbufr);
     mdc_dicom_write_element(fi->ofp,0x0018,0x1074,len,(Uint8 *)mdcbufr);
     sprintf(mdcbufr,"%g",fi->isotope_halflife); len = strlen(mdcbufr);
     mdc_dicom_write_element(fi->ofp,0x0018,0x1075,len,(Uint8 *)mdcbufr);
     /* radionuclidecode info sequence */
     MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0300);
      /* item */
      MdcDicomWriteItem(fi->ofp);

       strcpy(mdcbufr,fi->isotope_code); len = strlen(mdcbufr);
       mdc_dicom_write_element(fi->ofp,0x0008,0x0100,len,(Uint8 *)mdcbufr);
       strcpy(mdcbufr,"99SDM");   len = strlen(mdcbufr);
       mdc_dicom_write_element(fi->ofp,0x0008,0x0102,len,(Uint8 *)mdcbufr);
       strcpy(mdcbufr,fi->isotope_code); len = strlen(mdcbufr);
       mdc_dicom_write_element(fi->ofp,0x0008,0x0104,len,(Uint8 *)mdcbufr);

      /* item delimitation item */
      MdcDicomWriteItemDelItem(fi->ofp);
     /* sequence delimiter */
     MdcDicomWriteSeqDelItem(fi->ofp);

     MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0304);
      /* item */
      MdcDicomWriteItem(fi->ofp);

       strcpy(mdcbufr,fi->radiopharma); len = strlen(mdcbufr);
       mdc_dicom_write_element(fi->ofp,0x0008,0x0100,len,(Uint8 *)mdcbufr);
       strcpy(mdcbufr,"99SDM");   len = strlen(mdcbufr);
       mdc_dicom_write_element(fi->ofp,0x0008,0x0102,len,(Uint8 *)mdcbufr);
       strcpy(mdcbufr,fi->radiopharma); len = strlen(mdcbufr);
       mdc_dicom_write_element(fi->ofp,0x0008,0x0104,len,(Uint8 *)mdcbufr);

      /* item delimitation item */
      MdcDicomWriteItemDelItem(fi->ofp);
     /* sequence delimiter */
     MdcDicomWriteSeqDelItem(fi->ofp);





    /* item delimitation item */
    MdcDicomWriteItemDelItem(fi->ofp);
  /* sequence delimiter */
  MdcDicomWriteSeqDelItem(fi->ofp);


  if (dicom->VectDO[MDC_VECT_DETECTOR] == MDC_YES) {
    /* MARK: detector vectors */
    if (fi->dim[6] == 0)
      return("DICM Bad zero value for fi->dim[6]");
    if (fi->number % fi->dim[6])
      return("DICM Garbled value for fi->dim[6]");
    vect = fi->number / fi->dim[6];
    bytes = fi->number * sizeof(Uint16);
    pui16 = (Uint16 *)malloc(bytes);
    if (pui16 == NULL)
      return("DICM Bad malloc DetectorVector");
    for (i=0; i<fi->number; i++) pui16[i] = (Uint16)((i/vect)+1);
    mdc_dicom_write_element(fi->ofp,0x0054,0x0020,bytes,(Uint8 *)pui16);
    MdcFree(pui16);
  }
  /* number of detector heads */
  ui16 = fi->dim[6]; len = sizeof(ui16);
  mdc_dicom_write_element(fi->ofp,0x0054,0x0021,len,(Uint8 *)&ui16);

  /* detector info sequence */
  MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0022);

   /* item */
   MdcDicomWriteItem(fi->ofp);

    /* colimator type */
    mdc_dicom_write_element(fi->ofp,0x0018,0x1181,0,NULL);

    /* focal distance */
    mdc_dicom_write_element(fi->ofp,0x0018,0x1182,0,NULL);

    /* image pos patient */
    sprintf(mdcbufr,"%+e\\%+e\\%+e",fi->image[0].image_pos_pat[0]
                                   ,fi->image[0].image_pos_pat[1]
                                   ,fi->image[0].image_pos_pat[2]);
    len = strlen(mdcbufr);
    mdc_dicom_write_element(fi->ofp,0x0020,0x0032,len,(Uint8 *)mdcbufr);
    /* image orient patient */
    sprintf(mdcbufr,"%+e\\%+e\\%+e\\%+e\\%+e\\%+e"
                                   ,fi->image[0].image_orient_pat[0]
                                   ,fi->image[0].image_orient_pat[1]
                                   ,fi->image[0].image_orient_pat[2]
                                   ,fi->image[0].image_orient_pat[3]
                                   ,fi->image[0].image_orient_pat[4]
                                   ,fi->image[0].image_orient_pat[5]);
    len = strlen(mdcbufr);
    mdc_dicom_write_element(fi->ofp,0x0020,0x0037,len,(Uint8 *)mdcbufr);

    /* static image label */
    if (fi->image[0].sdata != NULL) {
      STATIC_DATA *sd = fi->image[0].sdata;

      if (fi->number > 1) {
        MdcPrntWarn("DICM static info lost; to prevent choose to split slices");
      }

      /* view code sequence */
      MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0220);

       /* item */
       MdcDicomWriteItem(fi->ofp);

        strcpy(mdcbufr,sd->label); len = strlen(sd->label);
        mdc_dicom_write_element(fi->ofp,0x0008,0x0100,len,(Uint8 *)mdcbufr);

        strcpy(mdcbufr,"99SDM"); len = strlen(mdcbufr);
        mdc_dicom_write_element(fi->ofp,0x0008,0x0102,len,(Uint8 *)mdcbufr);

        strcpy(mdcbufr,sd->label); len = strlen(sd->label);
        mdc_dicom_write_element(fi->ofp,0x0008,0x0104,len,(Uint8 *)mdcbufr);

       /* item delimitation item */
       MdcDicomWriteItemDelItem(fi->ofp);

      /* sequence delimitation item */
      MdcDicomWriteSeqDelItem(fi->ofp);
    }else{
      /* start angle */
      v = acqdata[0].angle_start; v = MdcRotateAngle(v,180.);
      sprintf(mdcbufr,"%g",v); len = strlen(mdcbufr);
      mdc_dicom_write_element(fi->ofp,0x0054,0x0200,len,(Uint8 *)mdcbufr);
    }

   /* item delimitation item */
   MdcDicomWriteItemDelItem(fi->ofp);

  /* sequence delimitation item */
  MdcDicomWriteSeqDelItem(fi->ofp);

  if (dicom->modality == M_PT) {

   switch (fi->acquisition_type) {

     /* PET MODALITY */
     case MDC_ACQUISITION_DYNAMIC:
      /* MARK: slice vector */
      if (dicom->VectDO[MDC_VECT_SLICE] == MDC_YES) {
        if (fi->dim[3] == 0)
          return("DICM Bad zero value for fi->dim[3] (PT)");
        vect = fi->dim[3];
        bytes = fi->number*sizeof(Uint16);
        pui16=(Uint16 *)malloc(bytes);
        if (pui16 == NULL)
          return("DICM Bad malloc SliceVector (PT)");
        for (i=0; i<fi->number; i++) pui16[i]=(Uint16)((i%vect)+1);
        mdc_dicom_write_element(fi->ofp,0x0054,0x0080,bytes,(Uint8 *)pui16);
        MdcFree(pui16);
      }

      ui16 = (Uint16) fi->dim[3]; len = sizeof(ui16);
      mdc_dicom_write_element(fi->ofp,0x0054,0x0081,len,(Uint8 *)&ui16);

      /* MARK: time slice vector */
      if (dicom->VectDO[MDC_VECT_TIMESLICE] == MDC_YES) {
        if (fi->dim[4] == 0)
          return("DICM Bad zero value for fi->dim[4] (PT)");
        if (fi->number % fi->dim[4])
          return("DICM Garbled value for fi->dim[4] (PT)");
        vect = fi->number / fi->dim[4];
        bytes = fi->number * sizeof(Uint16);
        pui16 = (Uint16 *)malloc(bytes);
        if (pui16 == NULL)
          return("DICM Bad malloc TimeSlotVector (PT)");
        for (i=0; i<fi->number; i++) pui16[i] = (Uint16)((i/vect)+1);
        mdc_dicom_write_element(fi->ofp,0x0054,0x0100,bytes,(Uint8 *)pui16);
        MdcFree(pui16);
      }

      ui16 = (Uint16) fi->dim[4];
      mdc_dicom_write_element(fi->ofp,0x0054,0x0101,len,(Uint8 *)&ui16);
      break;
     case MDC_ACQUISITION_STATIC :
     case MDC_ACQUISITION_TOMO   :
     case MDC_ACQUISITION_UNKNOWN:
     default:
      if (fi->dim[4] > 1) return("DICM Unsupported dim[]-values (PT)");

      /* MARK: slice vector */
      if (dicom->VectDO[MDC_VECT_SLICE] == MDC_YES) {
        bytes = fi->number*sizeof(Uint16); pui16=(Uint16 *)malloc(bytes);
        if (pui16 == NULL)
          return("DICM Bad malloc slice vector buffer (PT)");
        for (i=0; i<fi->number; i++) pui16[i]=(Uint16)i+1;
        mdc_dicom_write_element(fi->ofp,0x0054,0x0080,bytes,(Uint8 *)pui16);
        MdcFree(pui16);
      }

      ui16 = (Uint16) fi->dim[3]; len = sizeof(ui16);
      mdc_dicom_write_element(fi->ofp,0x0054,0x0081,len,(Uint8 *)&ui16);
    }

    switch (fi->acquisition_type) {
     case MDC_ACQUISITION_TOMO: strcpy(mdcbufr,"STATIC"); break;
     case MDC_ACQUISITION_DYNAMIC: strcpy(mdcbufr,"DYNAMIC"); break;
     case MDC_ACQUISITION_UNKNOWN: /* fake as static */
     case MDC_ACQUISITION_STATIC : strcpy(mdcbufr,"STATIC"); break;
     default: strcpy(mdcbufr,"UNSPECIFIED");
    }

    /* type of detector motion */
    /* strcpy(mdcbufr,"UNDEFINED"); */
    /* mdc_dicom_write_element(fi->ofp,0x0054,0x0202,strlen(mdcbufr)   */
    /*                                              ,(Uint8 *)mdcbufr);*/

    /* patient orientation code sequence */
    MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0410);
    /* sequence delimitation item */
    MdcDicomWriteSeqDelItem(fi->ofp);

    /* patient gantry relationship code sequence */
    MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0414);
    /* sequence delimitation item */
    MdcDicomWriteSeqDelItem(fi->ofp);

    if (fi->reconstructed == MDC_YES)
      strcat(mdcbufr,"\\IMAGE");
    else
      strcat(mdcbufr,"\\REPROJECTION");
    len = strlen(mdcbufr);
    mdc_dicom_write_element(fi->ofp,0x0054,0x1000,len,(Uint8 *)mdcbufr);


    strcpy(mdcbufr,"UNKNOWN"); /* units */ len = strlen(mdcbufr);
    mdc_dicom_write_element(fi->ofp,0x0054,0x1001,len,(Uint8 *)mdcbufr);

    strcpy(mdcbufr,"UNKNOWN"); /* counts source */ len = strlen(mdcbufr);
    mdc_dicom_write_element(fi->ofp,0x0054,0x1002,len,(Uint8 *)mdcbufr);

    if (fi->decay_corrected) {
      strcpy(mdcbufr,"ADMIN"); len = strlen(mdcbufr);
      mdc_dicom_write_element(fi->ofp,0x0054,0x1102,len,(Uint8 *)mdcbufr);
    }

  }else{ /* NM-MODALITY */

    /* MARK: phases vector*/
    if (dicom->VectDO[MDC_VECT_PHASE] == MDC_YES) {

      if ((fi->dynnr == 0) || (fi->dyndata == NULL))
        return("DICM Required DYNAMIC_DATA values missing");

      bytes = fi->number * sizeof(Uint16);
      pui16 = (Uint16 *)malloc(bytes);
      if (pui16 == NULL)
        return("DICM Bad malloc PhaseVector (NM)");

      for (i=0; i<fi->number; i++) pui16[i] = (Uint16)fi->image[i].frame_number;
      mdc_dicom_write_element(fi->ofp,0x0054,0x0030,bytes,(Uint8 *)pui16);
      MdcFree(pui16);

      /* number of phases */
      ui16 = (Uint16)fi->dynnr; len = sizeof(ui16);
      mdc_dicom_write_element(fi->ofp,0x0054,0x0031,len,(Uint8 *)&ui16);

      MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0032);

      for (i=0; i<fi->dynnr; i++) {

         MdcDicomWriteItem(fi->ofp);

         dd = &fi->dyndata[i];

         sprintf(mdcbufr,"%-12.0f",MdcSingleImageDuration(fi,i));
         len = strlen(mdcbufr);
         mdc_dicom_write_element(fi->ofp,0x0018,0x1242,len,(Uint8 *)mdcbufr);
         /* number of slices in phase */
         ui16 = (Uint16)dd->nr_of_slices; len = sizeof(ui16);
         mdc_dicom_write_element(fi->ofp,0x0054,0x0033,len,(Uint8 *)&ui16);

         sprintf(mdcbufr,"%-12.0f",dd->time_frame_delay); len = strlen(mdcbufr);
         /* phase delay */
         mdc_dicom_write_element(fi->ofp,0x0054,0x0036,len,(Uint8 *)mdcbufr);
         /* pause between frames */
         sprintf(mdcbufr,"%-12.0f",dd->delay_slices); len = strlen(mdcbufr);
         mdc_dicom_write_element(fi->ofp,0x0054,0x0038,len,(Uint8 *)mdcbufr);

         MdcDicomWriteItemDelItem(fi->ofp);
      }

      MdcDicomWriteSeqDelItem(fi->ofp);

    }

    /* MARK: Rotation Vector: we always consider it as 1, */
    /* we don't know how to map on InterFile tomographic  */
    if (dicom->VectDO[MDC_VECT_ROTATION] == MDC_YES) {
      vect = fi->number / 1;
      bytes = fi->number * sizeof(Uint16);
      pui16=(Uint16 *)malloc(bytes);
      if (pui16 == NULL)
        return("DICM Bad malloc RotationVector (NM)");
      for (i=0; i<fi->number; i++) pui16[i]=(Uint16)((i/vect)+1);
      mdc_dicom_write_element(fi->ofp,0x0054,0x0050,bytes,(Uint8 *)pui16);
      MdcFree(pui16);
    }

    /* Rotation sequence must be included for all TOMO types */
    if ( fi->acquisition_type == MDC_ACQUISITION_TOMO ||
         fi->acquisition_type == MDC_ACQUISITION_GSPECT ) {

      /* number of rotations */
      ui16 = (Uint16)acqnr;
      len = sizeof(ui16);
      mdc_dicom_write_element(fi->ofp,0x0054,0x0051,len,(Uint8 *)&ui16);

      /* rotation info sequence */
      MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0052);

      for (i=0; i<acqnr; i++) {
         acq = (ACQ_DATA *)&acqdata[i];

         /* item */
         MdcDicomWriteItem(fi->ofp);

         /* MARK: distance source to detector: only transmission   */
         /* mdc_dicom_write_element(fi->ofp,0x0018,0x1110,0,NULL); */

         switch (acq->rotation_direction) {
           case MDC_ROTATION_CW: strcpy(mdcbufr,"CW"); break;
           case MDC_ROTATION_CC: strcpy(mdcbufr,"CC"); break;
           default             : mdcbufr[0] = '\0';
         }
         len = strlen(mdcbufr);
         mdc_dicom_write_element(fi->ofp,0x0018,0x1140,len,(Uint8 *)mdcbufr);
         sprintf(mdcbufr,"%g",acq->radial_position); len = strlen(mdcbufr);
         mdc_dicom_write_element(fi->ofp,0x0018,0x1142,len,(Uint8 *)mdcbufr);
         sprintf(mdcbufr,"%g",acq->scan_arc); len = strlen(mdcbufr);
         mdc_dicom_write_element(fi->ofp,0x0018,0x1143,len,(Uint8 *)mdcbufr);
         sprintf(mdcbufr,"%g",acq->angle_step); len = strlen(mdcbufr);
         mdc_dicom_write_element(fi->ofp,0x0018,0x1144,len,(Uint8 *)mdcbufr);
         sprintf(mdcbufr,"%g",acq->rotation_offset); len = strlen(mdcbufr);
         mdc_dicom_write_element(fi->ofp,0x0018,0x1145,len,(Uint8 *)mdcbufr);
         if (fi->acquisition_type == MDC_ACQUISITION_GSPECT) {
           /* gspect: gd->time_per_proj */
             v = gd->time_per_proj;
         }else{
           /* tomo  : dd->time_frame_duration */
           if ((fi->dynnr > 0) && (fi->dyndata != NULL)) {
             v = fi->dyndata[0].time_frame_duration;
           }else{
             v = 0.;
           }
         }
         sprintf(mdcbufr,"%-12.0f",v); len = strlen(mdcbufr);
         mdc_dicom_write_element(fi->ofp,0x0018,0x1242,len,(Uint8 *)mdcbufr);

         if (gd->nr_projections > 0.) {
           ui16 = (Uint16)gd->nr_projections; len = sizeof(ui16);
         }else{
           ui16 = (Uint16)fi->dim[3]; len = sizeof(ui16);
         }
         mdc_dicom_write_element(fi->ofp,0x0054,0x0053,len,(Uint8 *)&ui16);
         v = acq->angle_start; v = MdcRotateAngle(v, 180.);
         sprintf(mdcbufr,"%g",v); len = strlen(mdcbufr);
         mdc_dicom_write_element(fi->ofp,0x0054,0x0200,len,(Uint8 *)mdcbufr);

         switch (acq->detector_motion) {
           case MDC_MOTION_STEP: strcpy(mdcbufr,"STEP AND SHOOT");  break;
           case MDC_MOTION_CONT: strcpy(mdcbufr,"CONTINUOUS");      break;
           case MDC_MOTION_DRNG: strcpy(mdcbufr,"ACQ DURING STEP"); break;
           default             : strcpy(mdcbufr,"UNDEFINED");
         }
         len = strlen(mdcbufr);
         mdc_dicom_write_element(fi->ofp,0x0054,0x0202,len,(Uint8 *)mdcbufr);

         MdcDicomWriteItemDelItem(fi->ofp);
      }

      /* sequence delimitation item */
      MdcDicomWriteSeqDelItem(fi->ofp);

    }

    /* MARK: interval vectors */
    if (dicom->VectDO[MDC_VECT_RRINTERVAL] == MDC_YES) {
      if (fi->dim[5] == 0)
        return("DICM Bad zero value for fi->dim[5] (NM)");
      if (fi->number % fi->dim[5])
        return("DICM Garbled value for fi->dim[5] (NM)");
      vect = fi->number / fi->dim[5];
      bytes = fi->number * sizeof(Uint16);
      pui16=(Uint16 *)malloc(bytes);
      if (pui16 == NULL)
        return("DICM Bad malloc RRIntervalVector (NM)");
      for (i=0; i<fi->number; i++) pui16[i]=(Uint16)((i/vect)+1);
      mdc_dicom_write_element(fi->ofp,0x0054,0x0060,bytes,(Uint8 *)pui16);
      MdcFree(pui16);

      /* number of intervals */
      ui16 = (Uint16) fi->dim[5];
      mdc_dicom_write_element(fi->ofp,0x0054,0x0061,sizeof(Uint16)
                                                   ,(Uint8 *)&ui16);

      MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0062);
      for (i=0; i<(Uint32)fi->dim[5]; i++) {
         MdcDicomWriteItem(fi->ofp);
          MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0063);
           MdcDicomWriteItem(fi->ofp);
           /* nominal interval*/
           mdc_dicom_write_element(fi->ofp,0x0018,0x1062,0,NULL);
           /* frame time */
           sprintf(mdcbufr,"%+e",gd->image_duration); len = strlen(mdcbufr);
           mdc_dicom_write_element(fi->ofp,0x0018,0x1063,len,(Uint8 *)mdcbufr);
           /* low  RR value  */
           sprintf(mdcbufr,"%u",(Uint16)gd->window_low); len = strlen(mdcbufr);
           mdc_dicom_write_element(fi->ofp,0x0018,0x1081,len,(Uint8 *)mdcbufr);
           /* high RR value */
           sprintf(mdcbufr,"%u",(Uint16)gd->window_high); len = strlen(mdcbufr);
           mdc_dicom_write_element(fi->ofp,0x0018,0x1082,len,(Uint8 *)mdcbufr);
           /* intervals acquired */
           ui16 = (Uint16) gd->cycles_acquired;
           sprintf(mdcbufr,"%u",ui16); len = strlen(mdcbufr);
           mdc_dicom_write_element(fi->ofp,0x0018,0x1083,len,(Uint8 *)mdcbufr);
           /* intervals rejected */
           ui16 = (Uint16) (gd->cycles_observed - gd->cycles_acquired);
           sprintf(mdcbufr,"%u",ui16); len = strlen(mdcbufr);
           mdc_dicom_write_element(fi->ofp,0x0018,0x1084,len,(Uint8 *)mdcbufr);

            MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0072);
            for (t=0; t<(Uint32)fi->dim[4]; t++) {
               MdcDicomWriteItem(fi->ofp);
               v = (gd->cycles_acquired * gd->image_duration)/(float)fi->number;
               sprintf(mdcbufr,"%+e",v); len = strlen(mdcbufr);
               mdc_dicom_write_element(fi->ofp,0x0054,0x0073
                                              ,len,(Uint8 *)mdcbufr);
               MdcDicomWriteItemDelItem(fi->ofp);
            }
            MdcDicomWriteSeqDelItem(fi->ofp);

           MdcDicomWriteItemDelItem(fi->ofp);
          MdcDicomWriteSeqDelItem(fi->ofp);
         MdcDicomWriteItemDelItem(fi->ofp);
      }

      MdcDicomWriteSeqDelItem(fi->ofp);

    }

    /* MARK: timeslot vectors */
    if (dicom->VectDO[MDC_VECT_TIMESLOT] == MDC_YES) {
      if (fi->acquisition_type == MDC_ACQUISITION_GATED) {
        /* MARK: for gated, time slot is the last dimension !! */
        dim = fi->dim[3];
      }else{
        dim = fi->dim[4];
      }
      if (dim == 0)
        return("DICM Bad zero value for fi->dim[3|4] (NM)");
      if (fi->number % dim)
        return("DICM Garbled value for fi->dim[3|4] (NM)");
      vect = fi->number / dim;
      bytes = fi->number * sizeof(Uint16);
      pui16=(Uint16 *)malloc(bytes);
      if (pui16 == NULL)
        return("DICM Bad malloc TimeSlotVector (NM)");
      for (i=0; i<fi->number; i++) pui16[i]=(Uint16)((i/vect)+1);
      mdc_dicom_write_element(fi->ofp,0x0054,0x0070,bytes,(Uint8 *)pui16);
      MdcFree(pui16);

      /* number of intervals */
      ui16 = (Uint16) dim;
      mdc_dicom_write_element(fi->ofp,0x0054,0x0071,sizeof(Uint16)
                                                   ,(Uint8 *)&ui16);

    }

    /* MARK: slice vector */
    if (dicom->VectDO[MDC_VECT_SLICE] == MDC_YES) {
      bytes = fi->number*sizeof(Uint16); pui16=(Uint16 *)malloc(bytes);
      if (pui16 == NULL)
        return("DICM Bad malloc SliceVector (NM)");
      vect = fi->dim[3];
      for (i=0; i<fi->number; i++) pui16[i]=(Uint16)((i%vect)+1);
      mdc_dicom_write_element(fi->ofp,0x0054,0x0080,bytes,(Uint8 *)pui16);
      MdcFree(pui16);

      /* number of slices */
      ui16 = (Uint16) fi->dim[3];
      mdc_dicom_write_element(fi->ofp,0x0054,0x0081,sizeof(Uint16)
                                                   ,(Uint8 *)&ui16);
    }

    /* MARK: angular view vector */
    if (dicom->VectDO[MDC_VECT_ANGULARVIEW] == MDC_YES) {
      bytes = fi->number*sizeof(Uint16); pui16=(Uint16 *)malloc(bytes);
      if (pui16 == NULL)
        return("DICM Bad malloc AngularViewVector (NM)");
      vect = fi->dim[3];
      for (i=0; i<fi->number; i++) pui16[i]=(Uint16)((i%vect)+1);
      mdc_dicom_write_element(fi->ofp,0x0054,0x0090,bytes,(Uint8 *)pui16);
      MdcFree(pui16);
    }

    /* MARK: time slice vector */
    if (dicom->VectDO[MDC_VECT_TIMESLICE] == MDC_YES) {
      if ((fi->dynnr > 0) && (fi->dyndata != NULL)) {
        bytes = fi->number * sizeof(Uint16);
        pui16 = (Uint16 *)malloc(bytes);
        if (pui16 == NULL)
          return("DICM Bad malloc TimeSliceVector (NM)");

        /* phases */
        inr = 0;
        for (ph = 0; ph < fi->dynnr; ph++ ) {
           /* timeslices */
           for (ts=0; ts < fi->dyndata[ph].nr_of_slices ; ts++) {
              pui16[inr++] = (Uint16)ts + 1;
           }
        }

        mdc_dicom_write_element(fi->ofp,0x0054,0x0100,bytes,(Uint8 *)pui16);
        MdcFree(pui16);
      }else{
        return("DICM Missing dynamic data structs");
      }
    }

    /* patient orientation code sequence */
    MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0410);
    /* sequence delimitation item */
    MdcDicomWriteSeqDelItem(fi->ofp);

    /* patient gantry relationship code sequence */
    MdcDicomWriteInfoSeq(fi->ofp,0x0054,0x0414);
    /* sequence delimitation item */
    MdcDicomWriteSeqDelItem(fi->ofp);

  }

  return(NULL);
}

/* write Images */
char *MdcDicomWriteG7FE0(FILEINFO *fi, MDC_DICOM_STUFF_T *dicom)
{
  Uint32 i, bytes, pixels, length;
  Uint8 *newbuff, *buff, c=0x00, PAD_EVEN=0;
  float slope, intercept;

  /* group 0x7FE0 - dump the images */
  if (dicom->ENCAPSULATED_UNCOMPRESSED == MDC_YES) {
    /* one image per fragment (size > 4GB) */
    bytes = fi->mwidth * fi->mheight * MdcType2Bytes(dicom->type);
  }else{
    /* all images fit in one fragment (size < 4GB) */
    bytes = fi->number * fi->mwidth * fi->mheight * MdcType2Bytes(dicom->type);
  }

  if (bytes%2) { PAD_EVEN=1; bytes+=1; }

  if (dicom->ENCAPSULATED_UNCOMPRESSED == MDC_YES) {
    Uint8 vr_ob = BIT8_U;                                                   /* encapsulated: VR must be OB           */
    mdc_dicom_write_element(fi->ofp,0x7fe0,0x0010,UNDEFINED_LENGTH,&vr_ob); /* pixel data element + vr + data length */
    mdc_dicom_write_element(fi->ofp,0xfffe,0xe000,0,NULL);                  /* basic offset table with NO item value */
  }else{
    mdc_dicom_write_element(fi->ofp,0x7fe0,0x0010,bytes,(Uint8 *)&dicom->type);
  }

  for (i=0; i<fi->number; i++) {

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

     switch (dicom->type) {
       case BIT8_U : newbuff = MdcGetImgBIT8_U(fi,i);   break;
       case BIT16_S: newbuff = MdcGetImgBIT16_S(fi,i);  break;
       default: newbuff = NULL; /* bad pixel type */
     }
     if (newbuff == NULL) return("DICM Bad malloc newbuff image");

     if (fi->diff_size == MDC_YES) {
       buff = MdcGetResizedImage(fi,newbuff,dicom->type,i);
       if (buff == NULL) return("DICM Bad malloc resized image");
       MdcFree(newbuff);
     }else buff = newbuff;

     if (MDC_FILE_ENDIAN != MDC_HOST_ENDIAN)
       MdcMakeImgSwapped(buff, fi, i, fi->mwidth, fi->mheight, dicom->type);

     pixels = fi->mwidth * fi->mheight;
     bytes = MdcType2Bytes(dicom->type);

     if (dicom->ENCAPSULATED_UNCOMPRESSED == MDC_YES) {
       length = pixels * bytes;
       if (PAD_EVEN) length+=1;
       mdc_dicom_write_element(fi->ofp,0xfffe,0xe000,length,NULL); /* item tag with item length */
     }

     if (fwrite(buff,bytes,pixels,fi->ofp) != pixels)
       return("DICM Bad writing of image");

     if ((dicom->ENCAPSULATED_UNCOMPRESSED == MDC_YES) && PAD_EVEN) {
       /* one image per fragment, make even for each image */
       if (fwrite(&c,1,1,fi->ofp) != 1) {
         return("DICM Failed to pad encapsulated image fragment");
       }
     }

     MdcFree(buff);
  }

  if ((dicom->ENCAPSULATED_UNCOMPRESSED == MDC_NO) && PAD_EVEN) {
    /* all images in one fragment, make even at the end */
    if (fwrite(&c,1,1,fi->ofp) != 1) {
      return("DICM Failed to pad pixel data even");
    }
  }

  if (dicom->ENCAPSULATED_UNCOMPRESSED == MDC_YES) {
    /* finish encapsulated with sequence delim. tag */
    MdcDicomWriteSeqDelItem(fi->ofp);
  }

  if (MDC_QUANTIFY == MDC_YES || MDC_CALIBRATE == MDC_YES) {
    /* rewrite the true intercept value */
    fseeko(fi->ofp,MDC_REWRF_INTERCEPT,SEEK_SET);
    intercept = fi->image[0].rescaled_intercept;
    sprintf(mdcbufr,"%+e",intercept); length = strlen(mdcbufr);
    mdc_dicom_write_element(fi->ofp,0x0028,0x1052,length,(Uint8 *)mdcbufr);

    /* rewrite the true slope     value */
    fseeko(fi->ofp,MDC_REWRF_SLOPE,SEEK_SET);
    slope = fi->image[0].rescaled_slope;
    sprintf(mdcbufr,"%+e",slope); length = strlen(mdcbufr);
    mdc_dicom_write_element(fi->ofp,0x0028,0x1053,length,(Uint8 *)mdcbufr);
  }

  return(NULL);
}

const char *MdcWriteDICM(FILEINFO *fi)
{
  GATED_DATA tmpgd;
  MDC_DICOM_STUFF_T *dicom=&mdc_dicom_stuff;
  Int64 bytes;
  const char *msg;

  if (MDC_DICOM_WRITE_IMPLICIT == MDC_YES) {
    MDC_FILE_ENDIAN = MDC_LITTLE_ENDIAN;
  }else{
    MDC_FILE_ENDIAN = MDC_WRITE_ENDIAN;
  }

  if (fi->gatednr > 0 && fi->gdata != NULL) {
    gd = (GATED_DATA *)&fi->gdata[0];
  }else{
    gd = (GATED_DATA *)&tmpgd; MdcInitGD(gd);
  }

  /* no batch process in GUI, change UID's for each write */
  if (XMDC_GUI == MDC_YES) mdc_psec = NULL;

  if (mdc_psec == NULL) {
    /* for first time, retrieve universal time (seconds) */
    if ( time(&mdc_sec) == ((time_t)-1) ) {
      MdcPrntMesg("DICM Generating unique UID failed");
    }else{
      mdc_psec = &mdc_sec;
    }
  }

  if (XMDC_GUI == MDC_NO) {
    MdcDefaultName(fi,MDC_FRMT_DICM,fi->ofname,fi->ifname);
  }

  if (MDC_PROGRESS) MdcProgress(MDC_PROGRESS_BEGIN,0.,"Writing DICOM:");

  if (MDC_VERBOSE) MdcPrntMesg("DICM Writing <%s> ...",fi->ofname);

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

  if (MDC_FILE_STDOUT == MDC_YES) {
    fi->ofp = stdout;
  }else{
    if (MdcKeepFile(fi->ofname))
      return("DICM File exists!!");
    if ( (fi->ofp=fopen(fi->ofname,"wb")) == NULL)
      return("DICM Couldn't open file");
  }

  /* init dicom struct */
  MdcDicomInitStuff(dicom);

  /* set modality to write */
  MdcDicomWriteSetModality(fi,dicom);

  /* check for requested pixel type */
  if (MDC_FORCE_INT != MDC_NO) dicom->type = MDC_FORCE_INT;

  /* only Int16 or Uint8 type supported */
  if ( (dicom->type != BIT16_S) && (dicom->type != BIT8_U) ) {
    dicom->type = BIT16_S;
    MdcPrntWarn("DICM Only Int16 or Uint8 pixels supported");
  }

  /* check for large file > 4GB OR forced encapsulated uncompressed */
  if ( MDC_DICOM_WRITE_ENCAP_UNCOMP == MDC_YES ) {
    dicom->ENCAPSULATED_UNCOMPRESSED = MDC_YES;
  }else{
    bytes = (Int64)fi->number * fi->mwidth * fi->mheight * MdcType2Bytes(dicom->type);
    if ( bytes > ( (Int64)MdcTypeIntMax(BIT32_U) - 4 ) ) {
      MdcPrntWarn("DICM Writing as Encapsulated Uncompressed Little Endian (size > 4GB)");
      /* all images together do not fit in a single fragment       */
      /* write as encapsulated uncompressed explicit little endian */
      dicom->ENCAPSULATED_UNCOMPRESSED = MDC_YES;
    }
  }

  if (dicom->ENCAPSULATED_UNCOMPRESSED == MDC_YES) {
    /* now force explicit little endian */
    MDC_DICOM_WRITE_IMPLICIT = MDC_NO;
    MDC_DICOM_WRITE_NOMETA   = MDC_NO;
    MDC_FILE_ENDIAN = MDC_LITTLE_ENDIAN;
  }

  /* NM dynamic inappropriate for non-planar */
  if (dicom->modality == M_NM &&
      fi->planar == MDC_NO    &&
      fi->acquisition_type == MDC_ACQUISITION_DYNAMIC) {
    MdcPrntWarn("DICM Inappropriate for non-planar dynamic studies (NM)");
  }

  if (MDC_DICOM_WRITE_NOMETA == MDC_NO) {
    msg = MdcDicomWriteMetaHeader(fi,dicom);
    if (msg != NULL) return(msg);
  }

  msg = MdcDicomWriteG0008(fi,dicom); if (msg != NULL) return(msg);

  msg = MdcDicomWriteG0010(fi,dicom); if (msg != NULL) return(msg);

  msg = MdcDicomWriteG0018(fi,dicom); if (msg != NULL) return(msg);

  msg = MdcDicomWriteG0020(fi,dicom); if (msg != NULL) return(msg);

  msg = MdcDicomWriteG0028(fi,dicom); if (msg != NULL) return(msg);

  switch (dicom->modality) {
    case M_PT:
    default  : /* default to NM modality */
        msg = MdcDicomWriteG0054(fi,dicom); if (msg != NULL) return(msg);
  }

  msg = MdcDicomWriteG7FE0(fi,dicom); if (msg != NULL) return(msg);

  MdcCloseFile(fi->ofp);

  return NULL;

}

/**********
 * images *
 **********/

static int mdc_dicom_read(FILEINFO *fi, IMAGE **image, Uint32 *number)
{
  int err;

  dicom_init(fi->ifp);

  /* last argument: parametric(=1) (see more in vtdicom file transform.c)*/
  /* we need original values, preventing the rescaling to max/inverting  */
  /* for doing the things ourselves (centre/width or slope/intercept)    */

  err = dicom_read(fi->ipath,image,number,1);

  return(err);

}

/********
 * info *
 ********/

static void mdc_dicom_getinfo(FILEINFO *fi)
{
  ELEMENT       *e;
  DICTIONARY    *d;
  MDC_ACR_TAG   acrtag;
  MDC_SEQ_TAG   seqtag, *seq;
  /* MARK Int8 saved_file_endian = MDC_FILE_ENDIAN; */

  dicom_log(INFO,"dump_open()");

  dicom_init(fi->ifp);

  if (dicom_open(fi->ipath))
    return;

  for (;;)
  {
    e=dicom_element();
    if (!e)
      return;

    d=dicom_query(e);

    if (e->vr==UN)
      e->vr=d->vr;

    if (mdc_dicom_load(e->vr))
      return;

    acrtag.group  = e->group;
    acrtag.element= e->element;
    acrtag.length = e->length;
    acrtag.data   = (Uint8 *)e->value.UN;

    seqtag.group  = e->sqtag.group;
    seqtag.element= e->sqtag.element;

    seq = (e->sequence) ? &seqtag : NULL;

    if (acrtag.data != NULL) {

      if (mdc_dicom_skip_sequence(e) == 0) MdcDoTag(seq,&acrtag,fi,0);

      MdcFree(e->value.UN);

    }
  }
}


/********
 * open *
 ********/

static void mdc_dicom_dumpinfo(FILEINFO *fi)
{
  ELEMENT       *e;
  DICTIONARY    *d;

  dicom_log(INFO,"dump_open()");

  dicom_init(fi->ifp);

  if (dicom_open(fi->ipath))
    return;

  for (;;)
  {
    e=dicom_element();
    if (!e)
      return;

    d=dicom_query(e);

    if (e->vr==UN && d->vr!=ox) { /* replace, except for special tags */
      e->vr=d->vr;
    }

    if (dicom_load(e->vr))
      return;

    mdc_dicom_printinfo(e,d->description);

    MdcFree(e->value.UN);
  }
}


/*********
 * print *
 *********/

static void mdc_dicom_printinfo(const ELEMENT *e,const char *description)
{
  U32 i, len;

  dicom_log(INFO,"dump_print()");

  for (i=e->sequence; i; i--)
    MdcPrntScrn("  ");

  if (MDC_DICOM_VERBOSE) MdcPrntScrn("(%.4X,%.4X) %c%c[%u] "
                                     ,e->group,e->element
                                     ,e->vr>>8,e->vr&0xFF,e->vm);

  MdcPrntScrn("%s%s: ",e->encapsulated?"Encapsulated ":"",description);

  if (!e->vm)
  {
    puts("(no value)");
    return;
  }

  if (e->length == UNDEFINED_LENGTH)
  {
    puts("(undefined length)");
    return;
  }

  for (i=0; i<e->vm; i++)
    switch(e->vr)
    {
    case US :
      if (e->value.US == NULL) {
        MdcPrntWarn("Null pointer for US values");
        return;
      }
      MdcPrntScrn("%u ",e->value.US[i]);
      break;

    case SS :
      if (e->value.SS == NULL) {
        MdcPrntWarn("Null pointer for SS values");
        return;
      }
      MdcPrntScrn("%d ",e->value.SS[i]);
      break;

    case UL :
      if (e->value.UL == NULL) {
        MdcPrntWarn("Null pointer for UL values");
        return;
      }
      MdcPrntScrn("%u ",e->value.UL[i]);
      break;

    case SL :
      if (e->value.SL == NULL) {
        MdcPrntWarn("Null pointer for SL values");
        return;
      }
      MdcPrntScrn("%d ",e->value.SL[i]);
      break;

    case AT :
      if (e->value.AT == NULL) {
        MdcPrntWarn("Null pointer for AT values");
        return;
      }
      MdcPrntScrn("(%.4X,%.4X) ",e->value.AT[i].group,e->value.AT[i].element);
      break;

    case FL :
      if (e->value.FL == NULL) {
        MdcPrntWarn("Null pointer for FL values");
        return;
      }
      MdcPrntScrn("%f ",e->value.FL[i]);
      break;

    case FD :
      if (e->value.FD == NULL) {
        MdcPrntWarn("Null pointer for FD values");
        return;
      }
      MdcPrntScrn("%f ",e->value.FD[i]);
      break;

    case LT :
    case ST :
      if (e->value.LT == NULL) {
        MdcPrntWarn("Null pointer for LT values");
        return;
      }
      if (e->length > 128) {
        strcpy(mdcbufr,"...");
      }else{
        MdcGetSafeString(mdcbufr,e->value.LT,e->length,MDC_2KB_OFFSET);
      }
      MdcPrntScrn("[%s] ",mdcbufr);
      break;

    case AE :
    case AS :
    case CS :
    case DA :
    case DS :
    case DT :
    case IS :
    case LO :
    case PN :
    case SH :
    case TM :
    case UI :
      if (e->value.AE == NULL) {
        MdcPrntWarn("Null pointer for AE values");
        return;
      }
      len = strlen(e->value.AE[i]);
      if (len > 128) {
        strcpy(mdcbufr,"...");
      }else{
        MdcGetSafeString(mdcbufr,e->value.AE[i],len,MDC_2KB_OFFSET);
      }
      MdcPrntScrn("[%s] ",mdcbufr);
      break;

    default :
      MdcPrntScrn("(%u bytes)\n",e->length);
      return;
    }

  if (MDC_DICOM_VERBOSE)
    MdcPrntScrn("(%u bytes)",e->length);

  puts("");
}


void mdc_dicom_get_vr(ELEMENT *e)
{
   DICTIONARY *d;

   d = dicom_query(e);

   e->vr = d->vr;
}

Uint8 *mdc_dicom_handle_vr(ELEMENT *e, Uint8 *tdata)
{
  switch (e->vr) {
    case ox:
        if ((e->group == 0x7fe0) && (e->element == 0x0010)) {
          Int8 type; memcpy(&type,tdata,1);
          switch (type) {
            case BIT8_U : e->vr = OB; return(NULL);
            case BIT16_S: e->vr = OW; return(NULL);
          }
        }
        /* else other handle code */
        break;
    default: return(tdata); /* no special VR */

  }

  /* error exit = unhandled special VR */
  MdcPrntErr(MDC_BAD_CODE,"Internal ## Extra code required for tag %x:%x"
                         ,e->group,e->element);

  return(tdata);

}


/*********
 * write *
 *********/

int mdc_dicom_write_element(FILE *fp, Uint16 group, Uint16 element, Uint32 length, Uint8 *data)
{
  ELEMENT element_t, *e;
  Uint32 i, vr_w, length32_w;
  Uint16 length16_w;
  Int8 file_endian_saved=MDC_FILE_ENDIAN;
  Int8 MAKE_EVEN=0, DO_IMPLICIT=MDC_DICOM_WRITE_IMPLICIT;
  Uint8 *ndata=NULL;

  /* make even tags */
  if ((length%2) && (length != UNDEFINED_LENGTH)) {
    MAKE_EVEN = 1;
  }

  /* fill in the values */
  e = &element_t;
  e->group = group;
  e->element = element;
  e->length  = length + MAKE_EVEN;
  length32_w = e->length; length16_w = (Uint16)e->length;

  /* default transfer = explicit */
  if (DO_IMPLICIT == MDC_YES) {
    /* only implicit VR little */
    MDC_FILE_ENDIAN = MDC_LITTLE_ENDIAN;
  }

  /* meta group must be explicit VR little */
  if (e->group == 0x0002) {
    MDC_FILE_ENDIAN = MDC_LITTLE_ENDIAN;
    DO_IMPLICIT = MDC_NO;
  }

  /* fix endian of tag items to write */
  MdcSWAP(group); MdcSWAP(element);
  MdcSWAP(length16_w); MdcSWAP(length32_w);

  /* write group */
  fwrite((Uint8 *)&group,1,sizeof(e->group),fp);
  /* write element */
  fwrite((Uint8 *)&element,1,sizeof(e->element),fp);
  /* write value representation & length */
  mdc_dicom_get_vr(e);
  /* handle special VR values */
  ndata = mdc_dicom_handle_vr(e,data);
  vr_w = e->vr;
  if (MdcHostBig()) vr_w = (vr_w << 16);
  else MdcForceSwap((Uint8 *)&vr_w,2);
  switch (e->vr) {
    case OB :
    case OW :
    case SQ :
    case UN :
    case UT :
        if (DO_IMPLICIT == MDC_YES) {
          fwrite((Uint8 *)&length32_w,1,4,fp);
        }else{
          if (e->group != 0xfffe) fwrite((Uint8 *)&vr_w,1,4,fp);
          fwrite((Uint8 *)&length32_w,1,4,fp);
        }
        break;
    case AT : /* 2 bytes endian sensitive data */
    case SS :
    case US :
        if (DO_IMPLICIT == MDC_YES) {
          fwrite((Uint8 *)&length32_w,1,4,fp);
        }else{
          fwrite((Uint8 *)&vr_w,1,2,fp);
          fwrite((Uint8 *)&length16_w,1,2,fp);
        }
        e->vm = length >> 1;
        for (i=0; i<e->vm; i++) MdcSwapBytes(ndata+(i<<1),2);
        break;
    case FL : /* 4 bytes endian sensitive data */
    case SL :
    case UL :
        if (DO_IMPLICIT == MDC_YES) {
          fwrite((Uint8 *)&length32_w,1,4,fp);
        }else{
          fwrite((Uint8 *)&vr_w,1,2,fp);
          fwrite((Uint8 *)&length16_w,1,2,fp);
        }
        e->vm = length >> 2;
        for (i=0; i<e->vm; i++) MdcSwapBytes(ndata+(i<<2),4);
        break;
    case FD : /* 8 bytes endian sensitive data */
        if (DO_IMPLICIT == MDC_YES) {
          fwrite((Uint8 *)&length32_w,1,4,fp);
        }else{
          fwrite((Uint8 *)&vr_w,1,2,fp);
          fwrite((Uint8 *)&length16_w,1,2,fp);
        }
        e->vm = length >> 3;
        for (i=0; i<e->vm; i++) MdcSwapBytes(ndata+(i<<3),8);
        break;
    default :
        if (DO_IMPLICIT == MDC_YES) {
          fwrite((Uint8 *)&length32_w,1,4,fp);
        }else{
          fwrite((Uint8 *)&vr_w,1,2,fp);
          fwrite((Uint8 *)&length16_w,1,2,fp);
        }
  }
  /* write value data */
  if ((ndata != NULL) && (length != 0) && (length != UNDEFINED_LENGTH)) {
    fwrite((Uint8 *)ndata,1,length,fp);
    if (MAKE_EVEN) {
      switch (e->vr) {
        case UI:
          fputc('\0',fp); break;
        default:
          fputc(' ',fp);
      }
    }
  }
  /* restore original output file endian */
  MDC_FILE_ENDIAN = file_endian_saved;

  if (ferror(fp)) return(MDC_NO);

  return(MDC_YES);

}

