#include "mrilib.h"
#include <signal.h>

#undef  QBUF
#define QBUF 4096

MRI_IMAGE * mri_read_stuff( char *fname )
{
   static int first=1 ;
   static char *jpeg_filter = NULL ;  /* djpeg     */
   static char *gif_filter  = NULL ;  /* giftopnm  */
   static char *tiff_filter = NULL ;  /* tifftopnm */
   static char *bmp_filter  = NULL ;  /* bmptoppm  */
   static char *png_filter  = NULL ;  /* pngtopnm  */
   static char *pnm_filter  = NULL ;  /* cat */
   static char *heic_filter = NULL ;  /* magick [10 Feb 2020] */

   char *pg , *pg2 , *mg , *filt=NULL ;
   int nf , nbuf , ipos , nx,ny,maxval=255 , bper,nbim, pbm=0 ;
   FILE *fp ;
   MRI_IMAGE *im ;
   byte *imar , *buf ;
   char *qs , *qd , *qname , *qq ;

ENTRY("mri_read_stuff") ;

   /*--- check input for OK-ness ---*/

   if( fname == NULL || *fname == '\0' ) RETURN(NULL) ;

   /*--- first time in, setup up filters to PNM format ---*/

   if( first ){
     first = 0 ;

     pg = THD_find_executable( "cat" ) ;    /* cheap, but works */
     if( pg != NULL ){
       pnm_filter = AFMALL(char, strlen(pg)+32) ;
       sprintf( pnm_filter , "%s %%s" , pg ) ;
     }

     pg = THD_find_executable( "djpeg" ) ;
     if( pg != NULL ){
       jpeg_filter = AFMALL(char, strlen(pg)+32) ;
       sprintf( jpeg_filter , "%s %%s" , pg ) ;
     }

     pg = THD_find_executable( "giftopnm" ) ;
     if( pg != NULL ){
       gif_filter = AFMALL(char, strlen(pg)+32) ;
       sprintf( gif_filter , "%s %%s" , pg ) ;
     }

     pg = THD_find_executable( "tifftopnm" ) ;
     if( pg != NULL ){
       tiff_filter = AFMALL(char, strlen(pg)+32) ;
       sprintf( tiff_filter , "%s %%s" , pg ) ;
     }

     pg = THD_find_executable( "bmptoppm" ) ;
     if( pg != NULL ){
       bmp_filter = AFMALL(char, strlen(pg)+32) ;
       sprintf( bmp_filter , "%s %%s" , pg ) ;
     }

     pg = THD_find_executable( "pngtopnm" ) ;
     if( pg != NULL ){
       png_filter = AFMALL(char, strlen(pg)+32) ;
       sprintf( png_filter , "%s %%s" , pg ) ;
     }

     /* this filter can do lots of format conversion [10 Feb 2020] */

     mg = THD_find_executable( "magick" ) ;
     if( mg != NULL ){
       heic_filter = AFMALL(char,strlen(mg)+32) ;
       sprintf( heic_filter , "%s %%s ppm:-" , mg ) ;
       if( png_filter  == NULL ) png_filter  = strdup(heic_filter) ;
       if( tiff_filter == NULL ) tiff_filter = strdup(heic_filter) ;
       if( jpeg_filter == NULL ) jpeg_filter = strdup(heic_filter) ;
       if( gif_filter  == NULL ) gif_filter  = strdup(heic_filter) ;
       if( bmp_filter  == NULL ) bmp_filter  = strdup(heic_filter) ;
     }
   }

   /*--- determine filter based on file suffix ---*/

   nf = strlen(fname) ;
   if( nf < 5 ) RETURN(NULL);  /* filename too short! */

   pg  = fname + (nf-4);       /* points to last 4 chars */
   pg2 = pg - 1;               /* points to last 5 chars */

        if( strcmp(pg ,".jpg" ) == 0 ||
            strcmp(pg ,".JPG" ) == 0 ||
            strcmp(pg2,".jpeg") == 0 ||
            strcmp(pg2,".JPEG") == 0   ) filt = jpeg_filter ;

   else if( strcmp(pg ,".gif" ) == 0 ||
            strcmp(pg ,".GIF" ) == 0   ) filt = gif_filter  ;

   else if( strcmp(pg ,".pbm" ) == 0 ||
            strcmp(pg ,".PBM" ) == 0 ||
            strcmp(pg ,".pgm" ) == 0 ||
            strcmp(pg ,".PGM" ) == 0 ||
            strcmp(pg ,".ppm" ) == 0 ||
            strcmp(pg ,".PPM" ) == 0   ) filt = pnm_filter  ;

   else if( strcmp(pg ,".tif" ) == 0 ||
            strcmp(pg ,".TIF" ) == 0 ||
            strcmp(pg2,".tiff") == 0 ||
            strcmp(pg2,".TIFF") == 0   ) filt = tiff_filter ;

   else if( strcmp(pg ,".bmp" ) == 0 ||
            strcmp(pg ,".BMP" ) == 0   ) filt = bmp_filter  ;

   else if( strcmp(pg ,".png" ) == 0 ||
            strcmp(pg ,".PNG" ) == 0   ) filt = png_filter  ;

   else if( strcmp(pg2,".heic") == 0 ||
            strcmp(pg2,".HEIC") == 0   ) filt = heic_filter  ;

   if( filt == NULL ) RETURN(NULL);  /* didn't match, or no filter */

   /*--- create the filter for this file and open the pipe ---*/

   pg = AFMALL(char, nf+strlen(filt)+32) ;  /* string to hold filter */

   qs = strchr(fname,'\'') ;
   qd = strchr(fname,'\"') ;
   if( qs == NULL || qd == NULL ){  /* 22 Dec 2005: quotize name */
     qq    = (qs==NULL) ? "'" : "\"" ;
     qname = (char *)malloc(sizeof(char)*(nf+8)) ;
     strcpy(qname,qq); strcat(qname,fname); strcat(qname,qq);
     sprintf( pg , filt , qname ) ; free((void *)qname) ;
   } else {
     sprintf( pg , filt , fname ) ;
   }

#ifndef CYGWIN
   signal( SIGPIPE , SIG_IGN ) ;  /* ignore this signal */
#endif
   fp = popen( pg , "r" ) ;
   if( fp == NULL ){ free(pg); RETURN(NULL); }  /* bad pipe */

   buf = AFMALL(byte, QBUF) ;  /* read buffer for initial data from pipe */

   /*--- read 1st block from pipe ---*/

   nbuf = fread( buf , 1 , QBUF , fp ) ;

   if( nbuf < 16 ){  /* bad read */
     free(buf); free(pg); pclose(fp); RETURN(NULL);
   }

   if( buf[0] != 'P' ){  /* not a P?M file */
     free(buf); free(pg); pclose(fp); RETURN(NULL);
   }

        if( buf[1] == '6' ) bper = 3 ;              /* PPM from pipe */
   else if( buf[1] == '5' ) bper = 1 ;              /* PGM from pipe */
   else if( buf[1] == '4' ){bper = 1 ; pbm=1; }     /* PBM from pipe */
   else {
     free(buf); free(pg); pclose(fp); RETURN(NULL); /* bad bad bad!! */
   }

   ipos = 2 ;  /* start scanning for PNM header stuff at position 2 in buf */

   /* skip comment lines in the buffer */

#undef  SKIPCOM
#define SKIPCOM                                                     \
 { if(buf[ipos]=='#')                                               \
     do{ ipos++; } while( ipos<nbuf && buf[ipos]!='\n' ) ; }

   /* find an ASCII number in the buffer */

#undef  NUMSCAN
#define NUMSCAN(var)                                                \
{ SKIPCOM ;                                                         \
  while( ipos<nbuf && !isdigit(buf[ipos]) ){ipos++; SKIPCOM;}       \
  if( ipos >= nbuf ){ var = -1; }                                   \
  else {                                                            \
    int nch; char chb[32];                                          \
    for( nch=0 ; ipos<nbuf && isdigit(buf[ipos]) ; nch++,ipos++ ){  \
      chb[nch] = buf[ipos]; }                                       \
    chb[nch]='\0'; var = strtol(chb,NULL,10);                       \
  } }

  /* scan for the nx variable */

  NUMSCAN(nx) ;
  if( nx < 2 || ipos >= nbuf ){                      /* bad */
    free(buf); free(pg); pclose(fp); RETURN(NULL);
  }

  /* scan for the ny variable */

  NUMSCAN(ny) ;
  if( ny < 2 || ipos >= nbuf ){                      /* bad */
    free(buf); free(pg); pclose(fp); RETURN(NULL);
  }

  /* scan for the maxval variable */

  if( !pbm ){
    NUMSCAN(maxval) ;
    if( maxval <= 0 || maxval > 255 || ipos >= nbuf ){ /* bad */
      free(buf); free(pg); pclose(fp); RETURN(NULL);
    }
  }

  ipos++ ;   /* skip byte after maxval;                   */
             /* ipos now points at 1st byte of image data */

  /*--- create output image struct  ---*/

  if( bper == 3 ){                        /* PPM */
    im   = mri_new( nx , ny , MRI_rgb ) ;
    imar = MRI_RGB_PTR(im) ;
  } else {                                /* PGM or PBM */
    im   = mri_new( nx , ny , MRI_byte ) ;
    imar = MRI_BYTE_PTR(im) ;
  }
  mri_add_name( fname , im ) ;
  nbim = bper * nx * ny ;        /* num bytes in image array imar */

  /*--- copy remaining data in buf (if any) to image array ---*/

  nbuf = nbuf - ipos ;             /* num bytes left in buf */
  if( nbuf > nbim ) nbuf = nbim ;  /* but don't want too much */
  if( nbuf > 0 )
    memcpy( imar , buf+ipos , nbuf ) ;

  free(buf) ;     /* have used this up now */

  /*--- read rest of image array directly from pipe ---*/

  if( nbuf < nbim )
    fread( imar+nbuf , 1 , nbim-nbuf , fp ) ;

  free(pg) ; pclose(fp) ;  /* toss out the trash */

  /*--- if was really a PBM image, inflate to PGM now ---*/

  if( pbm ) mri_inflate_pbm( im ) ;  /* 02 Jan 2003 */

  /*--- if maxval < 255, scale byte data up to that level ---*/

  if( maxval < 255 ){
    int ii ; float fac = 255.4/maxval ;
    for( ii=0 ; ii < nbim ; ii++ ) imar[ii] = (byte)( imar[ii]*fac ) ;
  }

  /*--- vamoose the ranch ---*/

  RETURN(im);
}

/*--------------------------------------------------------------------------*/
/*! Inflate data from PBM to PGM, in place.
----------------------------------------------------------------------------*/

void mri_inflate_pbm( MRI_IMAGE *im )  /* 02 Jan 2002 */
{
   MRI_IMAGE *qim ;
   byte *qimar , *imar ;
   int ii,jj , nx,ny , nbrow , i8 ;
   byte bmask[8] = { 1<<7 , 1<<6 , 1<<5 , 1<<4 , 1<<3 , 1<<2 , 1<<1 , 1 } ;

ENTRY("mri_inflate_pbm") ;

   if( im == NULL || im->kind != MRI_byte ) EXRETURN ;

   nx = im->nx ; ny = im->ny ;
   qim   = mri_new( nx , ny , MRI_byte ) ;
   qimar = MRI_BYTE_PTR(qim) ;
   imar  = MRI_BYTE_PTR(im) ;

   nbrow = nx/8 ; if( 8*nbrow < nx ) nbrow++ ;

   for( jj=0 ; jj < ny ; jj++ )
     for( ii=0 ; ii < nx ; ii++ ){
       i8 = ii >> 3 ;
       qimar[ii+jj*nx] = ( imar[(i8)+jj*nbrow] & bmask[ii&7] ) != 0 ;
     }

   memcpy( imar , qimar , nx*ny ) ; mri_free( qim ) ;
   EXRETURN ;
}
