/*
 * $Id: readraw.cxx,v 4.1 2004/05/24 23:45:14 hut66au Exp $
 *
 * Imview, the portable image analysis application
 * http://www.cmis.csiro.au/Hugues.Talbot/imview
 * ----------------------------------------------------------
 *
 *  Imview is an attempt to provide an image display application
 *  suitable for professional image analysis. It was started in
 *  1997 and is mostly the result of the efforts of Hugues Talbot,
 *  Image Analysis Project, CSIRO Mathematical and Information
 *  Sciences, with help from others (see the CREDITS files for
 *  more information)
 *
 *  Imview is Copyrighted (C) 1997-2001 by Hugues Talbot and was
 *  supported in parts by the Australian Commonwealth Science and 
 *  Industry Research Organisation. Please see the COPYRIGHT file 
 *  for full details. Imview also includes the contributions of 
 *  many others. Please see the CREDITS file for full details.
 *
 *  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 of the License, 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 Temple Place, Suite 330, Boston, MA 02111, USA.
 * */

/*------------------------------------------------------------------------
 *
 * Read raw buffers, for example coming from a socket or a pipe...
 *
 * Hugues Talbot	 6 Mar 2000
 *
 *      
 *-----------------------------------------------------------------------*/

#include "imnmspc.hxx" // name space definition if needed 

#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "imunistd.h"
#include <errno.h>
#include <map> // STL

#include "imview.hxx"
#include "imageIO.hxx"
#include "machine.hxx" // portability stuff
#include "rawImage.hxx"
#include "readraw.hxx"

using std::map;

extern imageIO *IOBlackBox;
extern map <string, RAWFILE_HEADER> rawfilemap;

typedef map<string, RAWFILE_HEADER>::iterator rfhmi; // raw file header map iterator

// prototypes for static functions


template<class T>int readrawbuf(int rfd, RAWFILE_HEADER const &myheader, T **&buffp);

int rawnbcomp(const char *ident)
{
    return 1; // we may change that later, don't know.
}

int readRaw(const char *ident, int index)
{
    int                 res = 0;
    rfhmi               p;
    void              **buf;

    dbgprintf("Read raw called\n");

    p = rawfilemap.find(ident);
    if (p == rawfilemap.end()) {
	errprintf("Header not found\n");
	res = 2;
    } else {
	RAWFILE_HEADER      &myheader = p->second;

	dbgprintf("Header found\n");

	// read the actual data
	res = readrawdata(ident, myheader, buf);

	if (res == 0) {
	    IOBlackBox->setCurrBuffp(buf);
	    IOBlackBox->setCurrImgWidth(myheader.nx);
	    IOBlackBox->setCurrImgHeight(myheader.ny);
	    IOBlackBox->setCurrImgThickness(myheader.nz);
	    IOBlackBox->setXOffset(0);
	    IOBlackBox->setYOffset(0);
	    IOBlackBox->setZOffset(0);
	    IOBlackBox->setCurrZPos(0); // first plane
	    // only guessing there.
	    if (myheader.spp > 1)
		IOBlackBox->setCurrImgType(IM_SPECTRUM);
	    else
		IOBlackBox->setCurrImgType(IM_SINGLE);
	    IOBlackBox->setCurrPixType(myheader.pixt);
	    IOBlackBox->setCurrImgNbComps(1);
	    IOBlackBox->setCurrImgNbSamples(myheader.spp);
	    IOBlackBox->setImgDesc("RAW");
	}
    }
    
    return res;
}


int readrawdata(const char *fname, RAWFILE_HEADER const &myheader, void **&buffp)
{
    int            retval = 0;
    int            rfd;  // raw file descriptor

    rfd = open(fname, O_RDONLY | O_BINARY);

    if (rfd > 0) {
	switch(myheader.pixt) {
	  case IM_BINARY:
	  case IM_INT1:
	  case IM_UINT1:
	  {
	      uchar **ch_buf;
	      
	      retval = readrawbuf(rfd, myheader, ch_buf);
	      buffp = (void **)ch_buf;
	  }
	  break;

	  case IM_INT2:
	  {
	      short **sh_buf;

	      retval = readrawbuf(rfd, myheader, sh_buf);
	      buffp = (void **)sh_buf;
	  }
	  break;

	  case IM_UINT2:
	  {
	      ushort **ush_buf;

	      retval = readrawbuf(rfd, myheader, ush_buf);
	      buffp = (void **)ush_buf;
	  }
	  break;

	  case IM_INT4:
	  {
	      int **int_buf;

	      retval = readrawbuf(rfd, myheader, int_buf);
	      buffp = (void **)int_buf;
	  }
	  break;
	  
	  case IM_UINT4:
	  {
	      uint **uint_buf;

	      retval = readrawbuf(rfd, myheader, uint_buf);
	      buffp = (void **)uint_buf;
	  }
	  break;

	  case IM_FLOAT:
	  {
	      float **flt_buf;

	      retval = readrawbuf(rfd, myheader, flt_buf);
	      buffp = (void **)flt_buf;
	  }
	  break;

	  case IM_DOUBLE:
	  {
	      double **dbl_buf;

	      retval = readrawbuf(rfd, myheader, dbl_buf);
	      buffp = (void **)dbl_buf;
	  }
	  break;
	  
	  default:
	    errprintf("readrawdata: Unsupported pixel type %s\n", IOBlackBox->typeName(myheader.pixt));
	    retval = 1;
	    break;
	}
	close(rfd);
    } else {
	errprintf("Could not open %s, %s\n", fname, strerror(errno));
	retval = 10;
    }
    
    return retval;
}

// template function!
template<class T>int readrawbuf(int rfd, RAWFILE_HEADER const &myheader, T **&buffp)
{
    T                **cbuf;
    int                nx, ny, nz;
    int                spp;
    int                i, j, p, l, interleave;
    ssize_t            nbread = 0;
    int                retval = 0;
        
    
    nx = myheader.nx;
    ny = myheader.ny;
    nz = myheader.nz;
    spp = myheader.spp;
    
    cbuf = (T **)calloc(spp, sizeof(T*));
    if (cbuf == 0) {
	return 20; // out of memory
    }
    for (i = 0 ; i < spp ; i++) {
	cbuf[i] = (T *)malloc(nx*ny*nz*sizeof(T));
	if (cbuf[i] == 0) {
	    for (j = 0 ; j < i ; j++)
		free(cbuf[i]);
	    return 21; // out of memory too!
	}
    }

    // if spp is 1, we use the BSQ interleaving, as it is the most efficient
    interleave = (spp == 1) ? rawimage::BSQ : myheader.itl;

    // skip the header

    if (lseek(rfd, myheader.skip, SEEK_SET) < 0) {
	errprintf("Cannot skip %d bytes (header size): %s\n", strerror(errno));
	for (i = 0 ; i < spp ; i++)
	    free(cbuf[i]);
	free(cbuf);
	*buffp = 0;
	return 50; // header not skipped, nothing read
    }
    
    switch(interleave) {
      case rawimage::BIL : { // line interleave
	  int *index;
	  
	  index = (int *)calloc(spp, sizeof(int));
	  for (p = 0; p < nz ; p++) {
	      for (l = 0 ; l < ny ; l++) {
		  for (i = 0 ; i < spp ; i++) {
		      nbread = read(rfd, cbuf[i] + index[i] , nx);
		      if (nbread != nx) {
			  errprintf("Incomplete read, %s\n",
				    (nbread < 0)
				    ? strerror(errno) :
				    "but no other error");
			  retval = 3;
			  goto fail_bil;
		      }
		      index[i] += nx;
		  }
	      }
	  }
      fail_bil:
	  free(index); // we will still endeavour to read something.
        }
        break;

      case rawimage::BIP : { // pixel interleave
	  T **q;
	  T  *aLine, *pl;
	  int     n;

	  q = (T **) malloc(spp * sizeof(T*));
	  aLine = (T *) malloc(nx * spp * sizeof(T));
	  for (i = 0 ; i < spp ; i++)
	      q[i] = cbuf[i];
	  
	  for (p = 0 ; p < nz ; p++) {
	      for (l = 0 ; l < ny ; l++) {
		  nbread = read(rfd, aLine, nx * spp * sizeof(T));
		  if (nbread != (int)(nx * spp * sizeof(T))) {
		      errprintf("Incomplete read, %s\n",
				(nbread < 0)
				? strerror(errno) :
				"but no other error");
		      retval = 4;
		      goto fail_bip;
		  }
		  pl = aLine;
		  for (n = 0 ; n < nx ; n++) 
		      for (i = 0 ; i < spp ; i++) {
			  *(q[i])++ = *pl++;
		      }
	      }
	  }
      fail_bip:
	  free(aLine);
	  free(q);
        }
	break;

      case rawimage::BSQ : // block interleave
	for (i = 0 ; i < spp ; i++) {
	    int totalread = 0;
	    int mustread = nx*ny*nz*sizeof(T);
	    // of course the read might be short, we read as much as we can in one go
	    while ((totalread < mustread) && ((nbread = read(rfd, cbuf[i]+totalread, mustread-totalread)) > 0))
		totalread += nbread;

	    if (totalread != mustread) {
		errprintf("Incomplete read, %d bytes missing: %s\n",
			  mustread-totalread,
			  (nbread < 0)
			  ? strerror(errno) :
			  "but no other error");
		retval = 2;
		break;
	    }
	}
	break;
	
    }

    buffp = cbuf;
    
    return retval;
}

