/*===========================================================================
  Copyright (C) 1989-2011 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
===========================================================================*/

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.TYPE        Module
.NAME        tbacol.c
.LANGUAGE    C
.AUTHOR      IPG-ESO Garching
.CATEGORY    table applications: Column operations
.COMMENTS
	The routines of this module are tools manipulating columns.

.VERSION 1.0 	25-Mar-1989   Definition     J.D. Ponz
.VERSION 3.0 	05-Jul-1990   New version with column arrays   F.O.
.VERSION 3.1 	10-Jun-1991: Copy Column use partial mapping when possible
.VERSION 3.2    27-Jul-1992: correct bug when the reference column is character

 110630		last modif
------------------------------------------------------------*/

#include <ctype.h>
#include <tbldef.h>
#include <midas_def.h>
#include <macrogen.h>		/* General-purpose macros   */
#include <proto_tbl.h>

#define MAXCHAR	 4096		/* Max available for conversion	*/

static int  itype, otype;	/* Column datatypes	    */
static int iitems, oitems;	/* Column sizes  	    */
static int ibytes, obytes;	/* Column sizes  	    */
static int  check_status;	/* Set by check routine	    */

				/* We use here pointers to functions */
typedef int (*FUNCTION)();

int TCERDC(), TCARDD(), TCARDI(), TCARDR();
int TCEWRC(), TCAWRD(), TCAWRI(), TCAWRR();
int TCCPAM();

static FUNCTION read_functions[]  = {TCERDC, TCARDI, TCARDR, TCARDD };
static FUNCTION write_functions[] = {TCEWRC, TCAWRI, TCAWRR, TCAWRD };

static int datatype(dtype)
/*++++++++++++++++++
.PURPOSE Convert datatype into an index for functions
.RETURNS 0 = char ; 1 = int ; 2 = R*4 ; 3 = R*8
------------------*/
	int	dtype;	/* IN: Datatype to convert */
{
  switch (dtype) {
    case D_R8_FORMAT:	return (3);
    case D_R4_FORMAT:	return (2);
    case D_C_FORMAT:	return (0);
    default        :    return (1);
  }
}

static int check(in, icol, out, ocol)
/*++++++++++++++++++
.PURPOSE Check compatibility of Columns
.RETURNS 0 : Compatible; >0 : can be column mapped; -1 : Error (check_status)
------------------*/
	int	in;	/* IN: Input Table  */
	int	icol;	/* IN: Column in    */
	int	out;	/* IN: Output Table */
	int	ocol;	/* IN: Column out   */
{
	int  alen, dummy, isto, osto;
  	char	form[1+TBL_FORLEN];

  check_status = TCBGET (in, icol, &itype, &iitems, &ibytes);
  if (check_status) return(-1);
  check_status = TCBGET (out,ocol, &otype, &oitems, &obytes);
  if (check_status) return(-1);
  check_status = TCDGET (in, &isto);
  if (check_status) return(-1);
  check_status = TCDGET (out,&osto);
  if (check_status) return(-1);
  
  if ((itype == otype) && (iitems == oitems) &&
      (isto == F_TRANS))
      return (ibytes);

  if (itype == D_C_FORMAT)	{
	if (iitems > MAXCHAR)	{
		SCTPUT ("**** Input column too long");
		check_status = ERR_TBLCOL;	return (-1);
	}
	return (0);
  }

  if (otype == D_C_FORMAT) {	/* Check large enough... */
  	TCFGET (in, icol, form, &alen, &dummy);
  	if (alen >= obytes) {
		SCTPUT("**** Output column too small");
		check_status = ERR_TBLCOL;	return (-1);
  	}
	else					return (0);
  }

/*  if ( (iitems > 1) || (oitems > 1)) {
	SCTPUT("**** Copy Array Columns not available");
	check_status = ERR_TBLIMP;
	return (-1); 
  } */

  return(0);
}

static int mod_rows(tid, rows)
/*++++++++++++++++++
.PURPOSE Modify the number of rows (in connection with MAP facilities)
.RETURNS Status
------------------*/
	int	tid;	/* IN: The table concerned	*/
	int	rows;	/* IN: Number of Rows		*/
{
        int	ncol, nrow, narow, dummy;
	int	status;

  status = TCIGET (tid, &ncol, &nrow, &dummy, &dummy, &narow);
  if (status) return(status);
  nrow = MAX (nrow, rows);
  nrow = MIN (nrow, narow);
  
  status = TCIPUT (tid, ncol, nrow);
  
  return (status);
}

/*=========================================================================
 * 			Public Routines
 *=========================================================================*/

int tbl_appcol(in, icol, out, ocol, type, nrow, nout)
/*++++++++++++++++++
.PURPOSE Copy the selected values of an input column to an output column.
	When the same table is used for input and output, values are
	placed in the same row, i.e. values of ocol are replaced by 
	the calues of icol for selected rows. 

	When two different tables are used, the icol values of the 
	selected rows of the in table are used to replace the values
	of ocol values for consecutive rows of the  out table,
	starting at row number nout.
.RETURNS Status
------------------*/
 int in		/* IN : input table id 		*/;
 int icol	/* IN : input column 		*/;
 int out	/* IN : output table id 	*/;
 int ocol	/* IN : output column 		*/;
 int type	/* IN : column type (UNUSED)	*/;
 int nrow	/* IN : total number of rows in input table */;
 int *nout	/* MOD: starting row on input / last copied row on output */;
{
int 	irow;               /* input row      */
int     orow;               /* output row     */
int     *prow, status, nobytes,bytes,items,k,smbytes;
int     null, dummy, sel,*ival;
int     is, id, *smapped, ms, md, mms, mmd ;

float *rval;

double value[MAXCHAR/sizeof(double)],*dval;

char  cval[256]; 
char  form[TBL_FORLEN+1];
char *osmmget();
char *dmapped;

FUNCTION eread, ewrite;


dval = (double *) 0;
rval = (float *) 0;
ival = (int *) 0;
mmd = mms = ms = md = 0;

 /* Check Compatibility of columns */
  bytes = check (in, icol, out, ocol);
  
  /*if not compatible: */
  if (bytes < 0)	return (check_status);
  	  
  /* If a single table is used (i.e. in == out), the same row
		   id always used. */
  prow = (in == out ? &irow : &orow);
  orow = *nout;

  TCFGET(in, icol, form, &dummy, &dummy); /* read colum format */
                            /* reads binary storage characteristics of column*/
  TCBGET(in,icol,&dummy,&items,&nobytes); 
  if (toupper(form[0]) == 'T')	 	/* Time Function. Convert */
	TCFGET(out, ocol, form, &dummy, &dummy);
  if (toupper(form[0]) == 'T')	status = 0;	/* Force character */
  else				status = datatype(itype);
  eread  = read_functions[status];
  ewrite = write_functions[status];
  status = ERR_NORMAL;
 
  /*columns are compatible:*/ 
  if (bytes > 0 ) 
  {
	smapped = (int *)0;
	dmapped = (char *)0;
	is = id = 0;	
        switch (type) 
	{
             case D_I1_FORMAT: ival = (int *) osmmget(sizeof(int));
                               break;
             case D_I2_FORMAT: ival = (int *) osmmget(sizeof(int));
                               break;
             case D_I4_FORMAT: ival = (int *) osmmget(items * sizeof(int));
                               break;
             case D_R4_FORMAT: rval = (float *)osmmget(items * sizeof(float));
                               break;
             case D_R8_FORMAT: dval = (double *)osmmget(items * sizeof(double));
                               break;
        }
  	for (irow=1; (status == ERR_NORMAL) && (irow <= nrow); 
	     irow++, is--, id--,  smapped ++, 
             dmapped += nobytes) 
        {
		if (is <= 0)	
		{ 
                        if (smapped) 
			{
                          while (ms-- > 0) smapped --;
                          TCEUNM (in, (char *)smapped);
                        }
		        status = TCCPAM(in, 0, irow, 0, &is, &smapped);
                        ms = is;
                        mms = 0;
			if (status != ERR_NORMAL)	continue;
		}
		if (id <= 0)  
		{
                        if (dmapped) 
			{
                          while (md-- >0) dmapped -= nobytes;
			  TCEUNM (in,dmapped);
                        }
			status = TCCPAM(in, icol, irow, 0, &id, &dmapped);
                        md = id;
                        mmd = 0;
			if (status != ERR_NORMAL)	continue;
		 }
		if (!( *smapped)) continue;    
		orow += 1;
                mms++;
                mmd++;
                switch (type) 
		{
                case D_I1_FORMAT: for (k=0; k<items; k++)  
		                  {
                                    *ival = *(dmapped+k);
                                    if ((*ival < -127) || (*ival > 255)) 
                                      TCADEL(out,*prow,ocol,k+1,1);
                                    else TCAWRI(out,*prow,ocol,k+1,1,ival);
                                  }
                                  break;
                case D_I2_FORMAT: for (k=0; k<items; k++)  
		                  {
                                      *ival = *(short *)(dmapped + 
                                            k * sizeof(short)) ;
                                      if ((*ival < -32767) || (*ival > 0xffff))
                                      TCADEL(out,*prow,ocol,k+1,1);
                                      else TCAWRI(out,*prow,ocol,k+1,1,ival);
                                  }
                                  break;
                case D_I4_FORMAT: for (k=0; k<items; k++)
                                  *(ival+k) = *(int *)(dmapped +
                                      k * sizeof(int)); 
                                  TCAWRI(out,*prow,ocol,1,items,ival);
                                  break;
                case D_R4_FORMAT: for (k=0; k<items; k++)
                                  *(rval+k) = *(float *)(dmapped + 
                                      k * sizeof(float));
                                  TCAWRR(out,*prow,ocol,1,items,rval);
                                  break;
                case D_R8_FORMAT: for (k=0; k<items; k++)
                                  *(dval+k) = *(double *)(dmapped +
                                      k * sizeof(double));
                                  TCAWRD(out,*prow,ocol,1,items,dval);
                                  break;
                case D_C_FORMAT:  smbytes = nobytes/items;
                                  for (k=0; k<items; k++) 
				  {
                                    strncpy(cval,dmapped,smbytes); 
                                    cval[smbytes] = '\0';
                                    TCAWRS(out,*prow,ocol,k+1,cval);
                                    if (k != items-1) dmapped += smbytes;
                                  }
                                  break;
                }
	} /* end of for loop over all input rows */
	
        switch (type) 
	{
             case D_I1_FORMAT: 
             case D_I2_FORMAT: 
             case D_I4_FORMAT: osmmfree(ival);
                               break;
             case D_R4_FORMAT: osmmfree(rval); 
                               break;
             case D_R8_FORMAT: osmmfree(dval);
                               break;
        }
        while (mms-- > 0) smapped --;
	TCEUNM (in, (char *)smapped);
        while (mmd-- >0) dmapped -= nobytes;
	TCEUNM (in, dmapped);
 	mod_rows (out, orow);
 	*nout = orow;
	return (status);
  }
  
  /* This part only executed if bytes == 0 */
  status = ERR_NORMAL;  /*MP 220291 */
  for (irow = 1; (status  == ERR_NORMAL) &&  (irow <= nrow); irow++) 
  {
        status = TCSGET (in, irow, &sel);
        if (status)   continue;
        if (!sel)     continue;

        orow += 1;
        status = (*eread)(in, irow, icol, 1, 1,value, &null);
        if (status)   continue;
        status = (*ewrite)(out, *prow, ocol,1,1, value);
  }
  mod_rows (out, orow);
  *nout = orow;
 
  return (status);
}


int tbl_copycol(in, icol, out, ocol, type, nrow, nout)
/*++++++++++++++++++
.PURPOSE Copy the selected values of an input column to an output column.
Routine similar to tbl_appcol, but the replacement always
starts from the first row of the output table.
.RETURNS Status
------------------*/
 int in		/* IN : input table id */;
 int icol	/* IN : input column */;
 int out	/* IN : output table id */;
 int ocol	/* IN : output column */;
 int type	/* IN : column type (UNUSED) */;
 int nrow	/* IN : total number of rows in input table */;
 int *nout	/* OUT: actual numberof rows copied */;
{
  *nout = 0;
  return ( tbl_appcol(in, icol, out, ocol, type, nrow, nout));
}


int tbl_copyref(in,icol,iref,out,ocol,oref,type, nrow, nout)
/*++++++++++++++++++
.PURPOSE Copy Column from one table to another by reference; NULL values
	are not copied.
.RETURNS Status
------------------*/
int in		/* IN : input table id */;
int icol	/* IN : input column */;
int iref	/* IN : input reference column */;
int out	/* IN : output table id */;
int ocol	/* IN : output column */;
int oref	/* IN : output reference column */;
int type	/* IN : column type (UNUSED) */;
int nrow	/* IN : total number of rows in input table */;
int *nout	/* OUT: actual number of rows copied */;

{
int  ocount, irow, orow, status, bytes;
int  null, sel, rtype, rbytes, nelem;
int  itype, ibytes, ielem;

double value[MAXCHAR/sizeof(double)], sval;

char  *cval;

FUNCTION sread;



ocount = *nout = 0;
cval = (char *) 0;

						/* Check Compatibility	*/
bytes = check (in, icol, out, ocol);
if (bytes < 0) return (check_status);

status = ERR_NORMAL;  /*MP 110491*/ 

(void) TCBGET (in, icol, &itype, &ielem, &ibytes);	/* type of in_col */

(void) TCBGET (in, iref, &rtype, &nelem, &rbytes);	/* type of refcol */

if (nelem > 1 && rtype != D_C_FORMAT) 
   {
   SCTPUT ("**** Bad reference Column...");
   return (ERR_TBLCOL);
   }

switch (rtype) 
   {
   case D_R4_FORMAT:
   case D_R8_FORMAT:	sread = TCERDD;	break;

   case D_C_FORMAT:	sread = TCERDC;	break;

   default:		sread = TCERDI; break;    
   }

for (irow=1; (status == ERR_NORMAL) && (irow <= nrow); irow++) 
   {
   status = (*sread)(in, irow, iref, &sval, &null);
   if (status)	continue;
   if (null)    continue;

   TCSGET (in, irow, &sel);	
   if (!sel)   continue;

   if (sread == TCERDI)	
      status = TCESRI(out, oref, *(int *)&sval, 0, 1, &orow);
   else if (sread == TCERDD)	
      status = TCESRD(out, oref, sval, 0.0e0, 1, &orow);
   else 
      status = TCESRC(out, oref, (char *)&sval, 1, rbytes, 1, &orow);

   if ((status) || (orow <=0)) continue;

   ocount ++;

   /* TCEMAP gives problems for big tables...
      so we always use TCERDD and TCEWRD (KB 070129) 

  	if (bytes) {			  /. Straight Copy. Use TCEMAP	./
		if (status = TCEMAP (in, irow, icol, &ip, &null)) continue;
		if (status = TCEMAP (out,orow, ocol, &op, &null)) continue;
		oscopy (op, ip, bytes);
                TCEUNM(in,ip);
                TCEUNM(out,op);
	}
	else { ... }         */

   if (itype == D_C_FORMAT)
      {
      cval = (char *) value;
      status = TCERDC(in, irow, icol, cval, &null);
      }
   else
      {
      status = TCERDD(in, irow, icol, value, &null);
      }


   if (status) continue;

   if (null)  
      status = TCEDEL(out, orow, ocol);
   else	 
      {
      if (itype == D_C_FORMAT)
         status = TCEWRC(out, orow, ocol, cval);
      else
         status = TCEWRD(out, orow, ocol, value);
      }
   }

*nout = ocount;
return (status);
}
