/*===========================================================================
  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
===========================================================================*/

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.TYPE        Module
.NAME        TZ1.C
.LANGUAGE    C
.AUTHOR      J.D.Ponz  IPG-ESO Garching
.CATEGORY    table interface (Design 2.0) low level routines.
.COMMENTS    This module contains the lower level routines handling tables.
             The module includes the functions:
             - Filename checking.
             - Low level error handling.
             - Format, label and type checking.
             - Logical to physical column correspondences. 

.VERSION  1.0 	25-Mar-1987   Implementation     JDP
.VERSION  1.1 	20-Oct-1987   Modify calling sequence 
.VERSION  1.2 	05-Jun-1989   Add integer data type
.VERSION  3.0 	01-Jul-1990   Extracted from tz2

 090406		last modif
------------------------------------------------------------*/

#include <midas_def.h>		/* General MIDAS Symbols	*/
#include <tblsys.h>		/* Table System parameters	*/
#include <tbldef.h>		/* Symbols used for Tables	*/
#include <tblerr.h>		/* List of Table Errors		*/

#include <atype.h>		/* Character classification	*/
#include <macrogen.h>		/* Classical Macros		*/

#include <stdlib.h>
#include <stdio.h>




#ifdef __STDC__
static char *atype(int ttype)
#else
static char *atype(ttype)
/*+++++++++++
.PURPOSE Convert table numeric datatype into understandable ones
.RETURNS The data-type
--------------*/
	int	ttype;	/* IN: The numeric datatype	*/
#endif
{
	static	char fmt[20];
	char	*p;



p = '\0';

  switch(TBL_ElementType(ttype)) {
    case TBL_D_A1:	p = "C*%d";	break;
    case TBL_D_I1:	p = "I*1(%d)";	break;
    case TBL_D_I2:	p = "I*2(%d)";	break;
    case TBL_D_I4:	p = "I*4(%d)";	break;
    case TBL_D_A2:	p = "L*2(%d)";	break;
    case TBL_D_A4:	p = "L*4(%d)";	break;
    case TBL_D_R4:	p = "R*4(%d)";	break;
    case TBL_D_R8:	p = "R*8(%d)";	break;
  }
  sprintf(fmt, p, TBL_Items(ttype));
  return(fmt);
}

/*==========================================================================*/

int TBL_offset(tp, row, col)
/*+++++++++++
.PURPOSE Compute position in table from row + col
.RETURNS Offset in bytes
--------------*/
	TABLE	*tp;	/* IN: Table concerned	*/
	int	row;		/* IN: Row	*/
	int	col;		/* IN: Column	*/
{
	int	o;
	int	i, j;
	
  i = row-1;
  j = col-1;

  if (col > 0) {
  	if (tp->swise == F_RECORD ) 	
	        o = tp->offset[j] + i * tp->reclen;
        else 	o = tp->offset[j] * tp->arows + i * tp->bytes[j];
  } 
  else	{				/* Selection Column	*/
  	if (tp->swise == F_RECORD)  	
                o = i * tp->reclen;
        else    o = i * 4;
  }

  return (o);
}

/*==========================================================================*/

char *TBL_Dlab(col)
/*++++++++
.PURPOSE Generate the column label descriptor
.RETURNS Address of descriptor's name
-----------*/
	int	col;	/* IN: Column number		*/
{
	static	char buf[sizeof(TBL_Dlabel)+3] = TBL_Dlabel;
	int	i, n;
	
  n = col;
  i = sizeof(buf);
  buf[--i] = '\0';
  buf[--i] = '0' + n%10;	n /= 10;
  buf[--i] = '0' + n%10;	n /= 10;
  buf[--i] = '0' + n;

  return(buf);
}	

/*==========================================================================*/

char *TBL_Cfmt(form, ttype)
/*++++++++++++++++++++
.PURPOSE Convert format from F77 into C
.RETURNS Translated format / "" if error.
	The returned string contains:
	%...A 	for C-edit
	H    	for convert to hours (/15.)
	Z    	for zero-fill
	+	for sing edition
	Sx.y    for Sexagesimal	(e.g. S12.4:), last byte tells the sep. char.
	Dx.y    for Date edition
	
---------------------*/
	char	*form;	/* IN: F77 Format */
	int	ttype;	/* IN: Table-specific data type	*/
{
	static  char chReturnArray[16] = "%";
	int     j;
	int     idot;
	int     dtype;
	int     dec;      /* what's that?*/
	
	char	*pchReturn;  /* pointer to char, will be returned */
	char    add_sign;
	char    zero_fill;

  dtype = TBL_ElementType(ttype);
  
  pchReturn = &chReturnArray[2]; /* why are chReturnArray[0] and [1] not used??? */
  *pchReturn = '%';   /* pchReturn[0] = chReturnArray[2] = '%' */
  idot = 0;

  /*step through form and search the '.', if existent*/  
  for (j=1; (j < sizeof(chReturnArray)-6) && isgraph(form[j]); j++) 
  {
	if (form[j] == '.')
		idot = j+1; 	   /* why +1? why not only j? */
  	pchReturn[j] = form[j];    /* fill pchReturn - why not chReturnArray? */
  }
  
  add_sign  = islower(*form);
  zero_fill = 0;

/*j equals now the length of form e.g. for Tww.dd six, for Iww three*/
  
  switch (form[0]) 		/* letter in form[0] gives format */
  {
    case 'A':			/* characters */
    	pchReturn[j] = 's';
    	break;
    case 'F': case 'f':
    	if (dtype & 020)
		pchReturn[j] = 'f';
    	else 
	{			/*  */
		if (idot != 0)	
			j = idot-1;
    		pchReturn[j] = (dtype & 010 ? 'd' : 'u');
    	}
    	break;
/*    case 'E': case 'G': case 'D': case 'e':*/
      case 'D': case 'e': case 'E':
	if (idot != 0) 		/* only true if '.' found */
	{			/* this is important! 1/99 */
		dec = atoi(form+idot); /**/
		if (dec > 0)	
			dec--;
		sprintf(pchReturn+idot, "%de", dec);
	}
	else			/* if no '.' found, use format e        */
	                        /* problems may occur here:             */
				/* for negative values, exponent missing*/
				/* therefore check for . */ 
				/* when E,D,e column are created */
		pchReturn[j] = 'e';
	break;
    case 'G':			/* G didn't work before 10/98*/
        pchReturn[j] = 'G';
	break;
    case 'I': case 'i':        /* integer */
    	if (dtype & 020)
		pchReturn[j++] = '.', pchReturn[j++] = '0', pchReturn[j] = 'f';
    	else
		pchReturn[j] = (dtype & 010 ? 'd' : 'u');
	break;
    case 'R': case 'r':  /* Right ascension, e.g. 12 34 56*/
    	j++;
	*--pchReturn = 'H';	/* NO BREAK */
	/* Fall through */
    case 'S': case 's': 
    	chReturnArray[2] = 'S';		/* Sexagesimal */
    	pchReturn[j]  = ' ';		/* Separator between mn / sec	*/
	break;
    case 'T': 
    	chReturnArray[2] = 'T';		/* Sexagesimal  */
    	pchReturn[j]  = ' ';		/* Numeric...	*/
	break;
    case 't': 
    	chReturnArray[2] = 'T';		/* Sexagesimal  */
    	pchReturn[j]  = 'A';		/* Numeric...	*/
    	add_sign = 0;
	break;
    case 'Z': case 'z': 		/* Zero filled */
    	pchReturn[j] = (dtype & 020 ? 'f' : (dtype & 010 ? 'd': 'u'));
    	zero_fill = 1;
	break;
    case 'O': case 'o':		/* Octal (format O didn't work before PNOct98)*/
        pchReturn[j] = 'o';
	break;
    case 'X': case 'x': 	/* Hexa */
    	add_sign = 0;		/* NO BREAK */	
	/* Fall through */
    default:
    	pchReturn[j] = form[0];		/* when will default be used at all? */
	break;
  }

  pchReturn[++j] = '\0';
  if (zero_fill != 0)
  	pchReturn[++j] = 'Z';	      /* why something written in pchReturn behind \0???*/
  if (add_sign != 0)
  		pchReturn[++j] = '+'; /* why something written in pchReturn behind \0???*/
  pchReturn[++j] = '\0';   	      /* why that again??? */

  return(pchReturn);
}

int TBL_FMTCHK(form, ttype, oform)
/*+++++++++
.PURPOSE Check F77 format vs datatype
.RETURNS Status. 
.REMARKS Error is logged if something wrong.
----------*/
	char	*form		/* IN: format F77 			*/;
	int	ttype		/* IN: Table coded type			*/;
	char	*oform		/* OUT: output format and length	*/;
{	
	int	status, len, i, dec;
	char	*table;

		/* Default Length according to datatype	*/

  static char d_i1[] = "F5.0\0f5.0\0I4\0i4\0O3\0o3\0X2\0x2\0Z4\0z4\0";
  static char d_i2[] = "F7.0\0f7.0\0I6\0i6\0O6\0o6\0X4\0x4\0Z6\0z6\0";
  static char d_a2[] = "F6.0\0f6.0\0I5\0i5\0O6\0o6\0X4\0x4\0Z6\0z6\0";
  static char d_i4[] = "F12.0\0f12.0\0I11\0i11\0O11\0o11\0X8\0x8\0Z11\0z11\0\
t20.6\0T19.6\0";
  static char d_a4[] = "F11.0\0f11.0\0I10\0i10\0O11\0o11\0X8\0x8\0Z10\0z10\0";
  static char d_r4[] = "E12.6\0e12.6\0F12.6\0f12.6\0G12.6\0I12\0i12\0\
R10.5\0r11.5\0S9.4\0s9.4\0Z12.6\0z12.6\0";
  static char d_r8[] = "E12.6\0e12.6\0F12.6\0f12.6\0G12.6\0D12.6\0I12\0i12\0\
R12.7\0r13.7\0S12.6\0s12.6\0t20.6\0T19.6\0Z12.6\0z12.6\0";

  status = ERR_NORMAL;
  dec    = 0;
  for (i=0; form[i]; i++){
  	oform[i] = form[i];
  	if (oform[i] == '.')	dec = i;
  }
  strcpy(oform,form);
  len = atoi(oform+1);
  if (dec)	dec = atoi(oform+1+dec);
  

				/* Check type, no default allowed */
  switch (TBL_ElementType(ttype)) {
    case TBL_D_A1:	    		/* String...	*/
    	if (form[0] != 'A' && form[0] != 'a' )	status = ERR_TBLFMT;  
    	if (len <= 0)	sprintf(oform+1, "%d", TBL_Items(ttype));
    	break;
    case TBL_D_I1:	table = d_i1;	goto check_table;
    case TBL_D_I2:	table = d_i2;	goto check_table;
    case TBL_D_I4:	table = d_i4;	goto check_table;
    case TBL_D_A2:	table = d_a2;	goto check_table;
    case TBL_D_A4:	table = d_a4;	goto check_table;
    case TBL_D_R4:	table = d_r4;	goto check_table;
    case TBL_D_R8:	table = d_r8;	goto check_table;
    check_table:
    	while (*table) {
		if (*table == *oform)	break;
		table += 1 + strlen(table);
    	}
	if (*table) {
		if (len == 0)	strcpy (oform, table);
	}
	else	status = ERR_TBLFMT;  
  }

  if (len && (len < (dec+1)))	status = ERR_TBLFMT;
  for (i = strlen(oform); i < 6; i++)	oform[i] = ' ';

#if 0						/* Add length 	*/
  len = atoi(oform+1);
  sprintf(&oform[6],"%3d", len);
#else
  oform[6] = oform[7] = oform[8] = '=';
#endif

  if (status)	TBL_errf(status, "datatype '%s' incompatible with format '%s'",
  	atype(ttype), form);
  return (status);
}

int TBL_UNICHK(unit, ounit)
/*+++++++++
.PURPOSE Check Units (just remove quotes of existing)
.RETURNS Status
----------*/
	char	*unit		/* IN: Unit		*/;
	char	*ounit		/* OUT: output unit 	*/;
{	
	int	status, i, j;

  status = ERR_NORMAL;
                                        /* remove double quotes*/
                                        /* MP , dec 1989       */
  j = (*unit == '"' ? 1 : 0);
  for (i=0; (i<TBL_UNILEN) && unit[j]; i++, j++)	ounit[i] = unit[j];
  	
  if ( (*unit == '"') && (i > 0) ) {
	if (ounit[--i] != '"')	i++;
  }
					/* Fill with blanks	*/
  while (i < TBL_UNILEN)	ounit[i++] = ' ';
  ounit[i] = '\0';

  return (status);
}

/*==========================================================================*/
int TBL_LABCHK(label, outlab)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE
  	Check label: must start with alphabetic / _ .
  	Truncted to LABLEN.
  	The starting : is removed.
.RETURNS
        status
------------------------------------------------------------------*/
	char	*label		/* IN: input label 			*/;
	char	*outlab		/* OUT: output label (exactly LABLEN)	*/;
{
	char	*p, c;
	int	status, i, j;

  status = ERR_NORMAL;
  p = label;
  if (*p == ':')	p++;
  
  for (i=j=0; (j < TBL_LABLEN) && isgraph(p[i]); i++) {
  	c = p[i];
	if (isalpha(c) || (c == '_'))	;	/* OK	*/
	else if (isdigit(c))  {
		if (i == 0)	status = ERR_TBLABL, outlab[j++] = '_';
	}
	else c = '_', status = ERR_TBLABL;
        outlab[j++] = c;
  }
  while (j < TBL_LABLEN) outlab[j++] = ' ';
  outlab[j] = '\0';
  
  if (status)	TBL_errf(-1, "column name :%s converted to :%s", p, outlab);
  return (ERR_NORMAL);
}

int TBL_TYPCHK(dtype, alen, icode)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE
  	Check external data type and generates TABLE code.
  	Implements TBL_TYPCHK
.RETURNS
	Status.
------------------------------------------------------------------*/
	int	dtype		/* IN: MIDAS data type 		*/;
	int	alen		/* IN: number of items		*/;
	int	*icode		/* OUT: Table-specific data type*/;
{
	int	status;

  status = ((alen > 0) && (alen <= TBL_D_MASK) ? ERR_NORMAL : ERR_TBLFMT);
  if (status)
  	return (TBL_errf(status, "bad size (%d) for table column", alen));
  
  switch (dtype) {		/* branch on data type */
    case D_I1_FORMAT:
	*icode   = TBL_D_I1 << TBL_D_BITS;	break;
    case D_I2_FORMAT:
	*icode   = TBL_D_I2 << TBL_D_BITS;	break;
    case D_I4_FORMAT:
	*icode   = TBL_D_I4 << TBL_D_BITS;	break;
    case D_R4_FORMAT:
	*icode   = TBL_D_R4 << TBL_D_BITS;	break;
    case D_R8_FORMAT:
	*icode   = TBL_D_R8 << TBL_D_BITS;	break;
    case D_C_FORMAT:
    case D_X_FORMAT:
    case D_L1_FORMAT:
	*icode   = TBL_D_A1 << TBL_D_BITS;	break;
    case D_UI2_FORMAT:
    case D_L2_FORMAT:
	*icode   = TBL_D_A2 << TBL_D_BITS;	break;
    case D_L4_FORMAT:
    case D_P_FORMAT:
	*icode   = TBL_D_A4 << TBL_D_BITS;	break;
    default :
	status = ERR_TBLFMT;
	TBL_errf(status, "bad numeric datatype (%d)", dtype);
	*icode   = 0;
	break;
  }
  *icode |= alen;
  return (status);
}
