#include "xvt.h"

#include <cfiles.h>
#include <csort.h>
#include <stdio.h>

#define MOSTMEM  51200
#define LEASTMEM 10240

/*----------------------- STATIC PER PRIMA PARTE ----------------------------*/

static struct s_prm datisort;
static int nsortfield;

/*----------------------- STATIC PER SECONDA PARTE --------------------------*/

static struct s_prm *sp;
/* @(!) 2.3.00.112 */
static unsigned long totrcd;
static unsigned no_seq;
static unsigned no_seq1;
/* @(:) 2.3.00.112 */
static unsigned bspace;
/* @(!) 2.3.00.112 */
static unsigned nrcds;
static unsigned nrcds1;
/* @(:) 2.3.00.112 */
static char *bf, *bf1;
/* @(!) 2.3.00.112 */
static unsigned inbf;
/* @(:) 2.3.00.112 */
static char **sptr;
static char *init_sptr;
/* @(!) 2.3.00.112 */
static unsigned rcds_seq;
/* @(:) 2.3.00.112 */
static FILE *fp1, *fp2;
static char fdname [42];
static char f2name [42];

//static int sortcomp(char **, char **);
static int sortcomp(const void*, const void*);
static char *appr_mem(unsigned *);
/* @(!) 2.3.00.112 */
static FILE *wopen(char *);
/* @(:) 2.3.00.112 */
static void dumpbuff(void);
static void merge(void);
static void prep_merge(void);
/* @(!) 2.3.01.245 */
void  dummy(void);
/* @(:) 2.3.01.245 */

/*----------------------- PRIMA PARTE ---------------------------------------*/

/*
   @(#) init_sort  SORT

   @(ID)
   Inizializza le variabili globali per il sort.
   @(FD)
   */

/* struttura di descrizione del sort */
int init_sort(struct s_prm *prms)
{
  sp = prms;
  if ((bf = appr_mem(&bspace)) != NULL)   {
    nrcds1 = nrcds = bspace / (sp->rc_len + sizeof(char *));
    init_sptr = bf;
    sptr = (char **) bf;
    bf += nrcds * sizeof(char *);
    fp1 = fp2 = NULL;
    totrcd = no_seq = inbf = 0;
    return 0;
  }
  else
    return -1;
}

/*
   @(#) sort  SORT

   @(ID)
   Funzione per l'input dei record da ordinare.
   @(FD)

   @(IN)
   La chiamata sort(char *) NULL) chiude la fase di input dei record.
   @(FN)
   */

void sort(s_rcd)
  char *s_rcd; /* buffer contenente il record da ordinare */

{
  /* @(!) 2.3.00.112 */
  if (inbf == nrcds)  { /* if the sort buffer is full */
    /* @(:) 2.3.00.112 */
    qsort(init_sptr, inbf, sizeof (char *), sortcomp);
    if (s_rcd)  {   /* if there are more records to sort  */
      dumpbuff(); /* dump the buffer to a sort work file*/
      no_seq++; /* count the sorted sequences       */
    }
  }
  if (s_rcd !=NULL) {
    /* --- this is a record to sort --- */
    totrcd++;
    /* --- put the rcd addr in the pointer array --- */
    *sptr = bf + inbf * sp->rc_len;
    inbf++;
    /* --- move the rcd to the buffer --- */
    memcpy(*sptr, s_rcd, sp->rc_len);
    sptr++;       /* point to next array entry*/
  }
  else {                    /* null pointer means no more rcds */
    if (inbf)   {           /* any records in the buffer?     */
      qsort(init_sptr, inbf, sizeof (char *), sortcomp);
      if (no_seq)     /* if this isn't the only sequence*/
        dumpbuff();   /* dump the buffer to a work file */
      no_seq++;       /* count the sequence           */
    }
    no_seq1 = no_seq;
    if (no_seq > 1)   /* if there is more than 1 sequence */
      prep_merge();   /* prepare for the merge            */
  }
}
/*
   @($) prep_merge  SORT

   @(ID)
   Funzione per la preparazione del Merge.
   @(FD)

   @(ISV)
   i      = contatore.

   rr     = puntatore ad una struttura di sequenza (di lavoro).

   n_bfsz = ampiezza in byte del merge buffer.

   @(FSV)
   */

static void prep_merge()
{
  unsigned i;
  struct bp *rr;
  unsigned n_bfsz;

  memset(init_sptr, '\0', bspace);
  /* -------- merge buffer size ------ */
  n_bfsz = bspace - no_seq * sizeof(struct bp);
  /* ------ # rcds/seq in merge buffer ------- */
  rcds_seq = n_bfsz / no_seq / sp->rc_len;
  if (rcds_seq < 2) {
    /* ---- more sequence blocks than will fit in buffer,
       merge down ---- */
    while (rcds_seq < 2)    {
      FILE *hd;
      /* @(!) 2.3.00.112 */
      char wname[42];         /* sort work name                 */

      fp2 = wopen(f2name);    /* open a sort work file */
      /* @(:) 2.3.00.112 */
      merge();                /* binary merge */
      hd = fp1;               /* swap fds */
      fp1 = fp2;
      fp2 = hd;
      /* @(!) 2.3.00.112 */
      strcpy(wname, fdname);
      strcpy(fdname, f2name);
      strcpy(f2name, wname);
      fclose(fp2);
      xvt_fsys_remove_file(f2name);
      fp2 = NULL;
      /* @(:) 2.3.00.112 */
      nrcds *= 2;
      /* ------ adjust number of sequence ------ */
      no_seq = (no_seq + 1) / 2;
      n_bfsz = bspace - no_seq * sizeof(struct bp);
      rcds_seq = n_bfsz / no_seq / sp->rc_len;
    }
  }
  bf1 = init_sptr;
  rr = (struct bp *) init_sptr;
  bf1 += no_seq * sizeof(struct bp);
  bf = bf1;

  /* fill the merge buffer with records from all sequences */

  for (i = 0; i < no_seq; i++)      {
    fseek(fp1, (long) i * ((long) nrcds * sp->rc_len), 0);
    /* ------ read them all at once ------ */
    /* @(!) 2.3.00.112 modificata fread(bf1, rcds_seq * sp->rc_len, 1, fp1); */
    fread(bf1, sp->rc_len, rcds_seq, fp1);
    /* @(:) 2.3.00.112 */
    rr->rc = bf1;
    /* --- the last seq has fewer rcds than the rest --- */
    if (i == no_seq-1)  {
      unsigned nrcd_last = (unsigned) totrcd % nrcds;
      if (nrcd_last == 0)
      	nrcd_last = nrcds;
      if (nrcd_last > rcds_seq)      {
        rr->rbuf = rcds_seq;
        /* @(!) 2.3.00.112 */
        rr->rdsk = nrcd_last - rcds_seq;
        /* @(:) 2.3.00.112 */
      }
      else        {
        /* @(!) 2.3.00.112 */
        rr->rbuf = nrcd_last;
        /* @(:) 2.3.00.112 */
        rr->rdsk = 0;
      }
    }
    else      {
      rr->rbuf = rcds_seq;
      rr->rdsk = nrcds - rcds_seq;
    }
    rr++;
    bf1 += rcds_seq * sp->rc_len;
  }
}

/* @(!) 2.3.01.245 */
/* Funzione  che non  fa assolutamente nulla,  ma  e'  estremamente 
   utile all'interno di sort_op, quando si supera una certa mole di 
   dati immessi  nel  buffer  di sort;  e' necessaria  solo  per la
   compilazione di eseguibili per 386. Questa funzione inserita tra
   le righe rtn= k+..... e memcpy(..) evita il  fastidioso   memory
   fault che si ottiene utilizzando strutture molto ampie  o  molto
   numerose. Probabilmente evita, in fase di compilazione, che vengano
   creati indirizzi strani, oppure in fase di esecuzione, che vengano
   reperite zone di memoria che non c'entrano per nulla.
   */
void dummy()
{
}
/* @(:) 2.3.01.245 */


/*
   @($) merge  SORT

   @(ID)
   Effettua un merge. 
   E' un merge binario di records dalla sequenza fp1 in fp2.
   @(FD

   @(ISV)
   i   = contatore.

   needy,needx = se true necessita un record da (x/y).

   xcnt,ycnt   = numero di records lasciati in ogni sequenza.

   x,y         = contatori per le sequenze.

   adx,ady     = indirizzi sul disco delle sequenze di record.

   ysptr       = stringa di lavoro.
   @(FSV)
   */

static void merge()
{
  unsigned i;
  int needy, needx; /* true = need a rcd from (x/y)   */
  /* @(!) 2.3.00.112 */
  unsigned xcnt, ycnt;    /* # rcds left each sequence      */
  /* @(:) 2.3.00.112 */
  unsigned x, y;         /* sequence counters              */
  long adx, ady;      /* sequence record disk addresses */
  /* @(!) 2.3.00.112 */
  char   *ysptr = init_sptr + sp->rc_len;
  
  /* Calcolo dei records residui nell'ultima sequenza */
  /* nel caso in  cui sia piena */
  unsigned nrcd_last = (unsigned) totrcd % nrcds;

  if (nrcd_last == 0)
  	nrcd_last = nrcds;
  /* @(:) 2.3.00.112 */

  /* --- the two sets of sequences are x and y ----- */
  fseek (fp2, 0L, 0);
  for (i = 0; i < no_seq; i += 2)   {
    x = y = i;
    y++;
    ycnt =
      (y == no_seq) ? 0 : (y == no_seq - 1 ?
        /* @(!) 2.3.00.112 */
        nrcd_last : nrcds);
    xcnt = y == no_seq ? nrcd_last : nrcds;
    /* @(:) 2.3.00.112 */
    adx = (long) x * (long) nrcds * sp->rc_len;
    ady = adx + (long) nrcds * sp ->rc_len;
    needy = needx = 1;
    while (xcnt || ycnt)  {
      if (needx && xcnt)  { /* need a rcd from x? */
        fseek(fp1, adx, 0);
        adx += (long) sp->rc_len;
        fread(init_sptr, sp->rc_len, 1, fp1);
        needx = 0;
      }
      if (needy && ycnt)  { /* need a rcd from y? */
        fseek(fp1, ady, 0);
        ady += sp->rc_len;
        /* @(!) 2.3.00.112 modificata fread(init_sptr+sp->rc_len, sp->rc_len, 1, fp1); */
        fread(ysptr, sp->rc_len, 1, fp1);
        /* @(:) 2.3.00.112 */
        needy = 0;
      }
      if (xcnt || ycnt) { /* if anything is left */
        /* ---- compare the two sequences --- */
        /* @(!) 2.3.00.112 modificata if (!ycnt || (xcnt && (sortcomp(&init_sptr, &init_sptr + sp->rc_len)) < 0)) { */
        if (!ycnt || (xcnt && (sortcomp(&init_sptr, &ysptr)) < 0)) {
          /* @(:) 2.3.00.112 */
          /* ----- record from x is lower ---- */
          fwrite(init_sptr, sp->rc_len, 1, fp2);
          --xcnt;
          needx = 1;
        }
        else if (ycnt)  { /* record from y is lower */
          /* @(!) 2.3.00.112 */
          fwrite(ysptr, sp->rc_len, 1, fp2);
          /* @(:) 2.3.00.112 */
          --ycnt;
          needy = 1;
        }
      }
    }
  }
}
/*
   @($) dumpbuff  SORT

   @(ID)
   Copia il buffer di sort nel file di lavoro.
   @(FD)

   @(ISV)
   i = contatore.
   @(FSV)
   */

static void dumpbuff()
{
  /* @(!) 2.3.00.112 */
  unsigned i;
  /* @(:) 2.3.00.112 */

  if (fp1 == NULL)
    /* @(!) 2.3.00.112 */
    fp1 = wopen(fdname);
  /* @(:) 2.3.00.112 */
  sptr = (char **) init_sptr;
  for (i = 0; i < inbf; i++)  {
    fwrite(*(sptr + i), sp->rc_len, 1, fp1);
    /* @(!) 2.3.00.112 */
    *(sptr + i) = 0;
    /* @(:) 2.3.00.112 */
  }
  inbf = 0;
}

/*
   @($) wopen  SORT

   @(ID)
   Apre un file di lavoro temporaneo (per il sort).
   @(FD)

   @(ISV)
   fp     = puntatore al file temporaneo.

   s = stringa di formato.

   n      = numero del file.

   @(FSV)
   */

static FILE *wopen(char* name) /* nome del file temporaneo */
{
  FILE* fp = NULL;
	char* tmp = _tempnam(".", "srt");
  if (tmp != NULL)
	{
		strcpy(name, tmp);
		free(tmp);
	}
	else
		tmpnam(name);
  fp = fopen(name, "wb+");
  if (fp == NULL)  
    xvt_dm_post_fatal_exit("Can't open SORT file");
  return fp;
}

  /*
     @(#) sort_op  SORT

     @(ID)
     Ritorna il puntatore al primo record non ancora elaborato nella sequenza ordinata.
     @(FD)

     @(ISV)
     j         = e' true se una qualche sequenza contiene ancora dei records.

     i         = contatore.

     k,nrd,l   = variabili di lavoro.

     rr        = variabile di lavoro.

     r1        = variabile di lavoro.

     rtn       = indirizzo del buffer da ritornare.

     ad,tr     = variabili di lavoro.
     @(FSV)

     @(FN)
     Se la funzione sort_op ritorna NULL non ci sono piu' records nella sequenza ordinata.
     @(FN)
     */


  char *sort_op()
  {
    int j = 0;
    /* @(!) 2.3.00.112 */
    unsigned i, k, nrd, l;
    /* @(:) 2.3.00.112 */
    struct bp *rr;
    /* @(!) 2.3.00.112 */
    static unsigned r1 = 0;
    /* @(:) 2.3.00.112 */
    char *rtn;
    long ad, tr;

    if (init_sptr == NULL) return NULL;

    sptr = (char **) init_sptr;
    if (no_seq < 2) {
      /* -- with only 1 sequence, mo merge has been done -- */
      /* @(!) 2.3.00.112 */
      if (r1 == (unsigned) totrcd)  {
        /* @(:) 2.3.00.112 */
        if (init_sptr != NULL) free(init_sptr);
        /* @(!) 2.3.00.112 */
        init_sptr = NULL;
        /* @(:) 2.3.00.112 */
        fp1 = fp2 = NULL;
        r1 = 0;
        return NULL;
      }
      return *(sptr + r1++);
    }


    rr = (struct bp *) init_sptr;
    for (i = 0; i < no_seq; i++)
      /* @(!) 2.3.00.112 */
      j |= (int) (rr + i)->rbuf | (rr + i)->rdsk;
    /* @(:) 2.3.00.112 */

    /* -- j will be true if any sequence still has records - */
    if (!j)
    {
      fclose(fp1);            /* none left */
      xvt_fsys_remove_file(fdname);
      /* @(!) 2.3.00.112  eliminata if (fp2) */
      /* @(!) 2.3.00.112  eliminata { */
      /* @(!) 2.3.00.112  eliminata fclose(fp2); */
      /* @(!) 2.3.00.112  eliminata unlink(f2name); */
      /* @(!) 2.3.00.112 eliminata } */
      /* @(:) 2.3.00.112 */
      if (init_sptr != NULL) free(init_sptr);
      /* @(!) 2.3.00.112 */
      init_sptr = NULL;
      /* @(:) 2.3.00.112 */
      fp1 = fp2 = NULL;
      r1 = 0;
      return NULL;
    }
    k = 0;

    /* --- find the sequence in the merge buffer
       with the lowest record --- */
    /* for (i = 0; i < no_seq; i++) */
     for (i = 1; i < no_seq; i++)
      k = ((sortcomp( &(rr + k)->rc, &(rr + i)->rc) < 0) ? k : i);


    /* --- k is an integer sequence number that offsets to the
       sequence with the lowest record ---- */

    (rr + k)->rbuf--;         /* decrement the rcd counter */
    rtn = (rr + k)->rc;       /* set the return pointer */
    (rr + k)->rc += sp->rc_len;
    if ((rr + k)->rbuf == 0)    {
      /* ---- the sequence got empty ---- */
      /* --- so get some more if there are any --- */
      rtn = bf + k * rcds_seq * sp->rc_len;
      /* @(!) 2.3.01.245 */
      dummy();
      /* @(:) 2.3.01.245 */ 
      memcpy(rtn, (rr + k)->rc - sp->rc_len, sp->rc_len);
      (rr + k)->rc = rtn + sp->rc_len;
      if ((rr + k)->rdsk != 0)    {
        l = ((rcds_seq-1) < (rr+k)->rdsk) ? rcds_seq-1 : (rr+k)->rdsk;
        /* @(!) 2.3.00.112 */
        nrd = k == no_seq - 1 ? (unsigned) (totrcd % nrcds) : nrcds;
        if (nrd == 0)
        	nrd = nrcds;
        /* @(:) 2.3.00.112 */
        tr = (long) ((k * nrcds + (nrd - (rr + k)->rdsk)));
        ad = tr * sp->rc_len;
        fseek(fp1, ad, 0);
        /* @(!) 2.3.00.112  modificata fread(rtn + sp->rc_len, l * sp->rc_len, 1, fp1); */
        fread(rtn + sp->rc_len, sp->rc_len, l, fp1);
        /* @(:) 2.3.00.112 */
        (rr + k)->rbuf = l;
        (rr + k)->rdsk -= l;
      }
      else
        memset((rr + k)->rc, 127, sp->rc_len);
    }
    return rtn;
  }

  /*
     @(#) sort_stats  SORT

     @(ID)
     Visualizza la statistica sul sort.
     @(FD)
     */

  void sort_stats()
  {
    char msg[256];
    sprintf(msg,
      "Record length = %d\n"
      "%lu records sorted\n"
      "%u sequences\n"
      "%u bytes of sort buffer\n"
      "%u records per buffer",
      sp->rc_len, totrcd, no_seq, bspace, nrcds
    );
    xvt_dm_post_note(msg);
  }

  /*
     @($) appr_mem  SORT

     @(ID)
     Alloca memoria per un buffer.
     @(FD)

     @(ISV)
     buff = buffer di memoria allocato.
     @(FSV)

     @(IN)
     Se puo' alloca MOSTMEM altrimenti ne alloca quanto puo', 
     non scendendo mai sotto il minimo LEASTMEM
     @(FN)
     */

static char *appr_mem(unsigned* h)
{
  char *buff = NULL;

  *h = (unsigned) MOSTMEM + 1024;
  while (buff == NULL && *h > LEASTMEM)   
  {
    *h -= 1024;
    buff = malloc(*h);
  }
  return buff;
}

/* 
   @($) sortcomp  SORT

   @(ID)
   Esegue i confronti per il sort ed il merge.
   @(FD)

   @(ISV)
   i = contatore.

   k = risultato della comparazione.
   @(FSV)

   @(IN)
   Ritorna un intero < 0  se  a < b
   0   se  a = b
   > 0  se  a > b
   @(FN)
   */


// static int sortcomp(a,b)
//   char **a; char **b;  /* puntatori ai puntatori ai record da confrontare */

static int sortcomp(const void* pa, const void* pb)
{
  int i, k;
  
  const char** a = (const char**)pa;
  const char** b = (const char**)pb;

  if (**a == 127 || **b == 127)
    return (int) **a - (int) **b;
  for (i = 0; i < NOFLDS; i++)
  {
    if (sp->s_fld[i].f_pos == 0) break;
    if (sp->s_fld[i].f_len >= 0)                 /* string compare */
    {
      if  ( ( k=strncmp ( (*a)+sp->s_fld[i].f_pos-1, 
                         (*b)+sp->s_fld[i].f_pos-1, 
                         sp->s_fld[i].f_len
                         ) ) != 0) 
        return (sp->s_fld[i].ad == 'd')?-k:k;
    } 

    if (sp->s_fld[i].f_len == -1)          /* integer compare */
    {
      if ( (*( (int*) (*a)+sp->s_fld[i].f_pos-1 )) !=
          (*( (int*) (*b)+sp->s_fld[i].f_pos-1 ))     )
      {
        k=(*( (int*) (*a)+sp->s_fld[i].f_pos-1 )) - (*( (int*) (*b)+sp->s_fld[i].f_pos-1 ));
        return (sp->s_fld[i].ad == 'd')?-k:k;
      }
    }

    if (sp->s_fld[i].f_len == -2)          /* BOOLEAN compare */
    {
      if ( (*( (BOOLEAN*) (*a)+sp->s_fld[i].f_pos-1 )) !=
          (*( (BOOLEAN*) (*b)+sp->s_fld[i].f_pos-1 ))     )  
      {
        k=(int) ( (*( (BOOLEAN*) (*a)+sp->s_fld[i].f_pos-1 )) - (*( (BOOLEAN*) (*b)+sp->s_fld[i].f_pos-1 )) );
        return (sp->s_fld[i].ad == 'd')?-k:k;
      }
    }
  }
  return 0;
}

/*---------------------------- SECONDA PARTE --------------------------------*/

/*
   @(#) initsortfield  SORT

   @(ID)
   Definizione di sort per un record ISAM.
   @(FD)
   */

void initsortfield ()
{
  nsortfield = datisort.s_fld[0].f_pos = 0;   /* sort su nessun campo */
}


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