/*===========================================================================
  Copyright (C) 1995-2007 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        tbjoin.c
.LANGUAGE    C
.AUTHOR      IPG-ESO Garching
.CATEGORY    table utilities

.COMMENTS       This module implements the following Midas commands:
\begin{TeX}
\begin{enumerate}
\item {\tt JOIN/TABLE} table1 :X1,[:Y1]  table2 :X2,[:Y2]
\end{enumerate}
\end{TeX}

.VERSION 1.0 10-May-1992  Definition  M.Peron

 070503		last modif

----------------------------------------------------------------------*/

#include <stdlib.h>
#include <tbldef.h>
#include <tblerr.h>
#include <midas_def.h>
#include <macrogen.h>
#include <proto_tbl.h>

#define PARLEN 80
#define STRINGLEN 256
#define COLS 2

struct tree {
     double xval;
     double yval;
     int row;
     struct tree *left;
     struct tree *right;
};
struct tree *head;

int lines, tid1, tid2, tod, *ic1, *ic2, *oc1, *oc2;
int ncol1, ncol2;
int *coltype1, *coltype2;
int flc;

int tbl_argc(), tbl_getarg();

char *osmmget();

void insiderect();

/*

*/

int tbl_join()

{
char *intable1,*intable2,*outtable,*incol1,*incol2;
char strsimple[STRINGLEN];
char form[1+TBL_FORLEN],unit[1+TBL_UNILEN],label[1+TBL_LABLEN];
char parm[6][PARLEN],cval[STRINGLEN];
char *cptr;

int npar,status,dummy,i1;
int i,j,nbyt1,nbyt2,nsiz;
int icol1[COLS],icol2[COLS],flag[COLS];
int nrow1,nrow2,ncol,nrow,lerr;
int type,type1,sel,snull,nbytes,bytes,alen,out,cop;
int first,nc;
int *jptr, *null;

void findtree();

float rerr;

double dval[COLS],derr[COLS];
double rect[4];
double dvalue;

typedef int (*FUNCTION)();

struct tree *s2dtree();



npar = tbl_argc();
intable1 = intable2 = outtable = incol1 = incol2 = (char *)0;
lines = 0;
derr[0] = derr[1] = 0.0e0;
nbyt1 = nbyt2 = 0;
lerr = 0;
cop = 0;

for (i=0; i<npar; i++) 
   {
   tbl_getarg(i+1,PARLEN,parm[i]);
   if ((parm[i][0] == ':') || (parm[i][0] == '#')) 
      {
      if (!incol1) 
         incol1  = parm[i];
      else  
         incol2  = parm[i];
      }
   else 
      {
      if (!intable1) 
         intable1 = parm[i];
      else if (!intable2) 
         intable2 = parm[i];
      else if (!outtable)
         outtable = parm[i];
      }
   }


status = CGN_CNVT(parm[5],4,2,&lerr,&rerr,derr);
if (status < 1) 
   {
   SCTPUT("Invalid error value(s)");
   return (ERR_INPINV);
   }

cop = 1;
tid1 = tid2 = -1;
status = TCTOPN(intable1,F_I_MODE,&tid1);
        if (status != ERR_NORMAL) {
                SCTPUT("Error opening  table");
                return (status);
        }


status = TCTOPN(intable2,F_I_MODE,&tid2);
        if (status != ERR_NORMAL) {
                SCTPUT("Error opening  table");
                return (status);
        }


TCIGET(tid1,&ncol1,&nrow1,&dummy,&dummy,&dummy);
TCIGET(tid2,&ncol2,&nrow2,&dummy,&dummy,&dummy);

TCCSEL(tid1,incol1,COLS,icol1,flag,&nc);
TCCSEL(tid2,incol2,COLS,icol2,flag,&nc);

ncol = ncol1+ncol2;
nrow = MIN(nrow1,nrow2);		/* was: nrow = nrow1+nrow2; */

nsiz = sizeof(int)*ncol*7;		/* space for 7 int arrays */
cptr = osmmget(nsiz);
if (cptr == NULL) 
   {
   SCTPUT("Error allocating memory...");
   return ERR_INPINV;
   }

jptr = (int *) cptr;
null = jptr;
coltype1 = null + ncol;
coltype2 = coltype1 + ncol;
ic1 = coltype2 + ncol;
ic2 = ic1 + ncol;
oc1 = ic2 + ncol;
oc2 = oc1 + ncol;

TCBGET(tid1,icol1[0],&type,&alen,&nbytes);
status = TCTINI(outtable,F_TRANS,F_O_MODE,ncol,nrow,&tod);

j = 0;
for (i=1; i<=ncol1;i++) {
	*(ic1+i-1) = i;
        TCFGET(tid1,i,form,&alen,&dummy);
        if (dummy == D_C_FORMAT) 
           *(coltype1+i-1) = 1;
        else 
           *(coltype1+i) = 0; 
	nbyt1 = nbyt1+alen+1;
	TCLGET(tid1,i,label);
	TCUGET(tid1,i,unit);
	strcat(label,"_1");
	TCBGET(tid1,i,&type1,&alen,&bytes);
        if (type1 == D_C_FORMAT)
           status = TCCINI(tod,type1,bytes,form,unit,label,oc1+j);
	else
           status = TCCINI(tod,type1,alen,form,unit,label,oc1+j);

        if (type == D_C_FORMAT && alen !=1 ) TCAPUT(tod,*(oc1+j),alen);
	j++;
	}

j =0;
for (i=1; i<=ncol2;i++) {
	*(ic2+i-1) = i;
        TCFGET(tid2,i,form,&alen,&dummy);
        if (dummy == D_C_FORMAT)
           *(coltype2+i-1) = 1;
        else 
           *(coltype2+i) = 0; 
	nbyt2 = nbyt2+alen+1;
	TCLGET(tid2,i,label);
	strcat(label,"_2");
	TCUGET(tid2,i,unit);
	TCBGET(tid2,i,&type1,&alen,&bytes);
        if (type1 == D_C_FORMAT)
           status = TCCINI(tod,type1,bytes,form,unit,label,oc2+j);
	else
           status = TCCINI(tod,type1,alen,form,unit,label,oc2+j);

        if (type == D_C_FORMAT && alen !=1 ) TCAPUT(tod,*(oc2+j),alen);
        j++;
	}


switch (nc) 
   {
  case 1:
      if (type == D_C_FORMAT) 
         {
         for (i1=1; i1<=nrow1; i1++) 
            {
            TCSGET(tid1,i1,&sel);
            if (!sel) continue;

            first = 1;
            out = 1;
	    TCERDC(tid1,i1,*icol1,cval,null);
	    if (*null) continue;

	    while (out > 0) 
               {
	       status = TCESRC(tid2,icol2[0],cval,1,nbytes,first,&out);
	       if ((status) || (out <=0)) continue;

               lines = lines+1;
               for (i=0; i<ncol1; i++)
                  {
                  if (coltype1[i] == 1) 
                     {
                     TCERDC(tid1,i1,ic1[i],strsimple,&snull);
                     if (!snull) TCEWRC(tod,lines,oc1[i],strsimple);
                     }
                  else 
                     {
                     TCERDD(tid1,i1,ic1[i],&dvalue,&snull);
                     if (!snull) TCEWRD(tod,lines,oc1[i],&dvalue);
                     }
                  }
               for (i=0; i<ncol2; i++) 
                  {
                  if (coltype2[i] == 1) 
                     {
                     TCERDC(tid2,out,ic2[i],strsimple,&snull);
                     if (!snull) TCEWRC(tod,lines,oc2[i],strsimple);
                     }
                  else 
                     {
                     TCERDD(tid2,out,ic2[i],&dvalue,&snull);
                     if (!snull) TCEWRD(tod,lines,oc2[i],&dvalue);
                     }
                  }

	       first=out+1;
	       if (first > nrow2) break;
	       }
	    }
         }

      else 
         {
         for (i1=1; i1<=nrow1; i1++)		/* loop over rows of tid1 */
            {
            TCSGET(tid1,i1,&sel);
            if (!sel) continue;

            first = 1;
            out = 1;
            TCRRDD(tid1,i1,nc,icol1,dval,null);	/* read value of refcol */ 
            if (*null) continue;

            while (out > 0)
               {
               status = TCESRD(tid2,*icol2,dval[0],derr[0],first,&out);
               if ((status) || (out <=0)) continue;

               lines = lines+1;
               for (i=0; i<ncol1; i++)
                  {

                  if (*(coltype1+i) == 1) 
                     {
                     TCERDC(tid1,i1,*(ic1+i),strsimple,&snull);
                     if (!snull) TCEWRC(tod,lines,*(oc1+i),strsimple);
                     }
                  else 
                     {
                     TCERDD(tid1,i1,*(ic1+i),&dvalue,&snull);
                     if (!snull) TCEWRD(tod,lines,*(oc1+i),&dvalue);
                     }
                  }

               for (i=0; i<ncol2; i++) 
                  {
                  if (*(coltype2+i) == 1) 
                     {
                     TCERDC(tid2,out,*(ic2+i),strsimple,&snull);
                     if (!snull) TCEWRC(tod,lines,*(oc2+i),strsimple);
                     }
                  else 
                     {
                     TCERDD(tid2,out,*(ic2+i),&dvalue,&snull);
                     if (!snull) TCEWRD(tod,lines,*(oc2+i),&dvalue);
                     }
                  }

               first=out+1;
               if (first > nrow2) break;
	       }
            }
         }
       break;

       case 2:
	head = (struct tree *) 0;
        for (i1=1; i1<=nrow2; i1++) {
        TCSGET(tid2,i1,&sel);
        if (!sel) continue;
        first = 1;
        out = 1;
	flc =0;
        TCRRDD(tid2,i1,nc,icol2,dval,null);      
        if (*null || *(null+1)) continue;
	if (!head) head = s2dtree(head,head,dval[0],dval[1],i1);
	else s2dtree(head,head,dval[0],dval[1],i1);
        }
	for (i1=1; i1<=nrow1; i1++){
        TCSGET(tid1,i1,&sel);
        if (!sel) continue;
        TCRRDD(tid1,i1,nc,icol1,dval,null);      
        rect[0] = dval[0]-derr[0];
	rect[1] = dval[0]+derr[0];
	rect[2] = dval[1]-derr[1];
	rect[3] = dval[1]+derr[1];
	flc = 0;
        findtree(head,head,rect,i1);
}
}
TCTCLO(tid1);
TCTCLO(tid2);
TCTCLO(tod);

free(jptr);
return(ERR_NORMAL);

}

/*

*/

struct tree *s2dtree(root,r,xval,yval,row)
struct tree *root;
struct tree *r;
double  xval,yval;
int row;
{

if (!r) {
	r = (struct tree *)osmmget(sizeof(struct tree));
	r->left = (struct tree *)0;
	r->right = (struct tree *)0;
	r->xval = xval;
	r->yval = yval;
	r->row  = row;
	if (!root) return r;
	if (flc == 1) {
     	   if (yval < root->yval) root->left = r;
	   else root->right = r;
           }
        else {
	   if (xval < root->xval) root->left = r;
	   else root->right = r;
	   }
	return r;
	}
if (flc == 0){
	   flc = 1;
	   if (yval < r->yval) s2dtree(r,r->left,xval,yval,row);
	   else
	   if (yval >= r->yval) s2dtree(r,r->right,xval,yval,row);
           }
else       {
	   flc = 0;
           if (xval < r->xval) s2dtree(r,r->left,xval,yval,row);
	   else
           if (xval >= r->xval) s2dtree(r,r->right,xval,yval,row);
   	   }
return ((struct tree*) NULL);
}


void findtree(head,r,rect,row1)
    struct tree *head;
    struct tree *r;
    double rect[4];
    int row1;
{
if (flc == 0) {
     if (r->yval < rect[2]) {
		  flc = 1;
		  if (r->right) findtree(head,r->right,rect,row1); 
	          } 
     else if (r->yval > rect[3]) {
		  flc = 1;
		  if (r->left) findtree(head,r->left,rect,row1);
		  }
     else {
	 insiderect(r,rect,0,row1);
	 flc = 1;
	 if (r->left) findtree(head,r->left,rect,row1);
	 flc = 1;
         if (r->right) findtree(head,r->right,rect,row1);
	 }
     }
else  {
     if (r->xval < rect[0]) {
		  flc = 0;
		  if (r->right) findtree(head,r->right,rect,row1);
		  }
     else if ( r->xval > rect[1]){
		  flc = 0;
		  if (r->left) findtree(head,r->left,rect,row1);
		  }
     else {
	   insiderect(r,rect,1,row1);
	   flc = 0;
	   if (r->left) findtree(head,r->left,rect,row1);
	   flc = 0;
           if (r->right) findtree(head,r->right,rect,row1);
     }
}
}

/*

*/

void insiderect(r,rect,b,row1)
struct tree *r;
double rect[4];
int b,row1;

{
int i, null;
double dvalue;
char strsimple[STRINGLEN];




if (b==1) {
    if (r->yval >= rect[2] && r->yval <= rect[3]) {
       lines = lines+1;
       for (i=0; i<ncol1; i++) {
           if (coltype1[i] == 1) {
                  TCERDC(tid1,row1,ic1[i],strsimple,&null);
                  if (!null) TCEWRC(tod,lines,oc1[i],strsimple);
                  }
           else {
                  TCERDD(tid1,row1,ic1[i],&dvalue,&null);
                  if (!null) TCEWRD(tod,lines,oc1[i],&dvalue);
                 }
           }
       for (i=0; i<ncol2; i++) {
           if (coltype2[i] == 1) {
                  TCERDC(tid2,r->row,ic2[i],strsimple,&null);
                  if (!null) TCEWRC(tod,lines,oc2[i],strsimple);
                  }
           else {
                  TCERDD(tid2,r->row,ic2[i],&dvalue,&null);
                  if (!null) TCEWRD(tod,lines,oc2[i],&dvalue);
                }
           }
       }
    }  
else {
    if (r->xval >=rect[0] && r->xval <= rect[1]) {
       lines = lines+1;
       for (i=0; i<ncol1; i++) {
           if (coltype1[i] == 1) {
                  TCERDC(tid1,row1,ic1[i],strsimple,&null);
                  if (!null) TCEWRC(tod,lines,oc1[i],strsimple);
                  }
           else {
                  TCERDD(tid1,row1,ic1[i],&dvalue,&null);
                  if (!null) TCEWRD(tod,lines,oc1[i],&dvalue);
                 }
           }
       for (i=0; i<ncol2; i++) {
           if (coltype2[i] == 1) {
                  TCERDC(tid2,r->row,ic2[i],strsimple,&null);
                  if (!null) TCEWRC(tod,lines,oc2[i],strsimple);
                  }
           else {
                  TCERDD(tid2,r->row,ic2[i],&dvalue,&null);
                  if (!null) TCEWRD(tod,lines,oc2[i],&dvalue);
                }
           }

       }
    }
}

