/*===========================================================================
  Copyright (C) 1995-2009 European Southern Observatory (ESO)
 
  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., 675 Massachusetts Ave, Cambridge, 
  MA 02139, USA.
 
  Correspondence concerning ESO-MIDAS should be addressed as follows:
	Internet e-mail: midas@eso.org
	Postal address: European Southern Observatory
			Data Management Division 
			Karl-Schwarzschild-Strasse 2
			D 85748 Garching bei Muenchen 
			GERMANY
===========================================================================*/

/*+++++++++++++++++++++ Module SCAUX +++++++++++++++++++++++++++++++++++++++
.LANGUAGE   C
.IDENTIFICATION  Module SCAUX.C
.AUTHOR   Klaus Banse
.KEYWORDS Midas utility routines.
.ENVIRONMENT VMS and UNIX
.COMMENTS
holds conv_dat, conv_pix, get_byte, rddisk, wrdisk
.VERSION
 090326		last modif

------------------------------------------------------------------------*/
 
#include <stdio.h>
#include <stdlib.h>
#include <fileexts.h>
#include <osyparms.h>
 
 
#define  AUX_READ     1
#define  AUX_WRITE    2
#define  AUX_GET      3

#define  NO_LINES  512
 
static int   wrkspace = -1;
static char  *wrkptr;

int get_byte();

static struct FCT_STRUCT  *fctpntr;


/*

*/

int conv_dat(flg,imno,felem,size,actsize,newptr)
 
int   flg	/* IN: 1 or 2 for read or write */;
int   imno	/* IN: FCT entry number */;
int   felem	/* IN: first data pixel in input frame */;
int   size	/* IN: no. of data pixels to convert */;
int   *actsize	/* OUT: actual size (only for flg = AUX_GET  */;
char   **newptr	/* IN/OUT: holds addresses of disk buffer/virtual memory */;
 
{
int status, ktimes, kpix, newbyte, oldbyte;
int newfmt, oldfmt, actsz, total;
register int  nr, mm;

char   *newbuf, *oldbuf;



fctpntr = FCT.ENTRIES + imno;

oldbyte = get_byte(fctpntr->FORMAT);
newbyte = get_byte(fctpntr->DATTYP);
status = ERR_NORMAL;


/*  allocate virtual memory for working buffer  */

if (wrkspace == -1)
   {
   wrkspace = NO_LINES * OUR_BLOCK_SIZE;	/* this may be updated ...  */
   wrkptr = malloc((size_t)wrkspace);
   if (wrkptr == (char *) 0) return ERR_MEMOUT;
   }

kpix = wrkspace / oldbyte;		/* no. of pixels in wrkspace  */
ktimes = ((size - 1) / kpix) + 1;
if (ktimes == 1 ) kpix = size;

if (flg == AUX_WRITE) goto write_section;
total = 0;


/*  if here for mapping, allocate virtual memory for data in new format   */

if (flg == AUX_READ)
   {
   mm = (size * newbyte);
   newptr[0] = malloc((size_t)mm);
   if (newptr[0] == (char *) 0) return ERR_MEMOUT;

   newptr[1] = newptr[0] + mm - 1;
   }


/*  here for reading in stuff  */
/*******************************/

newbuf = newptr[0];
oldbuf = wrkptr;
newfmt = fctpntr->DATTYP;
oldfmt = fctpntr->FORMAT;


/*  now read in from file and convert  */

for (nr=0; nr<ktimes; nr++) 
   {
   status = rddisk(imno,felem,kpix,&actsz,oldbuf);
   if (status != ERR_NORMAL)
      {
      if (status != ERR_NODATA)
         return status;				/* real problem */
      else
         {
         status = ERR_NORMAL;			/* we reached end of data */
         break;					/* get out of loop */
         }
      }

   conv_pix(newbuf,oldbuf,newfmt,oldfmt,actsz);
   newbuf += (actsz*newbyte);
   felem += actsz;
   total += actsz;
   size -= actsz;
   if (size < kpix) kpix = size;
   }

*actsize = total;
return status;


/*  here for writing out stuff  */
/*******************************/

write_section:

oldbuf = newptr[0];
newbuf = wrkptr;
oldfmt = fctpntr->DATTYP;
newfmt = fctpntr->FORMAT;


/*  now convert back and write to file */

for (nr=0; nr<ktimes; nr++)
   {
   conv_pix(newbuf,oldbuf,newfmt,oldfmt,kpix);
   status = wrdisk(imno,felem,kpix,newbuf);
   if (status != ERR_NORMAL) return status;
   oldbuf += (kpix*newbyte);
   felem += kpix;
   size -= kpix;
   if (size < kpix) kpix = size;
   }
    
return status;
}
/*

*/

void conv_pix(newpntr,oldpntr,newfmt,oldfmt,size)
int  newfmt, oldfmt, size;
char  *newpntr, *oldpntr;

{
register int  nr;

#if vms
unsigned char  *outc, *inc;			/* VMS compiler didn't like */
short int      *outj, *inj;			/* too many register pointers */
unsigned short int      *outk, *ink;
int       *outi, *ini;
float     *outr, *inr;
double    *outd, *ind;

#else
register unsigned char  *outc, *inc;
register short int      *outj, *inj;
register unsigned short int      *outk, *ink;
register int       *outi, *ini;
register float     *outr, *inr;
register double    *outd, *ind;
#endif


switch(oldfmt)
   {
   case D_I1_FORMAT: 				/* input is I*1 integer  */
    inc = (unsigned char *) oldpntr;	
   
    switch(newfmt)
       {
       case D_UI2_FORMAT:		/* output is unsigned I*2 integer */
        outk = (unsigned short int *) newpntr;
        for (nr=0; nr<size; nr++)
          *outk++ = (unsigned short int) *inc++;
        break;
       
       case D_I2_FORMAT:			/* output is I*2 integer */
        outj = (short int *) newpntr;
        for (nr=0; nr<size; nr++)
          *outj++ = (short int) *inc++;
        break;
       
       case D_I4_FORMAT:			/* output is I*4 integer */
        outi = (int *) newpntr;
        for (nr=0; nr<size; nr++)
          *outi++ = (int) *inc++;
        break;
       
       case D_R4_FORMAT:			/* output is R*4 real  */
        outr = (float *) newpntr;
        for (nr=0; nr<size; nr++)
          *outr++ = (float) *inc++;
        break;
       
       case D_R8_FORMAT:			/* output is R*8 real  */
        outd = (double *) newpntr;
        for (nr=0; nr<size; nr++)
          *outd++ = (double) *inc++;
        break;

       case D_I1_FORMAT:			/* same format ... */
        outc = (unsigned char *) newpntr;
        for (nr=0; nr<size; nr++) *outc++ = *inc++;
       } 
    break;
   
   case D_I2_FORMAT: 				/* input is I*2 integer  */
    inj = (short int *) oldpntr;	
   
    switch(newfmt)
       {
       case D_I1_FORMAT:			/* output is I*1 integer */
        outc = (unsigned char *) newpntr;
        for (nr=0; nr<size; nr++)
           *outc++ = (unsigned char) *inj++;
        break;
       
       case D_UI2_FORMAT:               /* output is unsigned I*2 integer */
        outk = (unsigned short int *) newpntr;
        for (nr=0; nr<size; nr++)
          *outk++ = (unsigned short int) *inj++;
        break;

       case D_I4_FORMAT:			/* output is I*4 integer */
        outi = (int *) newpntr;
        for (nr=0; nr<size; nr++)
           *outi++ = (int ) *inj++;
        break;
       
       case D_R4_FORMAT:			/* output is R*4 real  */
        outr = (float *) newpntr;
        for (nr=0; nr<size; nr++)
           *outr++ = (float) *inj++;
        break;
 
       case D_R8_FORMAT:			/* output is R*8 real  */
        outd = (double *) newpntr;
        for (nr=0; nr<size; nr++)
          *outd++ = (double) *inj++;
        break;

       case D_I2_FORMAT:			/* same format... */
        outj = (short int *) newpntr;
        for (nr=0; nr<size; nr++) *outj++ = *inj++;
       }
    break;

   case D_UI2_FORMAT:                     /* input is unsigned I*2 integer  */
    ink = (unsigned short int *) oldpntr;

    switch(newfmt)
       {
       case D_I1_FORMAT:                        /* output is I*1 integer */
        outc = (unsigned char *) newpntr;
        for (nr=0; nr<size; nr++)
           *outc++ = (unsigned char) *ink++;
        break;

       case D_I2_FORMAT:               	/* output is signed I*2 integer */
        outj = (short int *) newpntr;
        for (nr=0; nr<size; nr++)
          *outj++ = (short int) *ink++;
        break;

       case D_I4_FORMAT:                        /* output is I*4 integer */
        outi = (int *) newpntr;
        for (nr=0; nr<size; nr++)
           *outi++ = (int ) *ink++;
        break;

       case D_R4_FORMAT:                        /* output is R*4 real  */
        outr = (float *) newpntr;
        for (nr=0; nr<size; nr++)
           *outr++ = (float) *ink++;
        break;

       case D_R8_FORMAT:                        /* output is R*8 real  */
        outd = (double *) newpntr;
        for (nr=0; nr<size; nr++)
          *outd++ = (double) *ink++;
        break;

       case D_UI2_FORMAT:			/* same format... */
        outk = (unsigned short int *) newpntr;
        for (nr=0; nr<size; nr++) *outk++ = *ink++;
       }
    break;

   case D_I4_FORMAT:				/* input is I*4 integer */
    ini = (int *) oldpntr;			
    
    switch(newfmt)
       {
       case D_I1_FORMAT:			/* output is I*1 integer */
        outc = (unsigned char *) newpntr;
        for (nr=0; nr<size; nr++)
          *outc++ = (unsigned char) *ini++;
        break;
       
       case D_I2_FORMAT:			/* output is I*2 integer */
        outj = (short int *) newpntr;
        for (nr=0; nr<size; nr++)
          *outj++ = (short int) *ini++;
        break;
       
       case D_UI2_FORMAT:               /* output is unsigned I*2 integer */
        outk = (unsigned short int *) newpntr;
        for (nr=0; nr<size; nr++)
          *outk++ = (unsigned short int) *ini++;
        break;

       case D_R4_FORMAT:			/* output is R*4 real  */
        outr = (float *) newpntr;
        for (nr=0; nr<size; nr++)
          *outr++ = (float) *ini++;
        break;
       
       case D_R8_FORMAT:			/* output is R*8 real  */
        outd = (double *) newpntr;
        for (nr=0; nr<size; nr++)
          *outd++ = (double) *ini++;
        break;

       case D_I4_FORMAT:			/* same format... */
        outi = (int *) newpntr;
        for (nr=0; nr<size; nr++) *outi++ = *ini++;
        }
    break;

   case D_R4_FORMAT:				/* input is R*4 real  */
    inr = (float *) oldpntr;			
    
    switch(newfmt)
       {
       case D_I1_FORMAT:			/* output is I*1 integer */
        outc = (unsigned char *) newpntr;
        for (nr=0; nr<size; nr++)
          *outc++ = (unsigned char) *inr++;
        break;
       
       case D_I2_FORMAT:			/* output is I*2 integer */
        outj = (short int *) newpntr;
        for (nr=0; nr<size; nr++)
          *outj++ = (short int) *inr++;
        break;
       
       case D_UI2_FORMAT:               /* output is unsigned I*2 integer */
        outk = (unsigned short int *) newpntr;
        for (nr=0; nr<size; nr++)
          *outk++ = (unsigned short int) *inr++;
        break;

       case D_I4_FORMAT:			/* output is I*4 integer */
        outi = (int *) newpntr;
        for (nr=0; nr<size; nr++)
          *outi++ = (int) *inr++;
        break;
       
       case D_R8_FORMAT:			/* output is R*8 real  */
        outd = (double *) newpntr;
        for (nr=0; nr<size; nr++)
          *outd++ = (double) *inr++;
        break;

       case D_R4_FORMAT:			/* same format... */
        outr = (float *) newpntr;
        for (nr=0; nr<size; nr++) *outr++ = *inr++;
       }
    break;

   case D_R8_FORMAT:			/* input is double precision */
    ind = (double *) oldpntr;			
    
    switch(newfmt)
       {
       case D_I1_FORMAT:			/* output is I*1 integer */
        outc = (unsigned char *) newpntr;
        for (nr=0; nr<size; nr++)
          *outc++ = (unsigned char) *ind++;
        break;
       
       case D_I2_FORMAT:			/* output is I*2 integer */
        outj = (short int *) newpntr;
        for (nr=0; nr<size; nr++)
          *outj++ = (short int) *ind++;
        break;
       
       case D_UI2_FORMAT:               /* output is unsigned I*2 integer */
        outk = (unsigned short int *) newpntr;
        for (nr=0; nr<size; nr++)
          *outk++ = (unsigned short int) *ind++;
        break;

       case D_I4_FORMAT:			/* output is I*4 integer */
        outi = (int *) newpntr;
        for (nr=0; nr<size; nr++)
          *outi++ = (int) *ind++;
        break;
       
       case D_R4_FORMAT:			/* output is R*4 real  */
        outr = (float *) newpntr;
        for (nr=0; nr<size; nr++)
          *outr++ = (float ) *ind++;
        break;

       case D_R8_FORMAT:			/* same format... */
        outd = (double *) newpntr;
        for (nr=0; nr<size; nr++) *outd++ = *ind++;
       }
    break;
   }
}
/*

*/

int get_byte(format)
int  format;

{
switch(format)
   {
   case D_R4_FORMAT:
    return (RR_SIZE);
   case D_R8_FORMAT:
    return (DD_SIZE);
   case D_I4_FORMAT:
    return (II_SIZE);
   case D_I2_FORMAT:
   case D_UI2_FORMAT:
    return (JJ_SIZE);
   default:
    return (1);				/* single byte */
   }
}

/*

*/

int rddisk(imno,felem,size,actsize,bufadr)

/*++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE
read data from disk frame into memory 
.ALGORITHM
synchronous disk I/O
.RETURNS
return status ( 0 = o.k )
--------------------------------------------------------*/

int   imno		/* IN : file no. of data frame */;
int   felem	/* IN : 1st pixel to be accessed in data space */;
int   size    	/* IN : number of data values (pixels) to be read */;
int   *actsize 	/* OUT: actual no. of pixels read */;
char	*bufadr;	/* IN: address of data buffer  */

{
int   nbytes, pixblock;
int   status, chanl;
int mm;

unsigned int   difpix, nopix, firstpix;
unsigned int   uu, fvbn, lovbn, difbytes, bytesize;

size_t  fsize;



fctpntr = FCT.ENTRIES + imno;

chanl = fctpntr->IOCHAN;		/* I/O channel of frame  */
nbytes = fctpntr->NOBYTE;		/* no. of bytes per pixel */
pixblock = fctpntr->PIXPBL;		/* no. of pixels per block  */
fsize = fctpntr->SIZE;
if (size < 1) 				/* if no size given, get all pixels */
   nopix = fsize;
else
   nopix = size;

if ( (felem+nopix-1) > fsize)
   {
   nopix = fsize - felem + 1;
   if (nopix < 1)
      {
      if (nopix == 0)
         return ERR_NODATA;		/* we're at the end */
      else
         return ERR_INPINV;
      }
   }


/*  get first virtual block + real start pixel  */
 
uu = (unsigned int)felem * (unsigned int)nbytes;
fvbn = (uu - 1) / OUR_BLOCK_SIZE;       /* added to 1. data block */
firstpix = fvbn*pixblock + 1;		/* first pixel accessible  */
difpix = felem - firstpix;		/* difference to 1. required pixel */
	

/*  calculate starting block  */
 
lovbn = fctpntr->STBLOK + fvbn;	/* 1. data block + offset = 1. block to map */
bytesize = nopix * nbytes;	/* total no. of bytes we have to provide */
	

/*  either we have to read two blocks  */
 
if (difpix > 0)
   {
   char  temp[OUR_BLOCK_SIZE];

   difbytes = difpix * nbytes;
   status = OSY_RVB(chanl,temp,(unsigned int)OUR_BLOCK_SIZE,(int)lovbn);
   if (status != ERR_NORMAL) return status;
   
   mm = OUR_BLOCK_SIZE - difbytes;
   if (mm > bytesize) mm = bytesize;
   (void) memcpy(bufadr,&temp[difbytes],(size_t)mm);

   bytesize -= mm;
   if (bytesize > 0)
      status = OSY_RVB(chanl,bufadr+mm,bytesize,(int)(lovbn+1));
   }


/* or we get it all in one go  */

else
   status = OSY_RVB(chanl,bufadr,bytesize,(int)lovbn);
 

*actsize = (int) nopix;
return status;
}	
/*

*/

int wrdisk(imno,felem,size,bufadr)

/*++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE
write data from memory into disk frame 
.ALGORITHM
synchronous disk I/O
.RETURNS
return status ( 0 = o.k )
--------------------------------------------------------*/

int   imno		/* IN : file no. of data frame */;
int   felem	/* IN : 1st pixel to be accessed in data space */;
int   size    	/* IN : no. of data values (pixels) to be written  */;
char	*bufadr;	/* IN: address of data buffer  */

{
int   nbytes, pixblock;
int   status, chanl;
int mm;

unsigned int   difpix, nopix, firstpix;
unsigned int   uu, fvbn, lovbn, difbytes, bytesize;

size_t  fsize;



fctpntr = FCT.ENTRIES + imno;

chanl = fctpntr->IOCHAN;		/* I/O channel of frame  */
nbytes = fctpntr->NOBYTE;		/* no. of bytes per pixel */
pixblock = fctpntr->PIXPBL;		/* no. of pixels per block  */
fsize = fctpntr->SIZE;
if (size < 1) 			/* if no size given, get all pixels */
   nopix = fsize;
else
   nopix = size;

if ( (felem+nopix-1) > fsize )
   {
   nopix = fsize - felem + 1;
   if (nopix < 1) return ERR_INPINV;
   }


/*  get first virtual block + real start pixel  */
 
uu = (unsigned int)felem * (unsigned int)nbytes;
fvbn = (uu - 1) / OUR_BLOCK_SIZE;	/* added to 1. data block */
firstpix = (fvbn * pixblock) + 1;	/* first pixel accessible  */
difpix = felem - firstpix;		/* difference to 1. required pixel */
	

/*  calculate starting block  */
 
lovbn = fctpntr->STBLOK + fvbn;	/* 1. data block + offset = 1. block to map  */
bytesize = nopix * nbytes;	      /* total no. of bytes we have to write */
	

/*  either we have to read + write first block separately */
 
if (difpix > 0)
   {
   char  temp[OUR_BLOCK_SIZE];

   difbytes = difpix * nbytes;
   status = OSY_RVB(chanl,temp,(unsigned int)OUR_BLOCK_SIZE,(int)lovbn);
   if (status != ERR_NORMAL) return status;
   
   mm = OUR_BLOCK_SIZE - difbytes;
   if (mm > bytesize) mm = bytesize;
   (void) memcpy(&temp[difbytes],bufadr,(size_t)mm);
   status = OSY_WVB(chanl,temp,(unsigned int)OUR_BLOCK_SIZE,(int)lovbn);
   if (status != ERR_NORMAL) return status;

   if (bytesize > mm)
      {
      lovbn ++;
      bufadr += mm;
      bytesize -= mm;

#if vms
      nvb = bytesize / OUR_BLOCK_SIZE;		/* get no. of full blocks */
      if (nvb > 0)
         {
         mm = nvb * OUR_BLOCK_SIZE;
         status = OSY_WVB(chanl,bufadr,(unsigned int)mm,(int)lovbn);
         if (status != ERR_NORMAL) return status;
         lovbn += nvb;
         bufadr += mm;
         bytesize -= mm;
         }

      if (bytesize > 0)            /*  we must be in [0,OUR_BLOCK_SIZE-1] ... */
         {
         status = OSY_RVB(chanl,temp,(unsigned int)OUR_BLOCK_SIZE,(int)lovbn);
         if (status != ERR_NORMAL) return status;

         (void) memcpy(temp,bufadr,(size_t)bytesize);
         status = OSY_WVB(chanl,temp,(unsigned int)OUR_BLOCK_SIZE,(int)lovbn);
         }
#else

      status = OSY_WVB(chanl,bufadr,bytesize,(int)lovbn);

#endif
      }
   }


/* or we send it all in one go  */

else
   status = OSY_WVB(chanl,bufadr,bytesize,(int)lovbn);
 

return status;
}	
