/*****************************
 * libdicom - MDC extensions *
 *****************************/
/*
 *
 */

/*
 * Contributions by Jaslet Bertrand
 *
 * for handling encapsulated pixeldata:
 *
 *  - RLE
 *
 *  - LossLess JPEG
 *
 *  - Uncompressed Explicit Little
 *
 */

#include <string.h>
#include "dicom.h"

#ifdef MDC_SUPPORT_LJPG
 #include "jpeg.h"
 #include "jpegless.h"
#endif

#define MDC_MAX_RLE_SEGMENTS  4L   /* max of 32 bits (ARGB) images supported */

/*******
 * RLE *
 *******/

S16 mdc_dicom_decodeRLE_segment(U16 *, U8 *, U32, U32, U32, U32);

/*
 * gets and decode an RLE pixel data element
 * return : the image 
 */
S16 mdc_dicom_decomp_rle(FILE *fp, SINGLE *s, ELEMENT *e)
{
  U16 *image16 = (U16 *)e->value.OB;
  U32 length = e->length;
  U32 bytes = s->w*s->h*s->samples*s->alloc>>3;

  U32 numberSegments, i;
  U8  *rle;
  long offset[MDC_MAX_RLE_SEGMENTS + 1], rlelen, skip;
  int r;

  dicom_log(DEBUG,"mdc_dicom_decomp_rle()");

  /* for each image we have:                                       */
  /* 0xFFFE 0xE000 length RLE_header RLE_segment1 RLE_segment2 ... */
  /* length is 4 bits / image                                      */

  if (fp == NULL || s == NULL || e == NULL) {
    dicom_log(ERROR,"RLE - Invalid NULL argument");
    return(-1);
  }

  image16 = (U16 *)e->value.OB;
  length  = e->length;
  bytes   = s->w*s->h*s->samples*s->alloc>>3;

  if (image16 == NULL || length == 0 || bytes == 0) {
    dicom_log(ERROR,"RLE - Invalid parameter");
    return(-2);
  }

  /* read 4 chars from the file = number of segments */
  r = fread(&numberSegments,4,1,fp);
  if (r != 1) {
    dicom_check(-1);
    dicom_log(ERROR,"RLE - Failure numberSegments");
    return(-3);
  }
  dicom_swap(&numberSegments,4);
  if (numberSegments > MDC_MAX_RLE_SEGMENTS)
  {
    dicom_log(ERROR,"RLE - Maximum of 32 bits images supported");
    return(-4); /* allow 8, 16, 24 & 32 bits images, 8 bits per segment */
  }

  /* read offset0, offset1, offset2, ... */
  for (i=0; i < numberSegments; i++) {
     r = fread(&offset[i],4,1,fp);
     if (r != 1) {
       dicom_check(-1);
       dicom_log(ERROR,"RLE - Failure offsets");
       return(-5);
     }
     dicom_swap(&offset[i],4);
  }
  /* skip rest of header */
  skip = 60 - (numberSegments * 4);
  fseeko(fp, (off_t)skip, SEEK_CUR);
  if (dicom_check(-1))
  {
    dicom_log(ERROR,"RLE - Failure header skip");
    return(-6);
  }

  offset[numberSegments] = length; /* needed for offset last segment */

  /* read all segments */
  for (i=0; i < numberSegments; i++)
  {
    /* read rle image */
    rlelen = offset[i+1] - offset[i];
    rle    = (U8*)malloc((U32)(rlelen + 10L));
    if (rle)
    {
      /* extract the image from the file  */
      r = fread((void *)rle, (unsigned)rlelen, 1L, fp);
      if (r != 1) {
        dicom_check(-1);
        dicom_log(ERROR,"RLE - Failure image read");
        return(-7);
      }

      /* decode segment */
      r = mdc_dicom_decodeRLE_segment(image16, rle, bytes, (unsigned)rlelen, numberSegments, i);

      /* delete buffer */
      free(rle);

      /* check result */
      if (r != 0) {
        dicom_check(-1);
        dicom_log(ERROR,"RLE - Failure to decompress");
        return(-8);
      }
    }
    else
    {
      dicom_log(ERROR,"RLE - Out of memory");
      return(-9);
    }
  }

  return(0);

}

/* 
 *  decode a RLE segment
 *  image  : pointer on real image (8 or 16 bits)
 *  rle    : pointer on rle buffer (8bits)
 *  length : length of rle buffer
 *  segtot : total number of segments
 *  segnb  : number of current segment (zero based !)
 */
S16 mdc_dicom_decodeRLE_segment(U16 *image, U8 *rle, U32 bytes, U32 length, U32 segtot, U32 segnb)
{
  U32  j, indj;
  U8   *pix, val;
  U16  code; /* prevent warning: actually signed char >=128) = (256-code) */
  U16  ii, iimax;

  dicom_log(DEBUG,"mdc_dicom_decodeRLE_segment()");

  /* convert rle into real image      */
  pix  = (U8*) image;
  /* initial start number, zero based */
  /* segment 1st=0, 2nd=1, 3rd=2, ... */
  indj = segnb;
  for (j = 0L; j < length; )
  {

    code = (U16) rle [j];
    j++; /* yes, I know but do not move it */
    /* sequence of different bytes */
    if (code == 0)
    {
      if (indj > bytes) break; /* zero padding case */

      /* handle */
      if (j < length - 1)
        pix [indj] = rle [j++];
      indj += segtot;
    }
    else if ((code > 0) && (code <= 127))
    {
      /* check read overflow: incomplete stream, encoder bug */
      if ((j + code) > length) {
        dicom_log(ERROR,"RLE - segment: underflow for code < 128");
        return(-1);
      }
      /* check write overflow: encoder bug */
      if ((indj + (code*segtot)) > bytes) {
        dicom_log(ERROR,"RLE - segment: overflow for code < 128");
        return(-2);
      }
      /* handle */
      for (ii = 0; ii < (code + 1); ii++)
      {
         if (j == length) break;
         pix [indj] = rle [j++];
         indj += segtot;
      }
    }
    /* repetition of the same byte */
    else if ((code <= 255) && (code > 128))
    {
      /* check read overflow: incomplete stream, encoder bug */
      if ((j + 1) > length) {
        dicom_log(ERROR,"RLE - segment: underflow for code > 128");
        return(-3);
      }
      /* check write overflow: encoder bug */
      if ((indj + (256 - code)*segtot) > bytes) {
        dicom_log(ERROR,"RLE - segment: overflow for code > 128");
        return(-4);
      }
      /* handle */
      val = rle [j++];
      iimax = 256-code;
      for (ii = 0; ii <= iimax; ii++)
      {
        pix [indj] = val;
        indj += segtot;
      }
    }
    else if (code == 128)
    {
      /* illegal value as per the DICOM standard */
      dicom_log(ERROR,"RLE - segment: illegal code = 128");
      return(-5);
    }
  } /* for */

  return(0);
}

/*****************
 * LossLess JPEG *
 *****************/

S16 mdc_dicom_decomp_ljpg(FILE *fp, SINGLE *s, ELEMENT *e)
{
#if MDC_SUPPORT_LJPG

  U16 *image16;
  signed int length, depth;

  if (fp == NULL || s == NULL || e == NULL) {
    dicom_log(ERROR,"LJPG - Invalid NULL argument");
    return(-1);
  }

  image16 = (U16 *)e->value.OB;
  length  = (signed int)e->length;
  depth   = (signed int)s->alloc*s->samples;

  if (image16 == NULL || length <= 0 || depth <= 0) {
    dicom_log(ERROR,"LJPG - Invalid parameter");
    return(-2);
  }

  return(JPEGLosslessDecodeImage(fp,image16,depth,length));

#else

  return(-3);

#endif
}

/***********************
 * Uncompressed Little *
 ***********************/

S16 mdc_dicom_decomp_uncomp_little(FILE *fp, SINGLE *s, ELEMENT *e)
{
  size_t r;
  unsigned width, height, pixel, bytes;
  U8 *uncomp=NULL;

  dicom_log(DEBUG,"mdc_dicom_decomp_uncomp_little()");

  /* aka dicom_load() */
  uncomp = malloc(e->length); /* could be padded to even */
  if (uncomp == NULL) {
    dicom_log(ERROR,"Couldn't allocate uncomp buffer");
    return(-1);
  }

  r = fread(uncomp,1,(size_t)e->length,fp);
  if (r != (size_t)e->length) {
    if (dicom_check(0)) {
      free(uncomp);
      return(-2);
    }
  }

  width = (unsigned) s->w;
  height= (unsigned) s->h;
  pixel = (unsigned) s->samples*s->alloc>>3;
  bytes = width * height * pixel;

  if (e->value.OB != NULL) {
    memcpy(e->value.OB,uncomp,bytes); /* just copy required bytes */
  }else{
    dicom_log(ERROR,"Missing memory location for uncomp buffer");
    free(uncomp);
    return(-3);
  }

  free(uncomp);

  return(0);
}

