#include <Rcpp.h>
// Copyright (c) 2005-2008 ASCLEPIOS Project, INRIA Sophia-Antipolis (France)
// All rights reserved.
//
// This file is part of the ImageIO Library, and as been adapted for CGAL (www.cgal.org).
//
// $URL: https://github.com/CGAL/cgal/blob/v5.6.1/CGAL_ImageIO/include/CGAL/ImageIO/iris_impl.h $
// $Id: iris_impl.h 0779373 2020-03-26T13:31:46+01:00 Sébastien Loriot
// SPDX-License-Identifier: LGPL-3.0-or-later
//
//
// Author(s)     :  ASCLEPIOS Project (INRIA Sophia-Antipolis), Laurent Rineau

/*
 * xviris.c - load routine for IRIS 'rgb' format pictures
 *
 * LoadIRIS()
 * WriteIRIS()
 */

#ifdef CGAL_HEADER_ONLY
#define CGAL_INLINE_FUNCTION inline
#else
#define CGAL_INLINE_FUNCTION
#endif

#include <stdio.h>
#include <string.h>
#include <math.h>



/** Magic header for RGB files */
#define IRIS_MAGIC 0732



typedef unsigned char byte;

#define BPPMASK                        0x00ff
#define ITYPE_VERBATIM                0x0000
#define ITYPE_RLE                0x0100
#define ISRLE(type)                (((type) & 0xff00) == ITYPE_RLE)
#define ISVERBATIM(type)        (((type) & 0xff00) == ITYPE_VERBATIM)
#define BPP(type)                ((type) & BPPMASK)
#define RLE(bpp)                (ITYPE_RLE | (bpp))
#define VERBATIM(bpp)                (ITYPE_VERBATIM | (bpp))






#define TAGLEN        (5)

#define RINTLUM (79)
#define GINTLUM (156)
#define BINTLUM (21)

#define OFFSET_R        3        /* this is byte order dependent */
#define OFFSET_G        2
#define OFFSET_B        1
#define OFFSET_A        0

#define ILUM(r,g,b)     ((int)(RINTLUM*(r)+GINTLUM*(g)+BINTLUM*(b))>>8)
#define CHANOFFSET(z)        (3-(z))        /* this is byte order dependent */

static byte    *getimagedata  (const _image *im, unsigned short, int, int, int);
static void     interleaverow (byte *, byte *, int, int);
static void     expandrow     (byte *, byte *, int);
static void     readtab       (const _image *im, unsigned long *, int);
static void     addimgtag     (byte *, int, int);


static unsigned short  getshort      (const _image *im);
static unsigned long   getlong       (const _image *im);


CGAL_INLINE_FUNCTION
int testIrisHeader(char *magic,const char *) {
  if((((unsigned char *)magic)[0]<<8) + ((unsigned char *)magic)[1]
     == IRIS_MAGIC)
    return 0;
  else
    return -1;
}

CGAL_INLINE_FUNCTION
PTRIMAGE_FORMAT createIrisFormat() {
  PTRIMAGE_FORMAT f=(PTRIMAGE_FORMAT) ImageIO_alloc(sizeof(IMAGE_FORMAT));

  f->testImageFormat=&testIrisHeader;
  f->readImageHeader=&readIrisImage;
  f->writeImage=0;
  strcpy(f->fileExtension,".rgb");
  strcpy(f->realName,"IRIS");
  return f;
}



/******************************************/
static void interleaverow(byte *lptr, byte *cptr, int z, int n)
{
  lptr += z;
  while(n--) {
    *lptr = *cptr++;
    lptr += 4;
  }
}


/******************************************/
static void expandrow(byte *optr, byte *iptr, int z)
{
  byte pixel, count;

  optr += z;
  while (1) {
    pixel = *iptr++;
    if ( !(count = (pixel & 0x7f)) ) return;
    if (pixel & 0x80) {
      while (count>=8) {
        optr[0*4] = iptr[0];
        optr[1*4] = iptr[1];
        optr[2*4] = iptr[2];
        optr[3*4] = iptr[3];
        optr[4*4] = iptr[4];
        optr[5*4] = iptr[5];
        optr[6*4] = iptr[6];
        optr[7*4] = iptr[7];
        optr += 8*4;
        iptr += 8;
        count = byte(count - 8);
      }
      while(count--) {
        *optr = *iptr++;
        optr+=4;
      }
    }
    else {
      pixel = *iptr++;
      while(count>=8) {
        optr[0*4] = pixel;
        optr[1*4] = pixel;
        optr[2*4] = pixel;
        optr[3*4] = pixel;
        optr[4*4] = pixel;
        optr[5*4] = pixel;
        optr[6*4] = pixel;
        optr[7*4] = pixel;
        optr += 8*4;
        count = byte(count - 8);
      }
      while(count--) {
        *optr = pixel;
        optr+=4;
      }
    }
  }
}

/****************************************************/
static void readtab(const _image *im, unsigned long *tab, int n)
{
  while (n) {
    *tab++ = getlong(im);
    n--;
  }
}


/*****************************************************/
static void addimgtag(byte *dptr, int xsize, int ysize)
{
  /* this is used to extract image data from core dumps.
     I doubt this is necessary...  --jhb */

  dptr    = dptr + (xsize * ysize * 4);
  dptr[0] = 0x12;  dptr[1] = 0x34;  dptr[2] = 0x56;  dptr[3] = 0x78;
  dptr += 4;

  dptr[0] = 0x59;  dptr[1] = 0x49;  dptr[2] = 0x33;  dptr[3] = 0x33;
  dptr += 4;

  dptr[0] = 0x69;  dptr[1] = 0x43;  dptr[2] = 0x42;  dptr[3] = 0x22;
  dptr += 4;

  dptr[0] = byte((xsize>>24)&0xff);
  dptr[1] = byte((xsize>>16)&0xff);
  dptr[2] = byte((xsize>> 8)&0xff);
  dptr[3] = byte((xsize    )&0xff);
  dptr += 4;

  dptr[0] = byte((ysize>>24)&0xff);
  dptr[1] = byte((ysize>>16)&0xff);
  dptr[2] = byte((ysize>> 8)&0xff);
  dptr[3] = byte((ysize    )&0xff);
}

/* byte order independent read/write of shorts and longs. */
/*****************************************************/
static unsigned short getshort( const _image *im)
{
  byte buf[2] = { '\0', '\0' };
  ImageIO_read( im, buf, (size_t) 2);
  return (unsigned short)((buf[0]<<8)+(buf[1]<<0));
}

/*****************************************************/
static unsigned long getlong( const _image *im )
{
  byte buf[4] = { '\0', '\0', '\0', '\0' };
  ImageIO_read( im, buf, (size_t) 4);
  return (((unsigned long) buf[0])<<24) + (((unsigned long) buf[1])<<16)
       + (((unsigned long) buf[2])<<8) + buf[3];
}

/*****************************************************/
CGAL_INLINE_FUNCTION
int readIrisImage( const char *, _image *im ) {
  byte   *rawdata, *rptr;
  byte   *pic824,  *bptr, *iptr;
  std::size_t i, j, size;
  unsigned short imagic, type;
  unsigned int xsize, ysize, zsize;


  /* read header information from file */
  imagic = getshort( im );
  type   = getshort( im );
  getshort( im );
  xsize  = getshort( im );
  ysize  = getshort( im );
  zsize  = getshort( im );

  if ( ImageIO_error(im) ) return( 0 );

  if (imagic != IRIS_MAGIC) return( 0 );

  rawdata = getimagedata(im, type, xsize, ysize, zsize);
  if (!rawdata) return( 0 );

  if ( ImageIO_error(im) ) return( 0 );   /* probably truncated file */


  /*  t1=texture_alloc();
  (void) strcpy(t1->name,fname);
  t1->type=IRIS;

  t1->nrows = ysize;
  t1->ncols = xsize;

  t1->image = create_int_array(t1->nrows,t1->ncols);*/

  /* got the raw image data.  Convert to an XV image (1,3 bytes / pix) */
  if (zsize < 3)
    {  /* grayscale */
      im->xdim = xsize;
      im->ydim = ysize;
      im->zdim = 1;
      im->vdim = 1;
      im->wdim = 1;
      im->wordKind = WK_FIXED;
      im->sign = SGN_UNSIGNED;
      im->data = ImageIO_alloc(xsize * ysize);

      pic824 = (byte *) ImageIO_alloc((size_t) xsize * ysize);
      if (!pic824) exit(-1);

      /* copy plane 3 from rawdata into pic824, inverting pic vertically */
      for (i = 0, bptr = pic824; i < ysize; i++)
        {
          rptr = rawdata + 3 + ((ysize - 1) - i) * (xsize * 4);
          for (j = 0; j < xsize; j++, bptr++, rptr += 4)
            *bptr = *rptr;
        }


      size = im->xdim * im->ydim;
      for (bptr = pic824, iptr = (unsigned char *) im->data,
             i = 0; i < size; ++i, ++iptr, ++bptr)
        {
          *iptr = *bptr;
        }

      ImageIO_free(pic824);

  }

  else {  /* truecolor */
    im->xdim = xsize;
    im->ydim = ysize;
    im->zdim = zsize / 3;
    im->vdim = 4;
    im->wdim = 1;
    im->wordKind = WK_FIXED;
    im->sign = SGN_UNSIGNED;
    im->data = ImageIO_alloc(xsize * ysize * im->zdim * 4);

    pic824 = (byte *) ImageIO_alloc((size_t) xsize * ysize * 3);
    if (!pic824)
      exit(1);

    /* copy plane 3 from rawdata into pic824, inverting pic vertically */
    for (i = 0, bptr = pic824; i< ysize; i++)
      {
        rptr = rawdata + ((ysize - 1) - i) * (xsize * 4);

        for (j=0; j< xsize; j++, rptr += 4) {
          *bptr++ = rptr[3];
          *bptr++ = rptr[2];
          *bptr++ = rptr[1];
        }
      }


    size = im->xdim * im->ydim;
    for (bptr = pic824, iptr = (unsigned char *) im->data, i = 0;
         i < size; ++i, iptr += 4, bptr += 3)
      {
        if ( _getEndianness() == END_LITTLE ) {
          iptr[0] = 0xFF;
          iptr[1] = bptr[2];
          iptr[2] = bptr[1];
          iptr[3] = bptr[0];
        }
        else {
          iptr[0] = bptr[0];
          iptr[1] = bptr[1];
          iptr[2] = bptr[2];
          iptr[3] = 0xFF;
        }
      }
      ImageIO_free(pic824);
  }

  ImageIO_free(rawdata);

  return 1;
}




/****************************************************/
static byte *getimagedata(const _image *im, unsigned short type, int xsize, int ysize,
                          int zsize)
{
  /* read in a B/W RGB or RGBA iris image file and return a
     pointer to an array of 4-byte pixels, arranged ABGR, nullptr on error */

  byte   *base, *lptr;
  byte   *verdat;
  int     y, z, tablen;
  int     bpp, rle, badorder;
  int     rlebuflen;
  byte *rledat;
  unsigned long *starttab, *lengthtab, cur;


  rle     = ISRLE(type);
  bpp     = BPP(type);

  if (bpp != 1) {
    return (byte *) nullptr;
  }

  if (rle) {

    rlebuflen = 2 * xsize + 10;
    tablen    = ysize * zsize;
    starttab  = (unsigned long *) ImageIO_alloc((size_t) tablen * sizeof(long));
    lengthtab = (unsigned long *) ImageIO_alloc((size_t) tablen * sizeof(long));
    rledat    = (byte *)   ImageIO_alloc((size_t) rlebuflen);

    if (!starttab || !lengthtab || !rledat)
      exit(1);

    ImageIO_seek( im, 512L, SEEK_SET );
    readtab(im, starttab,  tablen);
    readtab(im, lengthtab, tablen);

    if ( ImageIO_error(im) ) {
      ImageIO_free(starttab);  ImageIO_free(lengthtab);  ImageIO_free(rledat);
      return (byte *) nullptr;
    }


    /* check data order */
    cur = 0;
    badorder = 0;
    for (y=0; y<ysize && !badorder; y++) {
      for (z=0; z<zsize && !badorder; z++) {
        if (starttab[y+z*ysize] < cur) badorder = 1;
        else cur = starttab[y+z*ysize];
      }
    }

    ImageIO_seek( im, (long) (512 + 2*tablen*4), 0);

    cur = 512 + 2*tablen*4;

    base = (byte *) ImageIO_alloc((size_t) (xsize*ysize+TAGLEN) * 4);
    if (!base)
      exit(1);

    addimgtag(base,xsize,ysize);

    if (badorder) {
      for (z=0; z<zsize; z++) {
        lptr = base;
        for (y=0; y<ysize; y++) {
          if (cur != starttab[y+z*ysize]) {
            ImageIO_seek( im, (long) starttab[y+z*ysize], 0);
            cur = starttab[y+z*ysize];
          }

          if (lengthtab[y+z*ysize]>(unsigned long)rlebuflen) {
            ImageIO_free(starttab); ImageIO_free(lengthtab); ImageIO_free(rledat); ImageIO_free(base);
            return (byte *) nullptr;
          }
          ImageIO_read(im, rledat, (size_t) lengthtab[y+z*ysize]);
          cur += lengthtab[y+z*ysize];
          expandrow(lptr,rledat,3-z);
          lptr += (xsize * 4);
        }
      }
    }
    else {
      lptr = base;
      for (y=0; y<ysize; y++) {
        for (z=0; z<zsize; z++) {
          if (cur != starttab[y+z*ysize]) {
            ImageIO_seek( im, (long) starttab[y+z*ysize], 0);
            cur = starttab[y+z*ysize];
          }

          ImageIO_read(im, rledat, (size_t) lengthtab[y+z*ysize]);
          cur += lengthtab[y+z*ysize];
          expandrow(lptr,rledat,3-z);
        }
        lptr += (xsize * 4);
      }
    }

    ImageIO_free(starttab);
    ImageIO_free(lengthtab);
    ImageIO_free(rledat);
    return base;
  }      /* end of RLE case */

  else {  /* not RLE */
    verdat = (byte *) ImageIO_alloc((size_t) xsize);
    base   = (byte *) ImageIO_alloc((size_t) (xsize*ysize+TAGLEN) * 4);
    if (!base || !verdat)
      exit(1);

    addimgtag(base,xsize,ysize);

    ImageIO_seek( im, 512L,0);

    for (z=0; z<zsize; z++) {
      lptr = base;
      for (y=0; y<ysize; y++) {
        ImageIO_read(im, verdat, (size_t) xsize);
        interleaverow(lptr,verdat,3-z,xsize);
        lptr += (xsize * 4);
      }
    }

    ImageIO_free(verdat);
    return base;
  }
}

















